yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/rds_mysql_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 qcloud
    16  
    17  import (
    18  	"fmt"
    19  	"time"
    20  
    21  	"yunion.io/x/log"
    22  	"yunion.io/x/pkg/errors"
    23  	"yunion.io/x/pkg/util/timeutils"
    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 SMySQLInstanceBackup struct {
    32  	multicloud.SDBInstanceBackupBase
    33  	QcloudTags
    34  	rds *SMySQLInstance
    35  
    36  	Name        string
    37  	Size        int
    38  	Date        string
    39  	IntranetUrl string
    40  	InternetUrl string
    41  	Type        string
    42  	BackupId    int
    43  	Status      string
    44  	FinishTime  string
    45  	Creator     string
    46  	StartTime   string
    47  	Method      string
    48  	Way         string
    49  }
    50  
    51  func (self *SMySQLInstanceBackup) GetId() string {
    52  	return fmt.Sprintf("%d", self.BackupId)
    53  }
    54  
    55  func (self *SMySQLInstanceBackup) GetGlobalId() string {
    56  	return self.GetId()
    57  }
    58  
    59  func (self *SMySQLInstanceBackup) GetName() string {
    60  	if len(self.Name) > 0 {
    61  		return self.Name
    62  	}
    63  	return self.GetId()
    64  }
    65  
    66  func (self *SMySQLInstanceBackup) GetEngine() string {
    67  	return api.DBINSTANCE_TYPE_MYSQL
    68  }
    69  
    70  func (self *SMySQLInstanceBackup) GetStatus() string {
    71  	switch self.Status {
    72  	case "SUCCESS":
    73  		return api.DBINSTANCE_BACKUP_READY
    74  	case "FAILED":
    75  		return api.DBINSTANCE_BACKUP_CREATE_FAILED
    76  	case "RUNNING":
    77  		return api.DBINSTANCE_BACKUP_CREATING
    78  	default:
    79  		return api.DBINSTANCE_BACKUP_UNKNOWN
    80  	}
    81  }
    82  
    83  func (self *SMySQLInstanceBackup) GetEngineVersion() string {
    84  	return self.rds.EngineVersion
    85  }
    86  
    87  func (self *SMySQLInstanceBackup) GetDBInstanceId() string {
    88  	return self.rds.InstanceId
    89  }
    90  
    91  func (self *SMySQLInstanceBackup) GetStartTime() time.Time {
    92  	start, err := timeutils.ParseTimeStr(self.StartTime)
    93  	if err != nil {
    94  		return time.Time{}
    95  	}
    96  	return start.Add(time.Hour * -8)
    97  }
    98  
    99  func (self *SMySQLInstanceBackup) GetEndTime() time.Time {
   100  	end, err := timeutils.ParseTimeStr(self.FinishTime)
   101  	if err != nil {
   102  		return time.Time{}
   103  	}
   104  	return end.Add(time.Hour * -8)
   105  }
   106  
   107  func (self *SMySQLInstanceBackup) GetBackupSizeMb() int {
   108  	return self.Size / 1024 / 1024
   109  }
   110  
   111  func (self *SMySQLInstanceBackup) GetDBNames() string {
   112  	return ""
   113  }
   114  
   115  func (self *SMySQLInstanceBackup) GetBackupMode() string {
   116  	if self.Way == "manual" {
   117  		return api.BACKUP_MODE_MANUAL
   118  	}
   119  	return api.BACKUP_MODE_AUTOMATED
   120  }
   121  
   122  func (self *SMySQLInstanceBackup) Delete() error {
   123  	return self.rds.region.DeleteBackup(self.rds.InstanceId, fmt.Sprintf("%d", self.BackupId))
   124  }
   125  
   126  func (self *SMySQLInstance) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) {
   127  	backups := []cloudprovider.ICloudDBInstanceBackup{}
   128  	for {
   129  		part, total, err := self.region.DescribeMySQLBackups(self.InstanceId, len(backups), 100)
   130  		if err != nil {
   131  			return nil, errors.Wrapf(err, "DescribeMySQLBackups")
   132  		}
   133  		for i := range part {
   134  			part[i].rds = self
   135  			backups = append(backups, &part[i])
   136  		}
   137  		if len(backups) >= total {
   138  			break
   139  		}
   140  	}
   141  	return backups, nil
   142  }
   143  
   144  func (self *SRegion) DescribeMySQLBackups(instanceId string, offset, limit int) ([]SMySQLInstanceBackup, int, error) {
   145  	if limit < 1 || limit > 100 {
   146  		limit = 100
   147  	}
   148  	params := map[string]string{
   149  		"Offset": fmt.Sprintf("%d", offset),
   150  		"Limit":  fmt.Sprintf("%d", limit),
   151  	}
   152  	if len(instanceId) > 0 {
   153  		params["InstanceId"] = instanceId
   154  	}
   155  	resp, err := self.cdbRequest("DescribeBackups", params)
   156  	if err != nil {
   157  		return nil, 0, errors.Wrapf(err, "DescribeBackups")
   158  	}
   159  	backups := []SMySQLInstanceBackup{}
   160  	err = resp.Unmarshal(&backups, "Items")
   161  	if err != nil {
   162  		return nil, 0, errors.Wrapf(err, "resp.Unmarshal")
   163  	}
   164  	totalCount, _ := resp.Float("TotalCount")
   165  	return backups, int(totalCount), nil
   166  }
   167  
   168  func (self *SRegion) DeleteBackup(instanceId, id string) error {
   169  	params := map[string]string{
   170  		"InstanceId": instanceId,
   171  		"BackupId":   id,
   172  	}
   173  	_, err := self.cdbRequest("DeleteBackup", params)
   174  	if err != nil {
   175  		return errors.Wrapf(err, "DeleteBackup")
   176  	}
   177  	return nil
   178  }
   179  
   180  func (self *SRegion) GetMySQLInstanceBackup(instanceId, backupId string) (*SMySQLInstanceBackup, error) {
   181  	backups := []SMySQLInstanceBackup{}
   182  	for {
   183  		part, total, err := self.DescribeMySQLBackups(instanceId, len(backups), 100)
   184  		if err != nil {
   185  			return nil, errors.Wrapf(err, "DescribeMySQLBackups")
   186  		}
   187  		for i := range part {
   188  			if fmt.Sprintf("%d", part[i].BackupId) == backupId {
   189  				return &part[i], nil
   190  			}
   191  		}
   192  		backups = append(backups, part...)
   193  		if len(backups) >= total {
   194  			break
   195  		}
   196  	}
   197  	return nil, fmt.Errorf("failed to found rds %s backup %s", instanceId, backupId)
   198  }
   199  
   200  func (self *SRegion) waitMySQLBackupReady(instanceId, backupId string) error {
   201  	return cloudprovider.Wait(time.Second*20, time.Minute*30, func() (bool, error) {
   202  		backup, err := self.GetMySQLInstanceBackup(instanceId, backupId)
   203  		if err != nil {
   204  			return false, errors.Wrapf(err, "GetMySQLInstanceBackup")
   205  		}
   206  		log.Infof("backup %s for instance %s status %s", backup.GetName(), instanceId, backup.Status)
   207  		if utils.IsInStringArray(backup.Status, []string{"FAILED", "SUCCESS"}) {
   208  			return true, nil
   209  		}
   210  		return false, nil
   211  	})
   212  }
   213  
   214  func (self *SRegion) CreateMySQLBackup(instanceId string, tables map[string]string) (string, error) {
   215  	params := map[string]string{
   216  		"InstanceId":   instanceId,
   217  		"BackupMethod": "physical",
   218  	}
   219  	if len(tables) > 0 {
   220  		params["BackupMethod"] = "logical"
   221  		idx := 0
   222  		for db, table := range tables {
   223  			params[fmt.Sprintf("BackupDBTableList.%d.Db", idx)] = db
   224  			if len(table) > 0 {
   225  				params[fmt.Sprintf("BackupDBTableList.%d.Table", idx)] = table
   226  			}
   227  			idx++
   228  		}
   229  	}
   230  	resp, err := self.cdbRequest("CreateBackup", params)
   231  	if err != nil {
   232  		return "", errors.Wrapf(err, "CreateBackup")
   233  	}
   234  	_backupId, _ := resp.Float("BackupId")
   235  	backupId := fmt.Sprintf("%d", int(_backupId))
   236  	err = self.waitMySQLBackupReady(instanceId, backupId)
   237  	if err != nil {
   238  		return "", errors.Wrapf(err, "waitBackupReady")
   239  	}
   240  	return backupId, nil
   241  }