yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/dbinstance_backup.go (about) 1 // Copyright 2019 Yunion 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 15 package aliyun 16 17 import ( 18 "fmt" 19 "strings" 20 "time" 21 22 "yunion.io/x/log" 23 "yunion.io/x/pkg/errors" 24 "yunion.io/x/pkg/utils" 25 26 api "yunion.io/x/cloudmux/pkg/apis/compute" 27 "yunion.io/x/cloudmux/pkg/cloudprovider" 28 "yunion.io/x/cloudmux/pkg/multicloud" 29 ) 30 31 type SDBInstanceBackup struct { 32 multicloud.SDBInstanceBackupBase 33 AliyunTags 34 region *SRegion 35 36 BackupDBNames string 37 BackupIntranetDownloadURL string 38 BackupDownloadURL string 39 BackupEndTime time.Time 40 BackupId string 41 BackupLocation string 42 BackupMethod string 43 BackupMode string 44 BackupScale string 45 BackupSize int 46 BackupStartTime time.Time 47 BackupStatus string 48 BackupType string 49 DBInstanceId string 50 HostInstanceID int 51 MetaStatus string 52 StoreStatus string 53 } 54 55 func (backup *SDBInstanceBackup) GetId() string { 56 return backup.BackupId 57 } 58 59 func (backup *SDBInstanceBackup) GetGlobalId() string { 60 return backup.BackupId 61 } 62 63 func (backup *SDBInstanceBackup) GetName() string { 64 return backup.BackupId 65 } 66 67 func (backup *SDBInstanceBackup) GetStartTime() time.Time { 68 return backup.BackupStartTime 69 } 70 71 func (backup *SDBInstanceBackup) GetEndTime() time.Time { 72 return backup.BackupEndTime 73 } 74 75 func (backup *SDBInstanceBackup) GetBackupMode() string { 76 switch backup.BackupMode { 77 case "Manual": 78 return api.BACKUP_MODE_MANUAL 79 default: 80 return api.BACKUP_MODE_AUTOMATED 81 } 82 } 83 84 func (backup *SDBInstanceBackup) GetStatus() string { 85 switch backup.BackupStatus { 86 case "Success": 87 return api.DBINSTANCE_BACKUP_READY 88 case "Failed": 89 return api.DBINSTANCE_BACKUP_FAILED 90 default: 91 return api.DBINSTANCE_BACKUP_UNKNOWN 92 } 93 } 94 95 func (backup *SDBInstanceBackup) GetBackupSizeMb() int { 96 return backup.BackupSize / 1024 / 1024 97 } 98 99 func (backup *SDBInstanceBackup) GetDBNames() string { 100 return backup.BackupDBNames 101 } 102 103 func (backup *SDBInstanceBackup) GetEngine() string { 104 instance, _ := backup.region.GetDBInstanceDetail(backup.DBInstanceId) 105 if instance != nil { 106 return instance.Engine 107 } 108 return "" 109 } 110 111 func (backup *SDBInstanceBackup) GetEngineVersion() string { 112 instance, _ := backup.region.GetDBInstanceDetail(backup.DBInstanceId) 113 if instance != nil { 114 return instance.EngineVersion 115 } 116 return "" 117 } 118 119 func (backup *SDBInstanceBackup) GetDBInstanceId() string { 120 return backup.DBInstanceId 121 } 122 123 func (region *SRegion) GetDBInstanceBackups(instanceId, backupId string, offset int, limit int) ([]SDBInstanceBackup, int, error) { 124 if limit > 50 || limit <= 0 { 125 limit = 50 126 } 127 params := map[string]string{ 128 "RegionId": region.RegionId, 129 "PageSize": fmt.Sprintf("%d", limit), 130 "PageNumber": fmt.Sprintf("%d", (offset/limit)+1), 131 "DBInstanceId": instanceId, 132 } 133 if len(backupId) > 0 { 134 params["BackupId"] = backupId 135 } 136 body, err := region.rdsRequest("DescribeBackups", params) 137 if err != nil { 138 return nil, 0, errors.Wrap(err, "DescribeBackups") 139 } 140 backups := []SDBInstanceBackup{} 141 err = body.Unmarshal(&backups, "Items", "Backup") 142 if err != nil { 143 return nil, 0, errors.Wrap(err, "Unmarshal") 144 } 145 total, _ := body.Int("TotalRecordCount") 146 return backups, int(total), nil 147 } 148 149 func (region *SRegion) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) { 150 dbinstnaces, err := region.GetIDBInstances() 151 if err != nil { 152 return nil, err 153 } 154 ibackups := []cloudprovider.ICloudDBInstanceBackup{} 155 for i := 0; i < len(dbinstnaces); i++ { 156 _dbinstance := dbinstnaces[i].(*SDBInstance) 157 _ibackup, err := _dbinstance.GetIDBInstanceBackups() 158 if err != nil { 159 return nil, errors.Wrapf(err, "_dbinstance(%v).GetIDBInstanceBackups", _dbinstance) 160 } 161 ibackups = append(ibackups, _ibackup...) 162 } 163 return ibackups, nil 164 } 165 166 func (rds *SDBInstance) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) { 167 backups := []SDBInstanceBackup{} 168 for { 169 parts, total, err := rds.region.GetDBInstanceBackups(rds.DBInstanceId, "", len(backups), 50) 170 if err != nil { 171 return nil, err 172 } 173 backups = append(backups, parts...) 174 if len(backups) >= total { 175 break 176 } 177 } 178 179 ibackups := []cloudprovider.ICloudDBInstanceBackup{} 180 for i := 0; i < len(backups); i++ { 181 backups[i].region = rds.region 182 ibackups = append(ibackups, &backups[i]) 183 } 184 return ibackups, nil 185 } 186 187 func (self *SRegion) CreateDBInstanceBackup(rdsId string, databases []string) (string, error) { 188 rds, err := self.GetDBInstanceDetail(rdsId) 189 if err != nil { 190 return "", errors.Wrapf(err, "GetDBInstanceDetail") 191 } 192 params := map[string]string{ 193 "DBInstanceId": rdsId, 194 } 195 switch rds.Engine { 196 case api.DBINSTANCE_TYPE_MYSQL: 197 if utils.IsInStringArray(rds.EngineVersion, []string{"5.7", "8.0"}) && ((utils.IsInStringArray(rds.GetStorageType(), []string{ 198 api.ALIYUN_DBINSTANCE_STORAGE_TYPE_CLOUD_ESSD, 199 api.ALIYUN_DBINSTANCE_STORAGE_TYPE_CLOUD_SSD, 200 }) && rds.GetCategory() == api.ALIYUN_DBINSTANCE_CATEGORY_HA) || 201 (rds.GetStorageType() == api.ALIYUN_DBINSTANCE_STORAGE_TYPE_CLOUD_SSD && 202 rds.GetCategory() == api.ALIYUN_DBINSTANCE_CATEGORY_BASIC)) { 203 params["BackupMethod"] = "Snapshot" 204 } else { 205 params["BackupMethod"] = "Physical" 206 if len(databases) > 0 { 207 params["BackupStrategy"] = "db" 208 params["DBName"] = strings.Join(databases, ",") 209 params["BackupMethod"] = "Logical" 210 } 211 } 212 case api.DBINSTANCE_TYPE_MARIADB: 213 params["BackupMethod"] = "Snapshot" 214 case api.DBINSTANCE_TYPE_SQLSERVER: 215 params["BackupMethod"] = "Physical" 216 case api.DBINSTANCE_TYPE_POSTGRESQL: 217 if rds.GetStorageType() == api.ALIYUN_DBINSTANCE_STORAGE_TYPE_LOCAL_SSD { 218 params["BackupMethod"] = "Physical" 219 } else { 220 params["BackupMethod"] = "Snapshot" 221 } 222 case api.DBINSTANCE_TYPE_PPAS: 223 params["BackupMethod"] = "Physical" 224 } 225 body, err := self.rdsRequest("CreateBackup", params) 226 if err != nil { 227 return "", errors.Wrap(err, "CreateBackup") 228 } 229 jobId, err := body.GetString("BackupJobId") 230 if err != nil { 231 return "", errors.Wrap(err, "body.BackupJobId") 232 } 233 return self.waitBackupCreateComplete(rds.DBInstanceId, jobId) 234 } 235 236 func (rds *SDBInstance) CreateIBackup(conf *cloudprovider.SDBInstanceBackupCreateConfig) (string, error) { 237 return rds.region.CreateDBInstanceBackup(rds.DBInstanceId, conf.Databases) 238 } 239 240 func (backup *SDBInstanceBackup) Delete() error { 241 return backup.region.DeleteDBInstanceBackup(backup.DBInstanceId, backup.BackupId) 242 } 243 244 func (region *SRegion) DeleteDBInstanceBackup(instanceId string, backupId string) error { 245 params := map[string]string{ 246 "DBInstanceId": instanceId, 247 "BackupId": backupId, 248 } 249 _, err := region.rdsRequest("DeleteBackup", params) 250 return err 251 } 252 253 type SDBInstanceBackupJob struct { 254 BackupProgressStatus string 255 Process string 256 JobMode string 257 TaskAction string 258 BackupStatus string 259 BackupJobId string 260 BackupId string 261 } 262 263 type SDBInstanceBackupJobs struct { 264 BackupJob []SDBInstanceBackupJob 265 } 266 267 func (region *SRegion) GetDBInstanceBackupJobs(instanceId, jobId string) (*SDBInstanceBackupJobs, error) { 268 params := map[string]string{ 269 "DBInstanceId": instanceId, 270 "ClientToken": utils.GenRequestId(20), 271 "BackupMode": "Manual", 272 } 273 if len(jobId) > 0 { 274 params["BackupJobId"] = jobId 275 } 276 body, err := region.rdsRequest("DescribeBackupTasks", params) 277 if err != nil { 278 return nil, errors.Wrap(err, "DescribeBackupTasks") 279 } 280 281 jobs := SDBInstanceBackupJobs{} 282 283 err = body.Unmarshal(&jobs, "Items") 284 if err != nil { 285 return nil, errors.Wrapf(err, "body.Unmarshal(%s)", body) 286 } 287 288 return &jobs, nil 289 } 290 291 func (region *SRegion) waitBackupCreateComplete(instanceId, jobId string) (string, error) { 292 err := cloudprovider.Wait(time.Second*10, time.Minute*40, func() (bool, error) { 293 jobs, err := region.GetDBInstanceBackupJobs(instanceId, jobId) 294 if err != nil { 295 return false, errors.Wrapf(err, "region.GetDBInstanceBackupJobs(%s, %s)", instanceId, jobId) 296 } 297 if len(jobs.BackupJob) == 0 { 298 return true, nil 299 } 300 for _, job := range jobs.BackupJob { 301 log.Infof("instance %s backup job %s status: %s(%s)", instanceId, jobId, job.BackupStatus, job.Process) 302 if job.BackupStatus == "Finished" && job.BackupJobId == jobId { 303 return true, nil 304 } 305 if job.BackupStatus == "Failed" && job.BackupJobId == jobId { 306 return false, fmt.Errorf("instance %s backup job %s failed", instanceId, jobId) 307 } 308 } 309 return false, nil 310 }) 311 if err != nil { 312 return "", errors.Wrapf(err, "wait backup create job") 313 } 314 jobs, err := region.GetDBInstanceBackupJobs(instanceId, jobId) 315 if err != nil { 316 return "", errors.Wrapf(err, "region.GetDBInstanceBackupJobs(%s, %s)", instanceId, jobId) 317 } 318 for _, job := range jobs.BackupJob { 319 if job.BackupStatus == "Finished" && job.BackupJobId == jobId { 320 if len(job.BackupId) == 0 { 321 return "", fmt.Errorf("Missing backup id") 322 } 323 return job.BackupId, nil 324 } 325 } 326 return "", fmt.Errorf("failed to found backup job %s backupid", jobId) 327 } 328 329 func (self *SDBInstanceBackup) GetBackupMethod() cloudprovider.TBackupMethod { 330 return cloudprovider.TBackupMethod(self.BackupMethod) 331 } 332 333 func (self *SDBInstanceBackup) CreateICloudDBInstance(opts *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) { 334 rdsId, err := self.region.CreateDBInstanceByBackup(self.BackupId, opts) 335 if err != nil { 336 return nil, errors.Wrapf(err, "CreateDBInstanceByBackup") 337 } 338 return self.region.GetDBInstanceDetail(rdsId) 339 } 340 341 func (self *SRegion) CreateDBInstanceByBackup(backupId string, opts *cloudprovider.SManagedDBInstanceCreateConfig) (string, error) { 342 params := map[string]string{ 343 "DBInstanceId": opts.RdsId, 344 "DBInstanceStorageType": opts.StorageType, 345 "PayType": "Postpaid", 346 "BackupId": backupId, 347 } 348 resp, err := self.rdsRequest("CloneDBInstance", params) 349 if err != nil { 350 return "", errors.Wrapf(err, "rdsRequest") 351 } 352 rdsId, err := resp.GetString("DBInstanceId") 353 if err != nil { 354 return "", fmt.Errorf("missing DBInstanceId after CloneDBInstance") 355 } 356 return rdsId, nil 357 }