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