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  }