2018-06-21 19:24:45 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
2018-12-06 21:50:17 +00:00
|
|
|
|
|
|
|
"github.com/sacloud/libsacloud/sacloud"
|
2018-06-21 19:24:45 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
//HACK: さくらのAPI側仕様: Applianceの内容によってJSONフォーマットが異なるため
|
|
|
|
// ロードバランサ/VPCルータそれぞれでリクエスト/レスポンスデータ型を定義する。
|
|
|
|
|
|
|
|
// SearchDatabaseResponse データベース検索レスポンス
|
|
|
|
type SearchDatabaseResponse struct {
|
|
|
|
// Total 総件数
|
|
|
|
Total int `json:",omitempty"`
|
|
|
|
// From ページング開始位置
|
|
|
|
From int `json:",omitempty"`
|
|
|
|
// Count 件数
|
|
|
|
Count int `json:",omitempty"`
|
|
|
|
// Databases データベースリスト
|
|
|
|
Databases []sacloud.Database `json:"Appliances,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type databaseRequest struct {
|
|
|
|
Database *sacloud.Database `json:"Appliance,omitempty"`
|
|
|
|
From int `json:",omitempty"`
|
|
|
|
Count int `json:",omitempty"`
|
|
|
|
Sort []string `json:",omitempty"`
|
|
|
|
Filter map[string]interface{} `json:",omitempty"`
|
|
|
|
Exclude []string `json:",omitempty"`
|
|
|
|
Include []string `json:",omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type databaseResponse struct {
|
|
|
|
*sacloud.ResultFlagValue
|
|
|
|
*sacloud.Database `json:"Appliance,omitempty"`
|
|
|
|
// Success
|
|
|
|
Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{}で受ける
|
|
|
|
}
|
|
|
|
|
|
|
|
type databaseStatusResponse struct {
|
|
|
|
*sacloud.ResultFlagValue
|
|
|
|
Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{}
|
|
|
|
Appliance *struct {
|
|
|
|
SettingsResponse *sacloud.DatabaseStatus
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type databaseBackupResponse struct {
|
|
|
|
Log string `json:",omitempty"`
|
|
|
|
IsOk bool `json:"is_ok,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// DatabaseAPI データベースAPI
|
|
|
|
type DatabaseAPI struct {
|
|
|
|
*baseAPI
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDatabaseAPI データベースAPI作成
|
|
|
|
func NewDatabaseAPI(client *Client) *DatabaseAPI {
|
|
|
|
return &DatabaseAPI{
|
|
|
|
&baseAPI{
|
|
|
|
client: client,
|
|
|
|
FuncGetResourceURL: func() string {
|
|
|
|
return "appliance"
|
|
|
|
},
|
|
|
|
FuncBaseSearchCondition: func() *sacloud.Request {
|
|
|
|
res := &sacloud.Request{}
|
|
|
|
res.AddFilter("Class", "database")
|
|
|
|
return res
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find 検索
|
|
|
|
func (api *DatabaseAPI) Find() (*SearchDatabaseResponse, error) {
|
|
|
|
data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var res SearchDatabaseResponse
|
|
|
|
if err := json.Unmarshal(data, &res); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *DatabaseAPI) request(f func(*databaseResponse) error) (*sacloud.Database, error) {
|
|
|
|
res := &databaseResponse{}
|
|
|
|
err := f(res)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return res.Database, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *DatabaseAPI) createRequest(value *sacloud.Database) *databaseResponse {
|
|
|
|
return &databaseResponse{Database: value}
|
|
|
|
}
|
|
|
|
|
|
|
|
// New 新規作成用パラメーター作成
|
|
|
|
func (api *DatabaseAPI) New(values *sacloud.CreateDatabaseValue) *sacloud.Database {
|
|
|
|
return sacloud.CreateNewDatabase(values)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create 新規作成
|
|
|
|
func (api *DatabaseAPI) Create(value *sacloud.Database) (*sacloud.Database, error) {
|
|
|
|
return api.request(func(res *databaseResponse) error {
|
|
|
|
return api.create(api.createRequest(value), res)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 読み取り
|
|
|
|
func (api *DatabaseAPI) Read(id int64) (*sacloud.Database, error) {
|
|
|
|
return api.request(func(res *databaseResponse) error {
|
|
|
|
return api.read(id, nil, res)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Status DBの設定/起動状態の取得
|
|
|
|
func (api *DatabaseAPI) Status(id int64) (*sacloud.DatabaseStatus, error) {
|
|
|
|
var (
|
|
|
|
method = "GET"
|
|
|
|
uri = fmt.Sprintf("%s/%d/status", api.getResourceURL(), id)
|
|
|
|
)
|
|
|
|
|
|
|
|
res := &databaseStatusResponse{}
|
|
|
|
err := api.baseAPI.request(method, uri, nil, res)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return res.Appliance.SettingsResponse, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Backup バックアップ取得
|
|
|
|
func (api *DatabaseAPI) Backup(id int64) (string, error) {
|
|
|
|
var (
|
|
|
|
method = "POST"
|
|
|
|
uri = fmt.Sprintf("%s/%d/action/history", api.getResourceURL(), id)
|
|
|
|
)
|
|
|
|
|
|
|
|
body := map[string]interface{}{
|
|
|
|
"Appliance": map[string]interface{}{
|
|
|
|
"Settings": map[string]interface{}{
|
|
|
|
"DBConf": map[string]interface{}{
|
|
|
|
"backup": map[string]string{
|
|
|
|
"availability": "discontinued",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
res := &databaseBackupResponse{}
|
|
|
|
err := api.baseAPI.request(method, uri, body, res)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return res.Log, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DownloadLog ログ取得
|
|
|
|
func (api *DatabaseAPI) DownloadLog(id int64, logID string) (string, error) {
|
|
|
|
var (
|
|
|
|
method = "GET"
|
|
|
|
uri = fmt.Sprintf("%s/%d/download/log/%s", api.getResourceURL(), id, logID)
|
|
|
|
)
|
|
|
|
|
|
|
|
res := &databaseBackupResponse{}
|
|
|
|
err := api.baseAPI.request(method, uri, nil, res)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return res.Log, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore バックアップからの復元
|
|
|
|
func (api *DatabaseAPI) Restore(id int64, backupID string) (string, error) {
|
|
|
|
var (
|
|
|
|
method = "POST"
|
|
|
|
uri = fmt.Sprintf("%s/%d/action/history/%s", api.getResourceURL(), id, backupID)
|
|
|
|
)
|
|
|
|
|
|
|
|
body := map[string]interface{}{
|
|
|
|
"Appliance": map[string]interface{}{},
|
|
|
|
}
|
|
|
|
|
|
|
|
res := &databaseBackupResponse{}
|
|
|
|
err := api.baseAPI.request(method, uri, body, res)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return res.Log, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteBackup バックアップの削除
|
|
|
|
func (api *DatabaseAPI) DeleteBackup(id int64, backupID string) (string, error) {
|
|
|
|
var (
|
|
|
|
method = "DELETE"
|
|
|
|
uri = fmt.Sprintf("%s/%d/action/history/%s", api.getResourceURL(), id, backupID)
|
|
|
|
)
|
|
|
|
|
|
|
|
body := map[string]interface{}{
|
|
|
|
"Appliance": map[string]interface{}{},
|
|
|
|
}
|
|
|
|
|
|
|
|
res := &databaseBackupResponse{}
|
|
|
|
err := api.baseAPI.request(method, uri, body, res)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return res.Log, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HistoryLock バックアップ削除ロック
|
|
|
|
func (api *DatabaseAPI) HistoryLock(id int64, backupID string) (string, error) {
|
|
|
|
var (
|
|
|
|
method = "PUT"
|
|
|
|
uri = fmt.Sprintf("%s/%d/action/history-lock/%s", api.getResourceURL(), id, backupID)
|
|
|
|
)
|
|
|
|
|
|
|
|
body := map[string]interface{}{
|
|
|
|
"Appliance": map[string]interface{}{},
|
|
|
|
}
|
|
|
|
|
|
|
|
res := &databaseBackupResponse{}
|
|
|
|
err := api.baseAPI.request(method, uri, body, res)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return res.Log, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HistoryUnlock バックアップ削除アンロック
|
|
|
|
func (api *DatabaseAPI) HistoryUnlock(id int64, backupID string) (string, error) {
|
|
|
|
var (
|
|
|
|
method = "DELETE"
|
|
|
|
uri = fmt.Sprintf("%s/%d/action/history-lock/%s", api.getResourceURL(), id, backupID)
|
|
|
|
)
|
|
|
|
|
|
|
|
body := map[string]interface{}{
|
|
|
|
"Appliance": map[string]interface{}{},
|
|
|
|
}
|
|
|
|
|
|
|
|
res := &databaseBackupResponse{}
|
|
|
|
err := api.baseAPI.request(method, uri, body, res)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return res.Log, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update 更新
|
|
|
|
func (api *DatabaseAPI) Update(id int64, value *sacloud.Database) (*sacloud.Database, error) {
|
|
|
|
return api.request(func(res *databaseResponse) error {
|
|
|
|
return api.update(id, api.createRequest(value), res)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateSetting 設定更新
|
|
|
|
func (api *DatabaseAPI) UpdateSetting(id int64, value *sacloud.Database) (*sacloud.Database, error) {
|
|
|
|
req := &sacloud.Database{
|
|
|
|
// Settings
|
|
|
|
Settings: value.Settings,
|
|
|
|
}
|
|
|
|
return api.request(func(res *databaseResponse) error {
|
|
|
|
return api.update(id, api.createRequest(req), res)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete 削除
|
|
|
|
func (api *DatabaseAPI) Delete(id int64) (*sacloud.Database, error) {
|
|
|
|
return api.request(func(res *databaseResponse) error {
|
|
|
|
return api.delete(id, nil, res)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Config 設定変更の反映
|
|
|
|
func (api *DatabaseAPI) Config(id int64) (bool, error) {
|
|
|
|
var (
|
|
|
|
method = "PUT"
|
|
|
|
uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id)
|
|
|
|
)
|
|
|
|
return api.modify(method, uri, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsUp 起動しているか判定
|
|
|
|
func (api *DatabaseAPI) IsUp(id int64) (bool, error) {
|
|
|
|
lb, err := api.Read(id)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return lb.Instance.IsUp(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsDown ダウンしているか判定
|
|
|
|
func (api *DatabaseAPI) IsDown(id int64) (bool, error) {
|
|
|
|
lb, err := api.Read(id)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return lb.Instance.IsDown(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsDatabaseRunning データベースプロセスが起動しているか判定
|
|
|
|
func (api *DatabaseAPI) IsDatabaseRunning(id int64) (bool, error) {
|
|
|
|
db, err := api.Status(id)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return db.IsUp(), nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Boot 起動
|
|
|
|
func (api *DatabaseAPI) Boot(id int64) (bool, error) {
|
|
|
|
var (
|
|
|
|
method = "PUT"
|
|
|
|
uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id)
|
|
|
|
)
|
|
|
|
return api.modify(method, uri, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown シャットダウン(graceful)
|
|
|
|
func (api *DatabaseAPI) Shutdown(id int64) (bool, error) {
|
|
|
|
var (
|
|
|
|
method = "DELETE"
|
|
|
|
uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id)
|
|
|
|
)
|
|
|
|
|
|
|
|
return api.modify(method, uri, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop シャットダウン(force)
|
|
|
|
func (api *DatabaseAPI) Stop(id int64) (bool, error) {
|
|
|
|
var (
|
|
|
|
method = "DELETE"
|
|
|
|
uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id)
|
|
|
|
)
|
|
|
|
|
|
|
|
return api.modify(method, uri, map[string]bool{"Force": true})
|
|
|
|
}
|
|
|
|
|
|
|
|
// RebootForce 再起動
|
|
|
|
func (api *DatabaseAPI) RebootForce(id int64) (bool, error) {
|
|
|
|
var (
|
|
|
|
method = "PUT"
|
|
|
|
uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id)
|
|
|
|
)
|
|
|
|
|
|
|
|
return api.modify(method, uri, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResetForce リセット
|
|
|
|
func (api *DatabaseAPI) ResetForce(id int64, recycleProcess bool) (bool, error) {
|
|
|
|
var (
|
|
|
|
method = "PUT"
|
|
|
|
uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id)
|
|
|
|
)
|
|
|
|
|
|
|
|
return api.modify(method, uri, map[string]bool{"RecycleProcess": recycleProcess})
|
|
|
|
}
|
|
|
|
|
|
|
|
// SleepUntilUp 起動するまで待機
|
|
|
|
func (api *DatabaseAPI) SleepUntilUp(id int64, timeout time.Duration) error {
|
|
|
|
handler := waitingForUpFunc(func() (hasUpDown, error) {
|
|
|
|
return api.Read(id)
|
|
|
|
}, 0)
|
|
|
|
return blockingPoll(handler, timeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SleepUntilDatabaseRunning 起動するまで待機
|
|
|
|
func (api *DatabaseAPI) SleepUntilDatabaseRunning(id int64, timeout time.Duration, maxRetry int) error {
|
|
|
|
handler := waitingForUpFunc(func() (hasUpDown, error) {
|
|
|
|
return api.Read(id)
|
|
|
|
}, maxRetry)
|
|
|
|
return blockingPoll(handler, timeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SleepUntilDown ダウンするまで待機
|
|
|
|
func (api *DatabaseAPI) SleepUntilDown(id int64, timeout time.Duration) error {
|
|
|
|
handler := waitingForDownFunc(func() (hasUpDown, error) {
|
|
|
|
return api.Read(id)
|
|
|
|
}, 0)
|
|
|
|
return blockingPoll(handler, timeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SleepWhileCopying コピー終了まで待機
|
|
|
|
func (api *DatabaseAPI) SleepWhileCopying(id int64, timeout time.Duration, maxRetry int) error {
|
|
|
|
handler := waitingForAvailableFunc(func() (hasAvailable, error) {
|
|
|
|
return api.Read(id)
|
|
|
|
}, maxRetry)
|
|
|
|
return blockingPoll(handler, timeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AsyncSleepWhileCopying コピー終了まで待機(非同期)
|
|
|
|
func (api *DatabaseAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration, maxRetry int) (chan (interface{}), chan (interface{}), chan (error)) {
|
|
|
|
handler := waitingForAvailableFunc(func() (hasAvailable, error) {
|
|
|
|
return api.Read(id)
|
|
|
|
}, maxRetry)
|
|
|
|
return poll(handler, timeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MonitorCPU CPUアクティビティーモニター取得
|
|
|
|
func (api *DatabaseAPI) MonitorCPU(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) {
|
|
|
|
return api.baseAPI.applianceMonitorBy(id, "cpu", 0, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MonitorDatabase データーベース固有項目アクティビティモニター取得
|
|
|
|
func (api *DatabaseAPI) MonitorDatabase(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) {
|
|
|
|
return api.baseAPI.applianceMonitorBy(id, "database", 0, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MonitorInterface NICアクティビティーモニター取得
|
|
|
|
func (api *DatabaseAPI) MonitorInterface(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) {
|
|
|
|
return api.baseAPI.applianceMonitorBy(id, "interface", 0, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MonitorSystemDisk システムディスクアクティビティーモニター取得
|
|
|
|
func (api *DatabaseAPI) MonitorSystemDisk(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) {
|
|
|
|
return api.baseAPI.applianceMonitorBy(id, "disk", 1, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MonitorBackupDisk バックアップディスクアクティビティーモニター取得
|
|
|
|
func (api *DatabaseAPI) MonitorBackupDisk(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) {
|
|
|
|
return api.baseAPI.applianceMonitorBy(id, "disk", 2, body)
|
|
|
|
}
|