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 sacloud 16 17import ( 18 "encoding/json" 19 "fmt" 20 "strings" 21) 22 23// AllowDatabaseBackupWeekdays データベースバックアップ実行曜日リスト 24func AllowDatabaseBackupWeekdays() []string { 25 return []string{"mon", "tue", "wed", "thu", "fri", "sat", "sun"} 26} 27 28// Database データベース(appliance) 29type Database struct { 30 *Appliance // アプライアンス共通属性 31 32 Remark *DatabaseRemark `json:",omitempty"` // リマーク 33 Settings *DatabaseSettings `json:",omitempty"` // データベース設定 34} 35 36// DatabaseRemark データベースリマーク 37type DatabaseRemark struct { 38 *ApplianceRemarkBase 39 propPlanID // プランID 40 DBConf *DatabaseCommonRemarks // コンフィグ 41 Network *DatabaseRemarkNetwork // ネットワーク 42 SourceAppliance *DatabaseSourceAppliance `json:",omitempty"` // クローン元DB 43 Zone struct { // ゾーン 44 ID ID `json:",omitempty"` // ゾーンID 45 } 46} 47 48// DatabaseSourceAppliance ソースアプライアンス(クローン元DB) 49type DatabaseSourceAppliance struct { 50 ID ID `json:",omitempty"` 51} 52 53// UnmarshalJSON 配列/オブジェクトが混在することへの対応 54// 55// v2からのバックポート 56func (s *DatabaseSourceAppliance) UnmarshalJSON(b []byte) error { 57 if string(b) == "[]" { 58 return nil 59 } 60 type alias DatabaseSourceAppliance 61 62 var a alias 63 if err := json.Unmarshal(b, &a); err != nil { 64 return err 65 } 66 *s = DatabaseSourceAppliance(a) 67 return nil 68} 69 70// DatabaseRemarkNetwork ネットワーク 71type DatabaseRemarkNetwork struct { 72 NetworkMaskLen int `json:",omitempty"` // ネットワークマスク長 73 DefaultRoute string `json:",omitempty"` // デフォルトルート 74} 75 76// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) 77func (s *DatabaseRemarkNetwork) UnmarshalJSON(data []byte) error { 78 targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1) 79 if targetData == `[]` { 80 return nil 81 } 82 83 tmp := &struct { 84 // NetworkMaskLen 85 NetworkMaskLen int `json:",omitempty"` 86 // DefaultRoute 87 DefaultRoute string `json:",omitempty"` 88 }{} 89 if err := json.Unmarshal(data, &tmp); err != nil { 90 return err 91 } 92 93 s.NetworkMaskLen = tmp.NetworkMaskLen 94 s.DefaultRoute = tmp.DefaultRoute 95 return nil 96} 97 98// DatabaseCommonRemarks リマークリスト 99type DatabaseCommonRemarks struct { 100 Common *DatabaseCommonRemark // Common 101} 102 103// DatabaseCommonRemark リマーク 104type DatabaseCommonRemark struct { 105 DatabaseName string `json:",omitempty"` // 名称 106 DatabaseRevision string `json:",omitempty"` // リビジョン 107 DatabaseTitle string `json:",omitempty"` // タイトル 108 DatabaseVersion string `json:",omitempty"` // バージョン 109} 110 111// DatabaseSettings データベース設定リスト 112type DatabaseSettings struct { 113 DBConf *DatabaseSetting `json:",omitempty"` // コンフィグ 114} 115 116// DatabaseSetting データベース設定 117type DatabaseSetting struct { 118 Backup *DatabaseBackupSetting `json:",omitempty"` // バックアップ設定 119 Common *DatabaseCommonSetting `json:",oitempty"` // 共通設定 120 Replication *DatabaseReplicationSetting `json:",omitempty"` // レプリケーション設定 121} 122 123// DatabaseServer データベースサーバー情報 124type DatabaseServer struct { 125 IPAddress string `json:",omitempty"` // IPアドレス 126 Port string `json:",omitempty"` // ポート 127 Enabled string `json:",omitempty"` // 有効/無効 128 Status string `json:",omitempty"` // ステータス 129 ActiveConn string `json:",omitempty"` // アクティブコネクション 130} 131 132// DatabasePlan プラン 133type DatabasePlan int 134 135var ( 136 // DatabasePlanMini ミニプラン(後方互換用) 137 DatabasePlanMini = DatabasePlan(10) 138 // DatabasePlan10G 10Gプラン 139 DatabasePlan10G = DatabasePlan(10) 140 // DatabasePlan30G 30Gプラン 141 DatabasePlan30G = DatabasePlan(30) 142 // DatabasePlan90G 90Gプラン 143 DatabasePlan90G = DatabasePlan(90) 144 // DatabasePlan240G 240Gプラン 145 DatabasePlan240G = DatabasePlan(240) 146 // DatabasePlan500G 500Gプラン 147 DatabasePlan500G = DatabasePlan(500) 148 // DatabasePlan1T 1Tプラン 149 DatabasePlan1T = DatabasePlan(1000) 150) 151 152// AllowDatabasePlans 指定可能なデータベースプラン 153func AllowDatabasePlans() []int { 154 return []int{ 155 int(DatabasePlan10G), 156 int(DatabasePlan30G), 157 int(DatabasePlan90G), 158 int(DatabasePlan240G), 159 int(DatabasePlan500G), 160 int(DatabasePlan1T), 161 } 162} 163 164// DatabaseBackupSetting バックアップ設定 165type DatabaseBackupSetting struct { 166 Rotate int `json:",omitempty"` // ローテーション世代数 167 Time string `json:",omitempty"` // 開始時刻 168 DayOfWeek []string `json:",omitempty"` // 取得曜日 169} 170 171// DatabaseCommonSetting 共通設定 172type DatabaseCommonSetting struct { 173 DefaultUser string `json:",omitempty"` // ユーザー名 174 UserPassword string `json:",omitempty"` // ユーザーパスワード 175 WebUI interface{} `json:",omitempty"` // WebUIのIPアドレス or FQDN 176 ReplicaPassword string `json:",omitempty"` // レプリケーションパスワード 177 ReplicaUser string `json:",omitempty"` // レプリケーションユーザー 178 ServicePort json.Number `json:",omitempty"` // ポート番号 179 SourceNetwork SourceNetwork // 接続許可ネットワーク 180} 181 182// SourceNetwork 接続許可ネットワーク 183type SourceNetwork []string 184 185// UnmarshalJSON JSONアンマーシャル(配列と文字列が混在するためここで対応) 186func (s *SourceNetwork) UnmarshalJSON(data []byte) error { 187 // SourceNetworkが未設定の場合、APIレスポンスが""となるため回避する 188 if string(data) == `""` { 189 return nil 190 } 191 192 tmp := []string{} 193 if err := json.Unmarshal(data, &tmp); err != nil { 194 return err 195 } 196 source := SourceNetwork(tmp) 197 *s = source 198 return nil 199} 200 201// MarshalJSON JSONマーシャル(配列と文字列が混在するためここで対応) 202func (s *SourceNetwork) MarshalJSON() ([]byte, error) { 203 if s == nil { 204 return []byte(""), nil 205 } 206 207 list := []string(*s) 208 if len(list) == 0 || (len(list) == 1 && list[0] == "") { 209 return []byte(`""`), nil 210 } 211 212 return json.Marshal(list) 213} 214 215// DatabaseReplicationSetting レプリケーション設定 216type DatabaseReplicationSetting struct { 217 // Model レプリケーションモデル 218 Model DatabaseReplicationModels `json:",omitempty"` 219 // Appliance マスター側アプライアンス 220 Appliance *DatabaseSourceAppliance `json:",omitempty"` 221 // IPAddress IPアドレス 222 IPAddress string `json:",omitempty"` 223 // Port ポート 224 Port int `json:",omitempty"` 225 // User ユーザー 226 User string `json:",omitempty"` 227 // Password パスワード 228 Password string `json:",omitempty"` 229} 230 231// DatabaseReplicationModels データベースのレプリケーションモデル 232type DatabaseReplicationModels string 233 234const ( 235 // DatabaseReplicationModelMasterSlave レプリケーションモデル: Master-Slave(マスター側) 236 DatabaseReplicationModelMasterSlave = "Master-Slave" 237 // DatabaseReplicationModelAsyncReplica レプリケーションモデル: Async-Replica(スレーブ側) 238 DatabaseReplicationModelAsyncReplica = "Async-Replica" 239) 240 241// CreateDatabaseValue データベース作成用パラメータ 242type CreateDatabaseValue struct { 243 Plan DatabasePlan // プラン 244 AdminPassword string // 管理者パスワード 245 DefaultUser string // ユーザー名 246 UserPassword string // パスワード 247 SourceNetwork []string // 接続許可ネットワーク 248 ServicePort int // ポート 249 EnableBackup bool // バックアップ有効化 250 BackupRotate int // バックアップ世代数 251 BackupTime string // バックアップ開始時間 252 BackupDayOfWeek []string // バックアップ取得曜日 253 SwitchID ID // 接続先スイッチ 254 IPAddress1 string // IPアドレス1 255 MaskLen int // ネットワークマスク長 256 DefaultRoute string // デフォルトルート 257 Name string // 名称 258 Description string // 説明 259 Tags []string // タグ 260 Icon *Resource // アイコン 261 WebUI bool // WebUI有効 262 DatabaseName string // データベース名 263 DatabaseRevision string // リビジョン 264 DatabaseTitle string // データベースタイトル 265 DatabaseVersion string // データベースバージョン 266 // ReplicaUser string // レプリケーションユーザー 現在はreplica固定 267 ReplicaPassword string // レプリケーションパスワード 268 SourceAppliance *Resource // クローン元DB 269} 270 271// SlaveDatabaseValue スレーブデータベース作成用パラメータ 272type SlaveDatabaseValue struct { 273 Plan DatabasePlan // プラン 274 DefaultUser string // ユーザー名 275 UserPassword string // パスワード 276 SwitchID ID // 接続先スイッチ 277 IPAddress1 string // IPアドレス1 278 MaskLen int // ネットワークマスク長 279 DefaultRoute string // デフォルトルート 280 Name string // 名称 281 Description string // 説明 282 Tags []string // タグ 283 Icon *Resource // アイコン 284 DatabaseName string // データベース名 285 DatabaseVersion string // データベースバージョン 286 // ReplicaUser string // レプリケーションユーザー 現在はreplica固定 287 ReplicaPassword string // レプリケーションパスワード 288 MasterApplianceID ID // クローン元DB 289 MasterIPAddress string // マスターIPアドレス 290 MasterPort int // マスターポート 291} 292 293// NewCreatePostgreSQLDatabaseValue PostgreSQL作成用パラメーター 294func NewCreatePostgreSQLDatabaseValue() *CreateDatabaseValue { 295 return &CreateDatabaseValue{ 296 DatabaseName: "postgres", 297 DatabaseVersion: "12", 298 } 299} 300 301// NewCreateMariaDBDatabaseValue MariaDB作成用パラメーター 302func NewCreateMariaDBDatabaseValue() *CreateDatabaseValue { 303 return &CreateDatabaseValue{ 304 DatabaseName: "MariaDB", 305 DatabaseVersion: "10.4", 306 } 307} 308 309// NewCloneDatabaseValue クローンDB作成用パラメータ 310func NewCloneDatabaseValue(db *Database) *CreateDatabaseValue { 311 return &CreateDatabaseValue{ 312 DatabaseName: db.Remark.DBConf.Common.DatabaseName, 313 DatabaseVersion: db.Remark.DBConf.Common.DatabaseVersion, 314 SourceAppliance: NewResource(db.ID), 315 } 316} 317 318// CreateNewDatabase データベース作成 319func CreateNewDatabase(values *CreateDatabaseValue) *Database { 320 321 db := &Database{ 322 // Appliance 323 Appliance: &Appliance{ 324 // Class 325 Class: "database", 326 // Name 327 propName: propName{Name: values.Name}, 328 // Description 329 propDescription: propDescription{Description: values.Description}, 330 // TagsType 331 propTags: propTags{ 332 // Tags 333 Tags: values.Tags, 334 }, 335 // Icon 336 propIcon: propIcon{ 337 &Icon{ 338 // Resource 339 Resource: values.Icon, 340 }, 341 }, 342 // Plan 343 //propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, 344 }, 345 // Remark 346 Remark: &DatabaseRemark{ 347 // ApplianceRemarkBase 348 ApplianceRemarkBase: &ApplianceRemarkBase{ 349 // Servers 350 Servers: []interface{}{""}, 351 }, 352 // DBConf 353 DBConf: &DatabaseCommonRemarks{ 354 // Common 355 Common: &DatabaseCommonRemark{ 356 // DatabaseName 357 DatabaseName: values.DatabaseName, 358 // DatabaseRevision 359 DatabaseRevision: values.DatabaseRevision, 360 // DatabaseTitle 361 DatabaseTitle: values.DatabaseTitle, 362 // DatabaseVersion 363 DatabaseVersion: values.DatabaseVersion, 364 }, 365 }, 366 // Plan 367 propPlanID: propPlanID{Plan: &Resource{ID: ID(values.Plan)}}, 368 }, 369 // Settings 370 Settings: &DatabaseSettings{ 371 // DBConf 372 DBConf: &DatabaseSetting{ 373 // Backup 374 Backup: &DatabaseBackupSetting{ 375 // Rotate 376 // Rotate: values.BackupRotate, 377 Rotate: 8, 378 // Time 379 Time: values.BackupTime, 380 // DayOfWeek 381 DayOfWeek: values.BackupDayOfWeek, 382 }, 383 // Common 384 Common: &DatabaseCommonSetting{ 385 // DefaultUser 386 DefaultUser: values.DefaultUser, 387 // UserPassword 388 UserPassword: values.UserPassword, 389 // SourceNetwork 390 SourceNetwork: SourceNetwork(values.SourceNetwork), 391 }, 392 }, 393 }, 394 } 395 396 if !values.SourceAppliance.ID.IsEmpty() { 397 db.Remark.SourceAppliance = &DatabaseSourceAppliance{ID: values.SourceAppliance.ID} 398 } 399 400 if values.ServicePort > 0 { 401 db.Settings.DBConf.Common.ServicePort = json.Number(fmt.Sprintf("%d", values.ServicePort)) 402 } 403 404 if !values.EnableBackup { 405 db.Settings.DBConf.Backup = nil 406 } 407 408 db.Remark.Switch = &ApplianceRemarkSwitch{ 409 // ID 410 ID: values.SwitchID, 411 } 412 db.Remark.Network = &DatabaseRemarkNetwork{ 413 // NetworkMaskLen 414 NetworkMaskLen: values.MaskLen, 415 // DefaultRoute 416 DefaultRoute: values.DefaultRoute, 417 } 418 419 db.Remark.Servers = []interface{}{ 420 map[string]interface{}{"IPAddress": values.IPAddress1}, 421 } 422 423 if values.WebUI { 424 db.Settings.DBConf.Common.WebUI = values.WebUI 425 } 426 427 if values.ReplicaPassword != "" { 428 db.Settings.DBConf.Common.ReplicaUser = "replica" 429 db.Settings.DBConf.Common.ReplicaPassword = values.ReplicaPassword 430 db.Settings.DBConf.Replication = &DatabaseReplicationSetting{ 431 Model: DatabaseReplicationModelMasterSlave, 432 } 433 } 434 435 return db 436} 437 438// NewSlaveDatabaseValue スレーブ向けパラメータ作成 439func NewSlaveDatabaseValue(values *SlaveDatabaseValue) *Database { 440 db := &Database{ 441 // Appliance 442 Appliance: &Appliance{ 443 // Class 444 Class: "database", 445 // Name 446 propName: propName{Name: values.Name}, 447 // Description 448 propDescription: propDescription{Description: values.Description}, 449 // TagsType 450 propTags: propTags{ 451 // Tags 452 Tags: values.Tags, 453 }, 454 // Icon 455 propIcon: propIcon{ 456 &Icon{ 457 // Resource 458 Resource: values.Icon, 459 }, 460 }, 461 // Plan 462 //propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, 463 }, 464 // Remark 465 Remark: &DatabaseRemark{ 466 // ApplianceRemarkBase 467 ApplianceRemarkBase: &ApplianceRemarkBase{ 468 // Servers 469 Servers: []interface{}{""}, 470 }, 471 // DBConf 472 DBConf: &DatabaseCommonRemarks{ 473 // Common 474 Common: &DatabaseCommonRemark{ 475 // DatabaseName 476 DatabaseName: values.DatabaseName, 477 // DatabaseVersion 478 DatabaseVersion: values.DatabaseVersion, 479 }, 480 }, 481 // Plan 482 propPlanID: propPlanID{Plan: &Resource{ID: ID(int64(values.Plan) + 1)}}, 483 }, 484 // Settings 485 Settings: &DatabaseSettings{ 486 // DBConf 487 DBConf: &DatabaseSetting{ 488 // Common 489 Common: &DatabaseCommonSetting{ 490 // DefaultUser 491 DefaultUser: values.DefaultUser, 492 // UserPassword 493 UserPassword: values.UserPassword, 494 }, 495 // Replication 496 Replication: &DatabaseReplicationSetting{ 497 Model: DatabaseReplicationModelAsyncReplica, 498 Appliance: &DatabaseSourceAppliance{ID: values.MasterApplianceID}, 499 IPAddress: values.MasterIPAddress, 500 Port: values.MasterPort, 501 User: "replica", 502 Password: values.ReplicaPassword, 503 }, 504 }, 505 }, 506 } 507 508 db.Remark.Switch = &ApplianceRemarkSwitch{ 509 // ID 510 ID: values.SwitchID, 511 } 512 db.Remark.Network = &DatabaseRemarkNetwork{ 513 // NetworkMaskLen 514 NetworkMaskLen: values.MaskLen, 515 // DefaultRoute 516 DefaultRoute: values.DefaultRoute, 517 } 518 519 db.Remark.Servers = []interface{}{ 520 map[string]interface{}{"IPAddress": values.IPAddress1}, 521 } 522 523 return db 524} 525 526// AddSourceNetwork 接続許可ネットワーク 追加 527func (s *Database) AddSourceNetwork(nw string) { 528 res := []string(s.Settings.DBConf.Common.SourceNetwork) 529 res = append(res, nw) 530 s.Settings.DBConf.Common.SourceNetwork = SourceNetwork(res) 531} 532 533// DeleteSourceNetwork 接続許可ネットワーク 削除 534func (s *Database) DeleteSourceNetwork(nw string) { 535 res := []string{} 536 for _, s := range s.Settings.DBConf.Common.SourceNetwork { 537 if s != nw { 538 res = append(res, s) 539 } 540 } 541 s.Settings.DBConf.Common.SourceNetwork = SourceNetwork(res) 542} 543 544// IsReplicationMaster レプリケーションが有効かつマスターとして構成されているか 545func (s *Database) IsReplicationMaster() bool { 546 return s.IsReplicationEnabled() && s.Settings.DBConf.Replication.Model == DatabaseReplicationModelMasterSlave 547} 548 549// IsReplicationEnabled レプリケーションが有効な場合はTrueを返す 550func (s *Database) IsReplicationEnabled() bool { 551 return s.Settings.DBConf.Replication != nil 552} 553 554// DatabaseName MariaDB or PostgreSQLの何れかを返す 555func (s *Database) DatabaseName() string { 556 return s.Remark.DBConf.Common.DatabaseName 557} 558 559// DatabaseRevision データベースのリビジョンを返す 560// 561// 例: MariaDBの場合 => 10.2.15 / PostgreSQLの場合 => 10.3 562func (s *Database) DatabaseRevision() string { 563 return s.Remark.DBConf.Common.DatabaseRevision 564} 565 566// DatabaseVersion データベースのバージョンを返す 567// 568// 例: MariaDBの場合 => 10.2 / PostgreSQLの場合 => 10 569func (s *Database) DatabaseVersion() string { 570 return s.Remark.DBConf.Common.DatabaseVersion 571} 572 573// WebUIAddress WebUIが有効な場合、IPアドレス or FQDNを返す、無効な場合は空文字を返す 574func (s *Database) WebUIAddress() string { 575 webUI := s.Settings.DBConf.Common.WebUI 576 if webUI != nil { 577 if v, ok := webUI.(string); ok { 578 return v 579 } 580 } 581 return "" 582} 583 584// IPAddress IPアドレスを取得 585func (s *Database) IPAddress() string { 586 if len(s.Remark.Servers) < 1 { 587 return "" 588 } 589 v, ok := s.Remark.Servers[0].(map[string]string) 590 if !ok { 591 return "" 592 } 593 return v["IPAddress"] 594} 595 596// NetworkMaskLen ネットワークマスク長を取得 597func (s *Database) NetworkMaskLen() int { 598 if s.Remark.Network == nil { 599 return -1 600 } 601 return s.Remark.Network.NetworkMaskLen 602} 603 604// DefaultRoute デフォルトゲートウェイアドレスを取得 605func (s *Database) DefaultRoute() string { 606 if s.Remark.Network == nil { 607 return "" 608 } 609 return s.Remark.Network.DefaultRoute 610} 611