yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcso/dbinstance.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 hcso
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"time"
    21  
    22  	"github.com/pkg/errors"
    23  
    24  	"yunion.io/x/jsonutils"
    25  	"yunion.io/x/log"
    26  
    27  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    28  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    29  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    30  	"yunion.io/x/cloudmux/pkg/multicloud"
    31  	"yunion.io/x/cloudmux/pkg/multicloud/huawei"
    32  	"yunion.io/x/onecloud/pkg/util/billing"
    33  )
    34  
    35  type SBackupStrategy struct {
    36  	KeepDays  int
    37  	StartTime string
    38  }
    39  
    40  type SDatastore struct {
    41  	Type    string
    42  	Version string
    43  }
    44  
    45  type SHa struct {
    46  	ReplicationMode string
    47  }
    48  
    49  type SNonde struct {
    50  	AvailabilityZone string
    51  	Id               string
    52  	Name             string
    53  	Role             string
    54  	Staus            string
    55  }
    56  
    57  type SVolume struct {
    58  	Size int
    59  	Type string
    60  }
    61  
    62  type SRelatedInstance struct {
    63  	Id   string
    64  	Type string
    65  }
    66  
    67  type SDBInstance struct {
    68  	multicloud.SDBInstanceBase
    69  	huawei.HuaweiTags
    70  	region *SRegion
    71  
    72  	flavorCache []SDBInstanceFlavor
    73  
    74  	BackupStrategy      SBackupStrategy
    75  	Created             string //time.Time
    76  	Datastore           SDatastore
    77  	DbUserName          string
    78  	DIskEncryptionId    string
    79  	FlavorRef           string
    80  	Ha                  SHa
    81  	Id                  string
    82  	MaintenanceWindow   string
    83  	Name                string
    84  	Nodes               []SNonde
    85  	Port                int
    86  	PrivateIps          []string
    87  	PublicIps           []string
    88  	Region              string
    89  	RelatedInstance     []SRelatedInstance
    90  	SecurityGroupId     string
    91  	Status              string
    92  	SubnetId            string
    93  	SwitchStrategy      string
    94  	TimeZone            string
    95  	Type                string
    96  	Updated             string //time.Time
    97  	Volume              SVolume
    98  	VpcId               string
    99  	EnterpriseProjectId string
   100  }
   101  
   102  func (region *SRegion) GetDBInstances() ([]SDBInstance, error) {
   103  	params := map[string]string{}
   104  	dbinstances := []SDBInstance{}
   105  	err := doListAllWithOffset(region.ecsClient.DBInstance.List, params, &dbinstances)
   106  	return dbinstances, err
   107  }
   108  
   109  func (region *SRegion) GetDBInstance(instanceId string) (*SDBInstance, error) {
   110  	if len(instanceId) == 0 {
   111  		return nil, cloudprovider.ErrNotFound
   112  	}
   113  	instance := SDBInstance{region: region}
   114  	err := DoGet(region.ecsClient.DBInstance.Get, instanceId, nil, &instance)
   115  	return &instance, err
   116  }
   117  
   118  func (rds *SDBInstance) GetName() string {
   119  	return rds.Name
   120  }
   121  
   122  func (rds *SDBInstance) GetId() string {
   123  	return rds.Id
   124  }
   125  
   126  func (rds *SDBInstance) GetGlobalId() string {
   127  	return rds.GetId()
   128  }
   129  
   130  // 值为“BUILD”,表示实例正在创建。
   131  // 值为“ACTIVE”,表示实例正常。
   132  // 值为“FAILED”,表示实例异常。
   133  // 值为“FROZEN”,表示实例冻结。
   134  // 值为“MODIFYING”,表示实例正在扩容。
   135  // 值为“REBOOTING”,表示实例正在重启。
   136  // 值为“RESTORING”,表示实例正在恢复。
   137  // 值为“MODIFYING INSTANCE TYPE”,表示实例正在转主备。
   138  // 值为“SWITCHOVER”,表示实例正在主备切换。
   139  // 值为“MIGRATING”,表示实例正在迁移。
   140  // 值为“BACKING UP”,表示实例正在进行备份。
   141  // 值为“MODIFYING DATABASE PORT”,表示实例正在修改数据库端口。
   142  // 值为“STORAGE FULL”,表示实例磁盘空间满。
   143  
   144  func (rds *SDBInstance) GetStatus() string {
   145  	switch rds.Status {
   146  	case "BUILD", "MODIFYING", "MODIFYING INSTANCE TYPE", "SWITCHOVER", "MODIFYING DATABASE PORT":
   147  		return api.DBINSTANCE_DEPLOYING
   148  	case "ACTIVE":
   149  		return api.DBINSTANCE_RUNNING
   150  	case "FAILED", "FROZEN", "STORAGE FULL":
   151  		return api.DBINSTANCE_UNKNOWN
   152  	case "REBOOTING":
   153  		return api.DBINSTANCE_REBOOTING
   154  	case "RESTORING":
   155  		return api.DBINSTANCE_RESTORING
   156  	case "MIGRATING":
   157  		return api.DBINSTANCE_MIGRATING
   158  	case "BACKING UP":
   159  		return api.DBINSTANCE_BACKING_UP
   160  	}
   161  	return rds.Status
   162  }
   163  
   164  func (rds *SDBInstance) GetBillingType() string {
   165  	return billing_api.BILLING_TYPE_POSTPAID
   166  }
   167  
   168  func (rds *SDBInstance) GetSecurityGroupIds() ([]string, error) {
   169  	return []string{rds.SecurityGroupId}, nil
   170  }
   171  
   172  func (rds *SDBInstance) fetchFlavor() error {
   173  	if len(rds.flavorCache) > 0 {
   174  		return nil
   175  	}
   176  	flavors, err := rds.region.GetDBInstanceFlavors(rds.Datastore.Type, rds.Datastore.Version)
   177  	if err != nil {
   178  		return err
   179  	}
   180  	rds.flavorCache = flavors
   181  	return nil
   182  }
   183  
   184  func (rds *SDBInstance) GetExpiredAt() time.Time {
   185  	return time.Time{}
   186  }
   187  
   188  func (rds *SDBInstance) GetStorageType() string {
   189  	return rds.Volume.Type
   190  }
   191  
   192  func (rds *SDBInstance) GetCreatedAt() time.Time {
   193  	t, err := time.Parse("2006-01-02T15:04:05Z0700", rds.Created)
   194  	if err != nil {
   195  		return time.Time{}
   196  	}
   197  	return t
   198  }
   199  
   200  func (rds *SDBInstance) GetEngine() string {
   201  	return rds.Datastore.Type
   202  }
   203  
   204  func (rds *SDBInstance) GetEngineVersion() string {
   205  	return rds.Datastore.Version
   206  }
   207  
   208  func (rds *SDBInstance) GetInstanceType() string {
   209  	return rds.FlavorRef
   210  }
   211  
   212  func (rds *SDBInstance) GetCategory() string {
   213  	switch rds.Type {
   214  	case "Single":
   215  		return api.HUAWEI_DBINSTANCE_CATEGORY_SINGLE
   216  	case "Ha":
   217  		return api.HUAWEI_DBINSTANCE_CATEGORY_HA
   218  	case "Replica":
   219  		return api.HUAWEI_DBINSTANCE_CATEGORY_REPLICA
   220  	}
   221  	return rds.Type
   222  }
   223  
   224  func (rds *SDBInstance) GetVcpuCount() int {
   225  	err := rds.fetchFlavor()
   226  	if err != nil {
   227  		log.Errorf("failed to fetch flavors: %v", err)
   228  		return 0
   229  	}
   230  	for _, flavor := range rds.flavorCache {
   231  		if flavor.SpecCode == rds.FlavorRef {
   232  			return flavor.Vcpus
   233  		}
   234  	}
   235  	return 0
   236  }
   237  
   238  func (rds *SDBInstance) GetVmemSizeMB() int {
   239  	err := rds.fetchFlavor()
   240  	if err != nil {
   241  		log.Errorf("failed to fetch flavors: %v", err)
   242  		return 0
   243  	}
   244  	for _, flavor := range rds.flavorCache {
   245  		if flavor.SpecCode == rds.FlavorRef {
   246  			return flavor.Ram * 1024
   247  		}
   248  	}
   249  	return 0
   250  }
   251  
   252  func (rds *SDBInstance) GetDiskSizeGB() int {
   253  	return rds.Volume.Size
   254  }
   255  
   256  func (rds *SDBInstance) GetPort() int {
   257  	return rds.Port
   258  }
   259  
   260  func (rds *SDBInstance) GetMaintainTime() string {
   261  	return rds.MaintenanceWindow
   262  }
   263  
   264  func (rds *SDBInstance) GetIVpcId() string {
   265  	return rds.VpcId
   266  }
   267  
   268  func (rds *SDBInstance) GetProjectId() string {
   269  	return rds.EnterpriseProjectId
   270  }
   271  
   272  func (rds *SDBInstance) Refresh() error {
   273  	instance, err := rds.region.GetDBInstance(rds.Id)
   274  	if err != nil {
   275  		return err
   276  	}
   277  	return jsonutils.Update(rds, instance)
   278  }
   279  
   280  func (rds *SDBInstance) GetZone1Id() string {
   281  	return rds.GetZoneIdByRole("master")
   282  }
   283  
   284  func (rds *SDBInstance) GetZoneIdByRole(role string) string {
   285  	for _, node := range rds.Nodes {
   286  		if node.Role == role {
   287  			zone, err := rds.region.getZoneById(node.AvailabilityZone)
   288  			if err != nil {
   289  				log.Errorf("failed to found zone %s for rds %s error: %v", node.AvailabilityZone, rds.Name, err)
   290  				return ""
   291  			}
   292  			return zone.GetGlobalId()
   293  		}
   294  	}
   295  	return ""
   296  }
   297  
   298  func (rds *SDBInstance) GetZone2Id() string {
   299  	return rds.GetZoneIdByRole("slave")
   300  }
   301  
   302  func (rds *SDBInstance) GetZone3Id() string {
   303  	return ""
   304  }
   305  
   306  type SRdsNetwork struct {
   307  	SubnetId string
   308  	IP       string
   309  }
   310  
   311  func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) {
   312  	ret := []cloudprovider.SDBInstanceNetwork{}
   313  	for _, ip := range rds.PrivateIps {
   314  		network := cloudprovider.SDBInstanceNetwork{
   315  			IP:        ip,
   316  			NetworkId: rds.SubnetId,
   317  		}
   318  		ret = append(ret, network)
   319  	}
   320  	return ret, nil
   321  }
   322  
   323  func (rds *SDBInstance) GetInternalConnectionStr() string {
   324  	for _, ip := range rds.PrivateIps {
   325  		return ip
   326  	}
   327  	return ""
   328  }
   329  
   330  func (rds *SDBInstance) GetConnectionStr() string {
   331  	for _, ip := range rds.PublicIps {
   332  		return ip
   333  	}
   334  	return ""
   335  }
   336  
   337  func (region *SRegion) GetIDBInstanceById(instanceId string) (cloudprovider.ICloudDBInstance, error) {
   338  	dbinstance, err := region.GetDBInstance(instanceId)
   339  	if err != nil {
   340  		log.Errorf("failed to get dbinstance by id %s error: %v", instanceId, err)
   341  	}
   342  	return dbinstance, err
   343  }
   344  
   345  func (region *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) {
   346  	instances, err := region.GetDBInstances()
   347  	if err != nil {
   348  		return nil, errors.Wrapf(err, "region.GetDBInstances()")
   349  	}
   350  	idbinstances := []cloudprovider.ICloudDBInstance{}
   351  	for i := 0; i < len(instances); i++ {
   352  		instances[i].region = region
   353  		idbinstances = append(idbinstances, &instances[i])
   354  	}
   355  	return idbinstances, nil
   356  }
   357  
   358  func (rds *SDBInstance) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) {
   359  	parameters, err := rds.region.GetDBInstanceParameters(rds.Id)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	iparameters := []cloudprovider.ICloudDBInstanceParameter{}
   364  	for i := 0; i < len(parameters); i++ {
   365  		iparameters = append(iparameters, &parameters[i])
   366  	}
   367  	return iparameters, nil
   368  }
   369  
   370  func (rds *SDBInstance) GetIDBInstanceDatabases() ([]cloudprovider.ICloudDBInstanceDatabase, error) {
   371  	databases, err := rds.region.GetDBInstanceDatabases(rds.Id)
   372  	if err != nil {
   373  		return nil, errors.Wrap(err, "rds.region.GetDBInstanceDatabases(rds.Id)")
   374  	}
   375  
   376  	idatabase := []cloudprovider.ICloudDBInstanceDatabase{}
   377  	for i := 0; i < len(databases); i++ {
   378  		databases[i].instance = rds
   379  		idatabase = append(idatabase, &databases[i])
   380  	}
   381  	return idatabase, nil
   382  }
   383  
   384  func (rds *SDBInstance) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) {
   385  	accounts, err := rds.region.GetDBInstanceAccounts(rds.Id)
   386  	if err != nil {
   387  		return nil, errors.Wrap(err, "rds.region.GetDBInstanceAccounts(rds.Id)")
   388  	}
   389  
   390  	user := "root"
   391  	if rds.GetEngine() == api.DBINSTANCE_TYPE_SQLSERVER {
   392  		user = "rduser"
   393  	}
   394  
   395  	accounts = append(accounts, SDBInstanceAccount{
   396  		Name:     user,
   397  		instance: rds,
   398  	})
   399  
   400  	iaccounts := []cloudprovider.ICloudDBInstanceAccount{}
   401  	for i := 0; i < len(accounts); i++ {
   402  		accounts[i].instance = rds
   403  		iaccounts = append(iaccounts, &accounts[i])
   404  	}
   405  	return iaccounts, nil
   406  }
   407  
   408  func (rds *SDBInstance) Delete() error {
   409  	return rds.region.DeleteDBInstance(rds.Id)
   410  }
   411  
   412  func (region *SRegion) DeleteDBInstance(instanceId string) error {
   413  	_, err := region.ecsClient.DBInstance.Delete(instanceId, nil)
   414  	return err
   415  }
   416  
   417  func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) {
   418  	zoneIds := []string{}
   419  	zones, err := region.GetIZones()
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  	for _, zone := range zones {
   424  		zoneIds = append(zoneIds, zone.GetId())
   425  	}
   426  
   427  	if len(desc.SecgroupIds) == 0 {
   428  		return nil, fmt.Errorf("Missing secgroupId")
   429  	}
   430  
   431  	params := map[string]interface{}{
   432  		"region": region.ID,
   433  		"name":   desc.Name,
   434  		"datastore": map[string]string{
   435  			"type":    desc.Engine,
   436  			"version": desc.EngineVersion,
   437  		},
   438  		"password": desc.Password,
   439  		"volume": map[string]interface{}{
   440  			"type": desc.StorageType,
   441  			"size": desc.DiskSizeGB,
   442  		},
   443  		"vpc_id":            desc.VpcId,
   444  		"subnet_id":         desc.NetworkId,
   445  		"security_group_id": desc.SecgroupIds[0],
   446  	}
   447  
   448  	if len(desc.ProjectId) > 0 {
   449  		params["enterprise_project_id"] = desc.ProjectId
   450  	}
   451  
   452  	if len(desc.MasterInstanceId) > 0 {
   453  		params["replica_of_id"] = desc.MasterInstanceId
   454  		delete(params, "security_group_id")
   455  	}
   456  
   457  	if len(desc.RdsId) > 0 && len(desc.BackupId) > 0 {
   458  		params["restore_point"] = map[string]interface{}{
   459  			"backup_id":   desc.BackupId,
   460  			"instance_id": desc.RdsId,
   461  			"type":        "backup",
   462  		}
   463  	}
   464  
   465  	switch desc.Category {
   466  	case api.HUAWEI_DBINSTANCE_CATEGORY_HA:
   467  		switch desc.Engine {
   468  		case api.DBINSTANCE_TYPE_MYSQL, api.DBINSTANCE_TYPE_POSTGRESQL:
   469  			params["ha"] = map[string]string{
   470  				"mode":             "Ha",
   471  				"replication_mode": "async",
   472  			}
   473  		case api.DBINSTANCE_TYPE_SQLSERVER:
   474  			params["ha"] = map[string]string{
   475  				"mode":             "Ha",
   476  				"replication_mode": "sync",
   477  			}
   478  		}
   479  	case api.HUAWEI_DBINSTANCE_CATEGORY_SINGLE:
   480  	case api.HUAWEI_DBINSTANCE_CATEGORY_REPLICA:
   481  	}
   482  
   483  	if desc.BillingCycle != nil {
   484  		periodType := "month"
   485  		periodNum := desc.BillingCycle.GetMonths()
   486  		if desc.BillingCycle.GetYears() > 0 {
   487  			periodType = "year"
   488  			periodNum = desc.BillingCycle.GetYears()
   489  		}
   490  		params["charge_info"] = map[string]interface{}{
   491  			"charge_mode":   "prePaid",
   492  			"period_type":   periodType,
   493  			"period_num":    periodNum,
   494  			"is_auto_renew": false,
   495  		}
   496  	}
   497  	params["flavor_ref"] = desc.InstanceType
   498  	params["availability_zone"] = desc.ZoneId
   499  	resp, err := region.ecsClient.DBInstance.Create(jsonutils.Marshal(params))
   500  	if err != nil {
   501  		return nil, errors.Wrapf(err, "Create")
   502  	}
   503  
   504  	instance := &SDBInstance{region: region}
   505  	err = resp.Unmarshal(instance, "instance")
   506  	if err != nil {
   507  		return nil, errors.Wrap(err, `resp.Unmarshal(&instance, "instance")`)
   508  	}
   509  	if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
   510  		err = cloudprovider.Wait(10*time.Second, 20*time.Minute, func() (bool, error) {
   511  			job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
   512  			if err != nil {
   513  				return false, nil
   514  			}
   515  			status, _ := job.GetString("status")
   516  			process, _ := job.GetString("process")
   517  			log.Debugf("create dbinstance job %s status: %s process: %s", jobId, status, process)
   518  			if status == "Completed" {
   519  				return true, nil
   520  			}
   521  			if status == "Failed" {
   522  				return false, fmt.Errorf("create failed")
   523  			}
   524  			return false, nil
   525  		})
   526  	}
   527  	return instance, err
   528  }
   529  
   530  func (rds *SDBInstance) Reboot() error {
   531  	return rds.region.RebootDBInstance(rds.Id)
   532  }
   533  
   534  func (rds *SDBInstance) OpenPublicConnection() error {
   535  	return fmt.Errorf("Huawei current not support this operation")
   536  	//return rds.region.PublicConnectionAction(rds.Id, "openRC")
   537  }
   538  
   539  func (rds *SDBInstance) ClosePublicConnection() error {
   540  	return fmt.Errorf("Huawei current not support this operation")
   541  	//return rds.region.PublicConnectionAction(rds.Id, "closeRC")
   542  }
   543  
   544  func (region *SRegion) PublicConnectionAction(instanceId string, action string) error {
   545  	resp, err := region.ecsClient.DBInstance.PerformAction2(action, instanceId, nil, "")
   546  	if err != nil {
   547  		return errors.Wrapf(err, "rds.%s", action)
   548  	}
   549  
   550  	if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
   551  		err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
   552  			job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
   553  			if err != nil {
   554  				log.Errorf("failed to get job %s info error: %v", jobId, err)
   555  				return false
   556  			}
   557  			status, _ := job.GetString("status")
   558  			process, _ := job.GetString("process")
   559  			if status == "Completed" {
   560  				return true
   561  			}
   562  			log.Debugf("%s dbinstance job %s status: %s process: %s", action, jobId, status, process)
   563  			return false
   564  		})
   565  	}
   566  
   567  	return nil
   568  
   569  }
   570  
   571  func (region *SRegion) RebootDBInstance(instanceId string) error {
   572  	params := jsonutils.Marshal(map[string]interface{}{
   573  		"restart": map[string]string{},
   574  	})
   575  	resp, err := region.ecsClient.DBInstance.PerformAction2("action", instanceId, params, "")
   576  	if err != nil {
   577  		return err
   578  	}
   579  	if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
   580  		err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
   581  			job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
   582  			if err != nil {
   583  				log.Errorf("failed to get job %s info error: %v", jobId, err)
   584  				return false
   585  			}
   586  			status, _ := job.GetString("status")
   587  			process, _ := job.GetString("process")
   588  			if status == "Completed" {
   589  				return true
   590  			}
   591  			log.Debugf("reboot dbinstance job %s status: %s process: %s", jobId, status, process)
   592  			return false
   593  		})
   594  	}
   595  	return err
   596  }
   597  
   598  func (rds *SDBInstance) CreateAccount(conf *cloudprovider.SDBInstanceAccountCreateConfig) error {
   599  	return rds.region.CreateDBInstanceAccount(rds.Id, conf.Name, conf.Password)
   600  }
   601  
   602  func (region *SRegion) CreateDBInstanceAccount(instanceId, account, password string) error {
   603  	params := map[string]string{
   604  		"name":     account,
   605  		"password": password,
   606  	}
   607  	_, err := region.ecsClient.DBInstance.CreateInContextWithSpec(nil, fmt.Sprintf("%s/db_user", instanceId), jsonutils.Marshal(params), "")
   608  	return err
   609  }
   610  
   611  func (rds *SDBInstance) CreateDatabase(conf *cloudprovider.SDBInstanceDatabaseCreateConfig) error {
   612  	return rds.region.CreateDBInstanceDatabase(rds.Id, conf.Name, conf.CharacterSet)
   613  }
   614  
   615  func (region *SRegion) CreateDBInstanceDatabase(instanceId, database, characterSet string) error {
   616  	params := map[string]string{
   617  		"name":          database,
   618  		"character_set": characterSet,
   619  	}
   620  	_, err := region.ecsClient.DBInstance.CreateInContextWithSpec(nil, fmt.Sprintf("%s/database", instanceId), jsonutils.Marshal(params), "")
   621  	return err
   622  }
   623  
   624  func (rds *SDBInstance) ChangeConfig(cxt context.Context, desc *cloudprovider.SManagedDBInstanceChangeConfig) error {
   625  	return rds.region.ChangeDBInstanceConfig(rds.Id, desc.InstanceType, desc.DiskSizeGB)
   626  }
   627  
   628  func (region *SRegion) ChangeDBInstanceConfig(instanceId string, instanceType string, diskSizeGb int) error {
   629  	instance, err := region.GetIDBInstanceById(instanceId)
   630  	if err != nil {
   631  		return errors.Wrapf(err, "region.GetIDBInstanceById(%s)", instanceId)
   632  	}
   633  
   634  	if len(instanceType) > 0 {
   635  		params := map[string]map[string]string{
   636  			"resize_flavor": map[string]string{
   637  				"spec_code": instanceType,
   638  			},
   639  		}
   640  		_, err := region.ecsClient.DBInstance.PerformAction("action", instanceId, jsonutils.Marshal(params))
   641  		if err != nil {
   642  			return errors.Wrap(err, "resize_flavor")
   643  		}
   644  		cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
   645  	}
   646  	if diskSizeGb > 0 {
   647  		params := map[string]map[string]int{
   648  			"enlarge_volume": map[string]int{
   649  				"size": diskSizeGb,
   650  			},
   651  		}
   652  		_, err := region.ecsClient.DBInstance.PerformAction("action", instanceId, jsonutils.Marshal(params))
   653  		if err != nil {
   654  			return errors.Wrap(err, "enlarge_volume")
   655  		}
   656  		cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
   657  	}
   658  	return nil
   659  }
   660  
   661  func (rds *SDBInstance) RecoveryFromBackup(conf *cloudprovider.SDBInstanceRecoveryConfig) error {
   662  	if len(conf.OriginDBInstanceExternalId) == 0 {
   663  		conf.OriginDBInstanceExternalId = rds.Id
   664  	}
   665  	return rds.region.RecoveryDBInstanceFromBackup(rds.Id, conf.OriginDBInstanceExternalId, conf.BackupId, conf.Databases)
   666  }
   667  
   668  func (region *SRegion) RecoveryDBInstanceFromBackup(target, origin string, backupId string, databases map[string]string) error {
   669  	source := map[string]interface{}{
   670  		"type":      "backup",
   671  		"backup_id": backupId,
   672  	}
   673  	if len(origin) > 0 {
   674  		source["instance_id"] = origin
   675  	}
   676  	if len(databases) > 0 {
   677  		source["database_name"] = databases
   678  	}
   679  	params := map[string]interface{}{
   680  		"source": source,
   681  		"target": map[string]string{
   682  			"instance_id": target,
   683  		},
   684  	}
   685  	_, err := region.ecsClient.DBInstance.PerformAction("", "recovery", jsonutils.Marshal(params))
   686  	if err != nil {
   687  		return errors.Wrap(err, "dbinstance.recovery")
   688  	}
   689  	return nil
   690  }
   691  
   692  func (rds *SDBInstance) Renew(bc billing.SBillingCycle) error {
   693  	return cloudprovider.ErrNotSupported
   694  }