yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/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 hcs
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"time"
    21  
    22  	"yunion.io/x/jsonutils"
    23  	"yunion.io/x/log"
    24  	"yunion.io/x/pkg/errors"
    25  
    26  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    27  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    28  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    29  	"yunion.io/x/cloudmux/pkg/multicloud"
    30  	"yunion.io/x/cloudmux/pkg/multicloud/huawei"
    31  	"yunion.io/x/onecloud/pkg/util/billing"
    32  	"yunion.io/x/onecloud/pkg/util/httputils"
    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  	dbinstances := []SDBInstance{}
   104  	err := region.rdsList("instances", nil, nil)
   105  	return dbinstances, err
   106  }
   107  
   108  func (region *SRegion) GetDBInstance(instanceId string) (*SDBInstance, error) {
   109  	if len(instanceId) == 0 {
   110  		return nil, cloudprovider.ErrNotFound
   111  	}
   112  	instance := SDBInstance{region: region}
   113  	res := &SDBInstance{}
   114  	err := region.rdsGet(fmt.Sprintf("instance/%s", instanceId), res)
   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.client.rdsDelete(region.Id, instanceId)
   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.client.request(httputils.POST, region.client._url("instance", "v3", region.Id, "instance"), nil, 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  
   512  			job, err := region.client.request(httputils.POST, region.client._url(fmt.Sprintf("instance/%s", jobId), "v3", region.Id, "instance"), nil, params)
   513  			if err != nil {
   514  				return false, nil
   515  			}
   516  			status, _ := job.GetString("status")
   517  			process, _ := job.GetString("process")
   518  			log.Debugf("create dbinstance job %s status: %s process: %s", jobId, status, process)
   519  			if status == "Completed" {
   520  				return true, nil
   521  			}
   522  			if status == "Failed" {
   523  				return false, fmt.Errorf("create failed")
   524  			}
   525  			return false, nil
   526  		})
   527  	}
   528  	return instance, err
   529  }
   530  
   531  func (rds *SDBInstance) Reboot() error {
   532  	// return rds.region.RebootDBInstance(rds.Id)
   533  	return nil
   534  }
   535  
   536  func (rds *SDBInstance) OpenPublicConnection() error {
   537  	return fmt.Errorf("Huawei current not support this operation")
   538  	//return rds.region.PublicConnectionAction(rds.Id, "openRC")
   539  }
   540  
   541  func (rds *SDBInstance) ClosePublicConnection() error {
   542  	return fmt.Errorf("Huawei current not support this operation")
   543  	//return rds.region.PublicConnectionAction(rds.Id, "closeRC")
   544  }
   545  
   546  func (region *SRegion) PublicConnectionAction(instanceId string, action string) error {
   547  	return cloudprovider.ErrNotImplemented
   548  	/*resp, err := region.ecsClient.DBInstance.PerformAction2(action, instanceId, nil, "")
   549  	if err != nil {
   550  		return errors.Wrapf(err, "rds.%s", action)
   551  	}
   552  
   553  	if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
   554  		err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
   555  			job := struct {
   556  				Status  string
   557  				Process string
   558  			}{}
   559  			query := url.Values{}
   560  			query.Add("id", jobId)
   561  			err := region.rdsJobGet("jobs", query, job)
   562  			if err != nil {
   563  				log.Errorf("failed to get job %s info error: %v", jobId, err)
   564  				return false
   565  			}
   566  			if job.Status == "Completed" {
   567  				return true
   568  			}
   569  			log.Debugf("%s dbinstance job %s status: %s process: %s", action, jobId, job.Status, job.Process)
   570  			return false
   571  		})
   572  	}
   573  
   574  	return nil
   575  	*/
   576  }
   577  
   578  func (region *SRegion) RebootDBInstance(instanceId string) error {
   579  	return cloudprovider.ErrNotImplemented
   580  	/*params := jsonutils.Marshal(map[string]interface{}{
   581  		"restart": map[string]string{},
   582  	})
   583  	resp, err := region.ecsClient.DBInstance.PerformAction2("action", instanceId, params, "")
   584  	if err != nil {
   585  		return err
   586  	}
   587  	if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
   588  		err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
   589  			job := struct {
   590  				Status  string
   591  				Process string
   592  			}{}
   593  			query := url.Values{}
   594  			query.Add("id", jobId)
   595  			err := region.rdsJobGet("jobs", query, job)
   596  			if err != nil {
   597  				log.Errorf("failed to get job %s info error: %v", jobId, err)
   598  				return false
   599  			}
   600  			if job.Status == "Completed" {
   601  				return true
   602  			}
   603  			log.Debugf("%s dbinstance job %s status: %s process: %s", action, jobId, job.Status, job.Process)
   604  			return false
   605  		})
   606  	}
   607  	return err*/
   608  }
   609  
   610  func (rds *SDBInstance) CreateAccount(conf *cloudprovider.SDBInstanceAccountCreateConfig) error {
   611  	return rds.region.CreateDBInstanceAccount(rds.Id, conf.Name, conf.Password)
   612  }
   613  
   614  func (region *SRegion) CreateDBInstanceAccount(instanceId, account, password string) error {
   615  	params := map[string]interface{}{
   616  		"name":     account,
   617  		"password": password,
   618  	}
   619  
   620  	err := region.rdsCreate(fmt.Sprintf("instances/%s/db_user", instanceId), params, nil)
   621  	return err
   622  }
   623  
   624  func (rds *SDBInstance) CreateDatabase(conf *cloudprovider.SDBInstanceDatabaseCreateConfig) error {
   625  	return rds.region.CreateDBInstanceDatabase(rds.Id, conf.Name, conf.CharacterSet)
   626  }
   627  
   628  func (region *SRegion) CreateDBInstanceDatabase(instanceId, database, characterSet string) error {
   629  	params := map[string]interface{}{
   630  		"name":          database,
   631  		"character_set": characterSet,
   632  	}
   633  	err := region.rdsCreate(fmt.Sprintf("instances/%s/database", instanceId), params, nil)
   634  	return err
   635  }
   636  
   637  func (rds *SDBInstance) ChangeConfig(cxt context.Context, desc *cloudprovider.SManagedDBInstanceChangeConfig) error {
   638  	return rds.region.ChangeDBInstanceConfig(rds.Id, desc.InstanceType, desc.DiskSizeGB)
   639  }
   640  
   641  func (region *SRegion) ChangeDBInstanceConfig(instanceId string, instanceType string, diskSizeGb int) error {
   642  	instance, err := region.GetIDBInstanceById(instanceId)
   643  	if err != nil {
   644  		return errors.Wrapf(err, "region.GetIDBInstanceById(%s)", instanceId)
   645  	}
   646  
   647  	if len(instanceType) > 0 {
   648  		params := map[string]interface{}{
   649  			"resize_flavor": map[string]string{
   650  				"spec_code": instanceType,
   651  			},
   652  		}
   653  		err := region.rdsPerform(fmt.Sprintf("instances/%s", instanceId), "action", params, nil)
   654  		if err != nil {
   655  			return errors.Wrap(err, "resize_flavor")
   656  		}
   657  		cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
   658  	}
   659  	if diskSizeGb > 0 {
   660  		params := map[string]interface{}{
   661  			"enlarge_volume": map[string]int{
   662  				"size": diskSizeGb,
   663  			},
   664  		}
   665  		err := region.rdsPerform(fmt.Sprintf("instances/%s", instanceId), "action", params, nil)
   666  		if err != nil {
   667  			return errors.Wrap(err, "enlarge_volume")
   668  		}
   669  		cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
   670  	}
   671  	return nil
   672  }
   673  
   674  func (rds *SDBInstance) RecoveryFromBackup(conf *cloudprovider.SDBInstanceRecoveryConfig) error {
   675  	if len(conf.OriginDBInstanceExternalId) == 0 {
   676  		conf.OriginDBInstanceExternalId = rds.Id
   677  	}
   678  	return rds.region.RecoveryDBInstanceFromBackup(rds.Id, conf.OriginDBInstanceExternalId, conf.BackupId, conf.Databases)
   679  }
   680  
   681  func (region *SRegion) RecoveryDBInstanceFromBackup(target, origin string, backupId string, databases map[string]string) error {
   682  	source := map[string]interface{}{
   683  		"type":      "backup",
   684  		"backup_id": backupId,
   685  	}
   686  	if len(origin) > 0 {
   687  		source["instance_id"] = origin
   688  	}
   689  	if len(databases) > 0 {
   690  		source["database_name"] = databases
   691  	}
   692  	params := map[string]interface{}{
   693  		"source": source,
   694  		"target": map[string]string{
   695  			"instance_id": target,
   696  		},
   697  	}
   698  	err := region.rdsPerform("instance", "recovery", params, nil)
   699  	if err != nil {
   700  		return errors.Wrap(err, "dbinstance.recovery")
   701  	}
   702  	return nil
   703  }
   704  
   705  func (rds *SDBInstance) Renew(bc billing.SBillingCycle) error {
   706  	return cloudprovider.ErrNotSupported
   707  }