1// Copyright 2016-2020 The Libsacloud Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package api 16 17import ( 18 "encoding/json" 19 "fmt" 20 "time" 21 22 "github.com/sacloud/libsacloud/sacloud" 23) 24 25//HACK: さくらのAPI側仕様: Applianceの内容によってJSONフォーマットが異なるため 26// ロードバランサ/VPCルータそれぞれでリクエスト/レスポンスデータ型を定義する。 27 28// SearchDatabaseResponse データベース検索レスポンス 29type SearchDatabaseResponse struct { 30 // Total 総件数 31 Total int `json:",omitempty"` 32 // From ページング開始位置 33 From int `json:",omitempty"` 34 // Count 件数 35 Count int `json:",omitempty"` 36 // Databases データベースリスト 37 Databases []sacloud.Database `json:"Appliances,omitempty"` 38} 39 40type databaseRequest struct { 41 Database *sacloud.Database `json:"Appliance,omitempty"` 42 From int `json:",omitempty"` 43 Count int `json:",omitempty"` 44 Sort []string `json:",omitempty"` 45 Filter map[string]interface{} `json:",omitempty"` 46 Exclude []string `json:",omitempty"` 47 Include []string `json:",omitempty"` 48} 49 50type databaseResponse struct { 51 *sacloud.ResultFlagValue 52 *sacloud.Database `json:"Appliance,omitempty"` 53 // Success 54 Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{}で受ける 55} 56 57type databaseStatusResponse struct { 58 *sacloud.ResultFlagValue 59 Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} 60 Appliance *struct { 61 SettingsResponse *sacloud.DatabaseStatus 62 } 63} 64 65type databaseBackupResponse struct { 66 Log string `json:",omitempty"` 67 IsOk bool `json:"is_ok,omitempty"` 68} 69 70// DatabaseAPI データベースAPI 71type DatabaseAPI struct { 72 *baseAPI 73} 74 75// NewDatabaseAPI データベースAPI作成 76func NewDatabaseAPI(client *Client) *DatabaseAPI { 77 return &DatabaseAPI{ 78 &baseAPI{ 79 client: client, 80 FuncGetResourceURL: func() string { 81 return "appliance" 82 }, 83 FuncBaseSearchCondition: func() *sacloud.Request { 84 res := &sacloud.Request{} 85 res.AddFilter("Class", "database") 86 return res 87 }, 88 }, 89 } 90} 91 92// Find 検索 93func (api *DatabaseAPI) Find() (*SearchDatabaseResponse, error) { 94 data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) 95 if err != nil { 96 return nil, err 97 } 98 var res SearchDatabaseResponse 99 if err := json.Unmarshal(data, &res); err != nil { 100 return nil, err 101 } 102 return &res, nil 103} 104 105func (api *DatabaseAPI) request(f func(*databaseResponse) error) (*sacloud.Database, error) { 106 res := &databaseResponse{} 107 err := f(res) 108 if err != nil { 109 return nil, err 110 } 111 return res.Database, nil 112} 113 114func (api *DatabaseAPI) createRequest(value *sacloud.Database) *databaseResponse { 115 return &databaseResponse{Database: value} 116} 117 118// New 新規作成用パラメーター作成 119func (api *DatabaseAPI) New(values *sacloud.CreateDatabaseValue) *sacloud.Database { 120 return sacloud.CreateNewDatabase(values) 121} 122 123// Create 新規作成 124func (api *DatabaseAPI) Create(value *sacloud.Database) (*sacloud.Database, error) { 125 return api.request(func(res *databaseResponse) error { 126 return api.create(api.createRequest(value), res) 127 }) 128} 129 130// Read 読み取り 131func (api *DatabaseAPI) Read(id sacloud.ID) (*sacloud.Database, error) { 132 return api.request(func(res *databaseResponse) error { 133 return api.read(id, nil, res) 134 }) 135} 136 137// Status DBの設定/起動状態の取得 138func (api *DatabaseAPI) Status(id sacloud.ID) (*sacloud.DatabaseStatus, error) { 139 var ( 140 method = "GET" 141 uri = fmt.Sprintf("%s/%d/status", api.getResourceURL(), id) 142 ) 143 144 res := &databaseStatusResponse{} 145 err := api.baseAPI.request(method, uri, nil, res) 146 if err != nil { 147 return nil, err 148 } 149 return res.Appliance.SettingsResponse, nil 150} 151 152// Backup バックアップ取得 153func (api *DatabaseAPI) Backup(id sacloud.ID) (string, error) { 154 var ( 155 method = "POST" 156 uri = fmt.Sprintf("%s/%d/action/history", api.getResourceURL(), id) 157 ) 158 159 body := map[string]interface{}{ 160 "Appliance": map[string]interface{}{ 161 "Settings": map[string]interface{}{ 162 "DBConf": map[string]interface{}{ 163 "backup": map[string]string{ 164 "availability": "discontinued", 165 }, 166 }, 167 }, 168 }, 169 } 170 171 res := &databaseBackupResponse{} 172 err := api.baseAPI.request(method, uri, body, res) 173 if err != nil { 174 return "", err 175 } 176 return res.Log, nil 177} 178 179// DownloadLog ログ取得 180func (api *DatabaseAPI) DownloadLog(id sacloud.ID, logID string) (string, error) { 181 var ( 182 method = "GET" 183 uri = fmt.Sprintf("%s/%d/download/log/%s", api.getResourceURL(), id, logID) 184 ) 185 186 res := &databaseBackupResponse{} 187 err := api.baseAPI.request(method, uri, nil, res) 188 if err != nil { 189 return "", err 190 } 191 return res.Log, nil 192} 193 194// Restore バックアップからの復元 195func (api *DatabaseAPI) Restore(id sacloud.ID, backupID string) (string, error) { 196 var ( 197 method = "POST" 198 uri = fmt.Sprintf("%s/%d/action/history/%s", api.getResourceURL(), id, backupID) 199 ) 200 201 body := map[string]interface{}{ 202 "Appliance": map[string]interface{}{}, 203 } 204 205 res := &databaseBackupResponse{} 206 err := api.baseAPI.request(method, uri, body, res) 207 if err != nil { 208 return "", err 209 } 210 return res.Log, nil 211} 212 213// DeleteBackup バックアップの削除 214func (api *DatabaseAPI) DeleteBackup(id sacloud.ID, backupID string) (string, error) { 215 var ( 216 method = "DELETE" 217 uri = fmt.Sprintf("%s/%d/action/history/%s", api.getResourceURL(), id, backupID) 218 ) 219 220 body := map[string]interface{}{ 221 "Appliance": map[string]interface{}{}, 222 } 223 224 res := &databaseBackupResponse{} 225 err := api.baseAPI.request(method, uri, body, res) 226 if err != nil { 227 return "", err 228 } 229 return res.Log, nil 230} 231 232// HistoryLock バックアップ削除ロック 233func (api *DatabaseAPI) HistoryLock(id sacloud.ID, backupID string) (string, error) { 234 var ( 235 method = "PUT" 236 uri = fmt.Sprintf("%s/%d/action/history-lock/%s", api.getResourceURL(), id, backupID) 237 ) 238 239 body := map[string]interface{}{ 240 "Appliance": map[string]interface{}{}, 241 } 242 243 res := &databaseBackupResponse{} 244 err := api.baseAPI.request(method, uri, body, res) 245 if err != nil { 246 return "", err 247 } 248 return res.Log, nil 249} 250 251// HistoryUnlock バックアップ削除アンロック 252func (api *DatabaseAPI) HistoryUnlock(id sacloud.ID, backupID string) (string, error) { 253 var ( 254 method = "DELETE" 255 uri = fmt.Sprintf("%s/%d/action/history-lock/%s", api.getResourceURL(), id, backupID) 256 ) 257 258 body := map[string]interface{}{ 259 "Appliance": map[string]interface{}{}, 260 } 261 262 res := &databaseBackupResponse{} 263 err := api.baseAPI.request(method, uri, body, res) 264 if err != nil { 265 return "", err 266 } 267 return res.Log, nil 268} 269 270// Update 更新 271func (api *DatabaseAPI) Update(id sacloud.ID, value *sacloud.Database) (*sacloud.Database, error) { 272 return api.request(func(res *databaseResponse) error { 273 return api.update(id, api.createRequest(value), res) 274 }) 275} 276 277// UpdateSetting 設定更新 278func (api *DatabaseAPI) UpdateSetting(id sacloud.ID, value *sacloud.Database) (*sacloud.Database, error) { 279 req := &sacloud.Database{ 280 // Settings 281 Settings: value.Settings, 282 } 283 return api.request(func(res *databaseResponse) error { 284 return api.update(id, api.createRequest(req), res) 285 }) 286} 287 288// Delete 削除 289func (api *DatabaseAPI) Delete(id sacloud.ID) (*sacloud.Database, error) { 290 return api.request(func(res *databaseResponse) error { 291 return api.delete(id, nil, res) 292 }) 293} 294 295// Config 設定変更の反映 296func (api *DatabaseAPI) Config(id sacloud.ID) (bool, error) { 297 var ( 298 method = "PUT" 299 uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id) 300 ) 301 return api.modify(method, uri, nil) 302} 303 304// IsUp 起動しているか判定 305func (api *DatabaseAPI) IsUp(id sacloud.ID) (bool, error) { 306 lb, err := api.Read(id) 307 if err != nil { 308 return false, err 309 } 310 return lb.Instance.IsUp(), nil 311} 312 313// IsDown ダウンしているか判定 314func (api *DatabaseAPI) IsDown(id sacloud.ID) (bool, error) { 315 lb, err := api.Read(id) 316 if err != nil { 317 return false, err 318 } 319 return lb.Instance.IsDown(), nil 320} 321 322// IsDatabaseRunning データベースプロセスが起動しているか判定 323func (api *DatabaseAPI) IsDatabaseRunning(id sacloud.ID) (bool, error) { 324 db, err := api.Status(id) 325 if err != nil { 326 return false, err 327 } 328 return db.IsUp(), nil 329 330} 331 332// Boot 起動 333func (api *DatabaseAPI) Boot(id sacloud.ID) (bool, error) { 334 var ( 335 method = "PUT" 336 uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) 337 ) 338 return api.modify(method, uri, nil) 339} 340 341// Shutdown シャットダウン(graceful) 342func (api *DatabaseAPI) Shutdown(id sacloud.ID) (bool, error) { 343 var ( 344 method = "DELETE" 345 uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) 346 ) 347 348 return api.modify(method, uri, nil) 349} 350 351// Stop シャットダウン(force) 352func (api *DatabaseAPI) Stop(id sacloud.ID) (bool, error) { 353 var ( 354 method = "DELETE" 355 uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) 356 ) 357 358 return api.modify(method, uri, map[string]bool{"Force": true}) 359} 360 361// RebootForce 再起動 362func (api *DatabaseAPI) RebootForce(id sacloud.ID) (bool, error) { 363 var ( 364 method = "PUT" 365 uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) 366 ) 367 368 return api.modify(method, uri, nil) 369} 370 371// ResetForce リセット 372func (api *DatabaseAPI) ResetForce(id sacloud.ID, recycleProcess bool) (bool, error) { 373 var ( 374 method = "PUT" 375 uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) 376 ) 377 378 return api.modify(method, uri, map[string]bool{"RecycleProcess": recycleProcess}) 379} 380 381// SleepUntilUp 起動するまで待機 382func (api *DatabaseAPI) SleepUntilUp(id sacloud.ID, timeout time.Duration) error { 383 handler := waitingForUpFunc(func() (hasUpDown, error) { 384 return api.Read(id) 385 }, 0) 386 return blockingPoll(handler, timeout) 387} 388 389// SleepUntilDatabaseRunning 起動するまで待機 390func (api *DatabaseAPI) SleepUntilDatabaseRunning(id sacloud.ID, timeout time.Duration, maxRetry int) error { 391 handler := waitingForUpFunc(func() (hasUpDown, error) { 392 return api.Read(id) 393 }, maxRetry) 394 return blockingPoll(handler, timeout) 395} 396 397// SleepUntilDown ダウンするまで待機 398func (api *DatabaseAPI) SleepUntilDown(id sacloud.ID, timeout time.Duration) error { 399 handler := waitingForDownFunc(func() (hasUpDown, error) { 400 return api.Read(id) 401 }, 0) 402 return blockingPoll(handler, timeout) 403} 404 405// SleepWhileCopying コピー終了まで待機 406func (api *DatabaseAPI) SleepWhileCopying(id sacloud.ID, timeout time.Duration, maxRetry int) error { 407 handler := waitingForAvailableFunc(func() (hasAvailable, error) { 408 return api.Read(id) 409 }, maxRetry) 410 return blockingPoll(handler, timeout) 411} 412 413// AsyncSleepWhileCopying コピー終了まで待機(非同期) 414func (api *DatabaseAPI) AsyncSleepWhileCopying(id sacloud.ID, timeout time.Duration, maxRetry int) (chan (interface{}), chan (interface{}), chan (error)) { 415 handler := waitingForAvailableFunc(func() (hasAvailable, error) { 416 return api.Read(id) 417 }, maxRetry) 418 return poll(handler, timeout) 419} 420 421// MonitorCPU CPUアクティビティーモニター取得 422func (api *DatabaseAPI) MonitorCPU(id sacloud.ID, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { 423 return api.baseAPI.applianceMonitorBy(id, "cpu", 0, body) 424} 425 426// MonitorDatabase データーベース固有項目アクティビティモニター取得 427func (api *DatabaseAPI) MonitorDatabase(id sacloud.ID, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { 428 return api.baseAPI.applianceMonitorBy(id, "database", 0, body) 429} 430 431// MonitorInterface NICアクティビティーモニター取得 432func (api *DatabaseAPI) MonitorInterface(id sacloud.ID, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { 433 return api.baseAPI.applianceMonitorBy(id, "interface", 0, body) 434} 435 436// MonitorSystemDisk システムディスクアクティビティーモニター取得 437func (api *DatabaseAPI) MonitorSystemDisk(id sacloud.ID, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { 438 return api.baseAPI.applianceMonitorBy(id, "disk", 1, body) 439} 440 441// MonitorBackupDisk バックアップディスクアクティビティーモニター取得 442func (api *DatabaseAPI) MonitorBackupDisk(id sacloud.ID, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { 443 return api.baseAPI.applianceMonitorBy(id, "disk", 2, body) 444} 445