yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/apsara/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 apsara
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strings"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    25  	"yunion.io/x/pkg/errors"
    26  	"yunion.io/x/pkg/utils"
    27  
    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  	"yunion.io/x/onecloud/pkg/util/rand"
    33  )
    34  
    35  type SReadOnlyDBInstanceIds struct {
    36  	ReadOnlyDBInstanceId []string
    37  }
    38  
    39  type SDBInstanceId struct {
    40  	DBInstanceId []string
    41  }
    42  
    43  type SDBInstanceExtra struct {
    44  	DBInstanceId SDBInstanceId
    45  }
    46  
    47  type SDBInstance struct {
    48  	multicloud.SDBInstanceBase
    49  	ApsaraTags
    50  
    51  	netInfo []SDBInstanceNetwork
    52  
    53  	region *SRegion
    54  
    55  	AccountMaxQuantity        int
    56  	AccountType               string
    57  	CanTempUpgrade            bool
    58  	Category                  string
    59  	AvailabilityValue         string
    60  	DBInstanceDescription     string
    61  	DBInstanceId              string
    62  	ConnectionMode            string
    63  	ConnectionString          string
    64  	CurrentKernelVersion      string
    65  	DBInstanceCPU             int
    66  	CreateTime                time.Time
    67  	DBInstanceClass           string
    68  	DBInstanceClassType       string
    69  	DBInstanceNetType         string
    70  	DBInstanceStatus          string
    71  	DBInstanceType            string
    72  	DBInstanceDiskUsed        int64
    73  	DBInstanceStorage         int
    74  	DBInstanceStorageType     string
    75  	MasterInstanceId          string
    76  	DBInstanceMemory          int
    77  	DBMaxQuantity             int
    78  	IPType                    string
    79  	LatestKernelVersion       string
    80  	DispenseMode              string
    81  	Engine                    string
    82  	EngineVersion             string
    83  	ExpireTime                time.Time
    84  	InstanceNetworkType       string
    85  	LockMode                  string
    86  	LockReason                string
    87  	MutriORsignle             bool
    88  	MaintainTime              string
    89  	MaxConnections            int
    90  	MaxIOPS                   int
    91  	Port                      int
    92  	PayType                   TChargeType
    93  	ReadOnlyDBInstanceIds     SReadOnlyDBInstanceIds
    94  	RegionId                  string
    95  	VSwitchId                 string
    96  	VpcCloudInstanceId        string
    97  	VpcId                     string
    98  	ZoneId                    string
    99  	Extra                     SDBInstanceExtra
   100  	SecurityIPList            string
   101  	SecurityIPMode            string
   102  	SupportCreateSuperAccount string
   103  	SupportUpgradeAccountType string
   104  	TempUpgradeTimeEnd        time.Time
   105  	TempUpgradeTimeStart      time.Time
   106  	DepartmentInfo
   107  }
   108  
   109  func (rds *SDBInstance) GetName() string {
   110  	if len(rds.DBInstanceDescription) > 0 {
   111  		return rds.DBInstanceDescription
   112  	}
   113  	return rds.DBInstanceId
   114  }
   115  
   116  func (rds *SDBInstance) GetId() string {
   117  	return rds.DBInstanceId
   118  }
   119  
   120  func (rds *SDBInstance) GetGlobalId() string {
   121  	return rds.GetId()
   122  }
   123  
   124  // Creating	创建中
   125  // Running	使用中
   126  // Deleting	删除中
   127  // Rebooting	重启中
   128  // DBInstanceClassChanging	升降级中
   129  // TRANSING	迁移中
   130  // EngineVersionUpgrading	迁移版本中
   131  // TransingToOthers	迁移数据到其他RDS中
   132  // GuardDBInstanceCreating	生产灾备实例中
   133  // Restoring	备份恢复中
   134  // Importing	数据导入中
   135  // ImportingFromOthers	从其他RDS实例导入数据中
   136  // DBInstanceNetTypeChanging	内外网切换中
   137  // GuardSwitching	容灾切换中
   138  // INS_CLONING	实例克隆中
   139  func (rds *SDBInstance) GetStatus() string {
   140  	switch rds.DBInstanceStatus {
   141  	case "Creating", "GuardDBInstanceCreating", "DBInstanceNetTypeChanging", "GuardSwitching", "NET_CREATING", "NET_DELETING":
   142  		return api.DBINSTANCE_DEPLOYING
   143  	case "DBInstanceClassChanging":
   144  		return api.DBINSTANCE_CHANGE_CONFIG
   145  	case "Running":
   146  		return api.DBINSTANCE_RUNNING
   147  	case "Deleting":
   148  		return api.DBINSTANCE_DELETING
   149  	case "Rebooting":
   150  		return api.DBINSTANCE_REBOOTING
   151  	case "TRANSING", "EngineVersionUpgrading", "TransingToOthers":
   152  		return api.DBINSTANCE_MIGRATING
   153  	case "Restoring":
   154  		return api.DBINSTANCE_RESTORING
   155  	case "Importing", "ImportingFromOthers":
   156  		return api.DBINSTANCE_IMPORTING
   157  	case "INS_CLONING":
   158  		return api.DBINSTANCE_CLONING
   159  	default:
   160  		log.Errorf("Unknown dbinstance status %s", rds.DBInstanceStatus)
   161  		return api.DBINSTANCE_UNKNOWN
   162  	}
   163  }
   164  
   165  func (rds *SDBInstance) GetBillingType() string {
   166  	return convertChargeType(rds.PayType)
   167  }
   168  
   169  func (rds *SDBInstance) GetExpiredAt() time.Time {
   170  	return rds.ExpireTime
   171  }
   172  
   173  func (rds *SDBInstance) GetCreatedAt() time.Time {
   174  	return rds.CreateTime
   175  }
   176  
   177  func (rds *SDBInstance) GetStorageType() string {
   178  	return rds.DBInstanceStorageType
   179  }
   180  
   181  func (rds *SDBInstance) GetIops() int {
   182  	return rds.MaxIOPS
   183  }
   184  
   185  func (rds *SDBInstance) GetEngine() string {
   186  	switch rds.Engine {
   187  	case "MySQL":
   188  		return api.DBINSTANCE_TYPE_MYSQL
   189  	case "SQLServer":
   190  		return api.DBINSTANCE_TYPE_SQLSERVER
   191  	case "PostgreSQL":
   192  		return api.DBINSTANCE_TYPE_POSTGRESQL
   193  	case "PPAS":
   194  		return api.DBINSTANCE_TYPE_PPAS
   195  	case "MariaDB":
   196  		return api.DBINSTANCE_TYPE_MARIADB
   197  	}
   198  	return rds.Engine
   199  }
   200  
   201  func (rds *SDBInstance) GetEngineVersion() string {
   202  	return rds.EngineVersion
   203  }
   204  
   205  func (rds *SDBInstance) GetInstanceType() string {
   206  	return rds.DBInstanceClass
   207  }
   208  
   209  func (rds *SDBInstance) GetCategory() string {
   210  	switch rds.Category {
   211  	case "Basic":
   212  		return api.ALIYUN_DBINSTANCE_CATEGORY_BASIC
   213  	case "HighAvailability":
   214  		return api.ALIYUN_DBINSTANCE_CATEGORY_HA
   215  	case "AlwaysOn":
   216  		return api.ALIYUN_DBINSTANCE_CATEGORY_ALWAYSON
   217  	case "Finance":
   218  		return api.ALIYUN_DBINSTANCE_CATEGORY_FINANCE
   219  	}
   220  	return rds.Category
   221  }
   222  
   223  func (rds *SDBInstance) GetVcpuCount() int {
   224  	if rds.DBInstanceCPU == 0 {
   225  		rds.Refresh()
   226  	}
   227  	return rds.DBInstanceCPU
   228  }
   229  
   230  func (rds *SDBInstance) GetVmemSizeMB() int {
   231  	if rds.DBInstanceMemory == 0 {
   232  		rds.Refresh()
   233  	}
   234  	return rds.DBInstanceMemory
   235  }
   236  
   237  func (rds *SDBInstance) GetDiskSizeGB() int {
   238  	if rds.DBInstanceStorage == 0 {
   239  		rds.Refresh()
   240  	}
   241  	return rds.DBInstanceStorage
   242  }
   243  
   244  func (rds *SDBInstance) GetDiskSizeUsedMB() int {
   245  	if rds.DBInstanceDiskUsed == 0 {
   246  		rds.Refresh()
   247  	}
   248  	return int(rds.DBInstanceDiskUsed / 1024 / 1024)
   249  }
   250  
   251  func (rds *SDBInstance) GetPort() int {
   252  	if rds.Port == 0 {
   253  		rds.Refresh()
   254  	}
   255  	return rds.Port
   256  }
   257  
   258  func (rds *SDBInstance) GetMaintainTime() string {
   259  	return rds.MaintainTime
   260  }
   261  
   262  func (rds *SDBInstance) GetIVpcId() string {
   263  	return rds.VpcId
   264  }
   265  
   266  func (rds *SDBInstance) Refresh() error {
   267  	instance, err := rds.region.GetDBInstanceDetail(rds.DBInstanceId)
   268  	if err != nil {
   269  		return err
   270  	}
   271  	return jsonutils.Update(rds, instance)
   272  }
   273  
   274  func (rds *SDBInstance) getZoneId(index int) string {
   275  	zoneId := rds.getZone(index)
   276  	if len(zoneId) > 0 {
   277  		zone, err := rds.region.getZoneById(zoneId)
   278  		if err != nil {
   279  			log.Errorf("failed to found zone %s for rds %s", zoneId, rds.GetName())
   280  			return ""
   281  		}
   282  		return zone.GetGlobalId()
   283  	}
   284  	return ""
   285  }
   286  
   287  func (rds *SDBInstance) GetZone1Id() string {
   288  	return rds.getZoneId(1)
   289  }
   290  
   291  func (rds *SDBInstance) GetZone2Id() string {
   292  	return rds.getZoneId(2)
   293  }
   294  
   295  func (rds *SDBInstance) GetZone3Id() string {
   296  	return rds.getZoneId(3)
   297  }
   298  
   299  func (rds *SDBInstance) getZone(index int) string {
   300  	zoneStr := strings.Replace(rds.ZoneId, ")", "", -1)
   301  	zoneInfo := strings.Split(zoneStr, ",")
   302  	if len(zoneInfo) < index {
   303  		return ""
   304  	}
   305  	zone := zoneInfo[index-1]
   306  	zoneCode := zone[len(zone)-1]
   307  	if strings.HasPrefix(rds.ZoneId, fmt.Sprintf("%s-", rds.RegionId)) {
   308  		return fmt.Sprintf("%s-%s", rds.RegionId, string(zoneCode))
   309  	}
   310  	return fmt.Sprintf("%s%s", rds.RegionId, string(zoneCode))
   311  }
   312  
   313  func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) {
   314  	netInfo, err := rds.region.GetDBInstanceNetInfo(rds.DBInstanceId)
   315  	if err != nil {
   316  		return nil, errors.Wrapf(err, "GetDBInstanceNetInfo")
   317  	}
   318  	networks := []cloudprovider.SDBInstanceNetwork{}
   319  	for _, net := range netInfo {
   320  		if net.IPType == "Private" {
   321  			network := cloudprovider.SDBInstanceNetwork{}
   322  			network.IP = net.IPAddress
   323  			network.NetworkId = net.VSwitchId
   324  			networks = append(networks, network)
   325  		}
   326  	}
   327  	return networks, nil
   328  }
   329  
   330  func (rds *SDBInstance) fetchNetInfo() error {
   331  	if len(rds.netInfo) > 0 {
   332  		return nil
   333  	}
   334  	netInfo, err := rds.region.GetDBInstanceNetInfo(rds.DBInstanceId)
   335  	if err != nil {
   336  		return errors.Wrap(err, "GetDBInstanceNetInfo")
   337  	}
   338  	rds.netInfo = netInfo
   339  	return nil
   340  }
   341  
   342  func (rds *SDBInstance) GetInternalConnectionStr() string {
   343  	err := rds.fetchNetInfo()
   344  	if err != nil {
   345  		log.Errorf("failed to fetch netInfo error: %v", err)
   346  		return ""
   347  	}
   348  
   349  	for _, net := range rds.netInfo {
   350  		if net.IPType != "Public" {
   351  			return net.ConnectionString
   352  		}
   353  	}
   354  	return ""
   355  }
   356  
   357  func (rds *SDBInstance) GetConnectionStr() string {
   358  	err := rds.fetchNetInfo()
   359  	if err != nil {
   360  		log.Errorf("failed to fetch netInfo error: %v", err)
   361  		return ""
   362  	}
   363  
   364  	for _, net := range rds.netInfo {
   365  		if net.IPType == "Public" {
   366  			return net.ConnectionString
   367  		}
   368  	}
   369  	return ""
   370  }
   371  
   372  func (region *SRegion) GetDBInstances(ids []string, offset int, limit int) ([]SDBInstance, int, error) {
   373  	if limit > 50 || limit <= 0 {
   374  		limit = 50
   375  	}
   376  	params := make(map[string]string)
   377  	params["RegionId"] = region.RegionId
   378  	params["PageSize"] = fmt.Sprintf("%d", limit)
   379  	params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1)
   380  
   381  	body, err := region.rdsRequest("DescribeDBInstances", params)
   382  	if err != nil {
   383  		return nil, 0, errors.Wrapf(err, "GetDBInstances")
   384  	}
   385  	instances := []SDBInstance{}
   386  	err = body.Unmarshal(&instances, "Items", "DBInstance")
   387  	if err != nil {
   388  		return nil, 0, errors.Wrapf(err, "GetDBInstances.Unmarshal")
   389  	}
   390  	total, _ := body.Int("TotalRecordCount")
   391  	return instances, int(total), nil
   392  }
   393  
   394  func (region *SRegion) GetIDBInstanceById(instanceId string) (cloudprovider.ICloudDBInstance, error) {
   395  	rds, err := region.GetDBInstanceDetail(instanceId)
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  	rds.region = region
   400  	return rds, nil
   401  }
   402  
   403  func (region *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) {
   404  	instances := []SDBInstance{}
   405  	for {
   406  		part, total, err := region.GetDBInstances([]string{}, len(instances), 50)
   407  		if err != nil {
   408  			return nil, err
   409  		}
   410  		instances = append(instances, part...)
   411  		if len(instances) >= total {
   412  			break
   413  		}
   414  	}
   415  	idbinstances := []cloudprovider.ICloudDBInstance{}
   416  	for i := 0; i < len(instances); i++ {
   417  		instances[i].region = region
   418  		idbinstances = append(idbinstances, &instances[i])
   419  	}
   420  	return idbinstances, nil
   421  }
   422  
   423  func (region *SRegion) GetDBInstanceDetail(instanceId string) (*SDBInstance, error) {
   424  	if len(instanceId) == 0 {
   425  		return nil, cloudprovider.ErrNotFound
   426  	}
   427  	params := map[string]string{}
   428  	params["RegionId"] = region.RegionId
   429  	params["DBInstanceId"] = instanceId
   430  	body, err := region.rdsRequest("DescribeDBInstanceAttribute", params)
   431  	if err != nil {
   432  		return nil, errors.Wrapf(err, "GetDBInstanceDetail")
   433  	}
   434  	instances := []SDBInstance{}
   435  	err = body.Unmarshal(&instances, "Items", "DBInstanceAttribute")
   436  	if err != nil {
   437  		return nil, errors.Wrapf(err, "GetDBInstanceDetail.Unmarshal")
   438  	}
   439  	if len(instances) == 1 {
   440  		instances[0].region = region
   441  		return &instances[0], nil
   442  	}
   443  	if len(instances) == 0 {
   444  		return nil, cloudprovider.ErrNotFound
   445  	}
   446  	return nil, cloudprovider.ErrDuplicateId
   447  }
   448  
   449  func (region *SRegion) DeleteDBInstance(instanceId string) error {
   450  	params := map[string]string{}
   451  	params["RegionId"] = region.RegionId
   452  	params["DBInstanceId"] = instanceId
   453  	_, err := region.rdsRequest("DeleteDBInstance", params)
   454  	return err
   455  }
   456  
   457  type SDBInstanceWeight struct {
   458  }
   459  
   460  type SDBInstanceWeights struct {
   461  	DBInstanceWeight []SDBInstanceWeight
   462  }
   463  
   464  type SsecurityIPGroup struct {
   465  }
   466  
   467  type SSecurityIPGroups struct {
   468  	securityIPGroup []SsecurityIPGroup
   469  }
   470  
   471  type SDBInstanceNetwork struct {
   472  	ConnectionString     string
   473  	ConnectionStringType string
   474  	DBInstanceWeights    SDBInstanceWeights
   475  	IPAddress            string
   476  	IPType               string
   477  	Port                 int
   478  	SecurityIPGroups     SSecurityIPGroups
   479  	Upgradeable          string
   480  	VPCId                string
   481  	VSwitchId            string
   482  }
   483  
   484  func (network *SDBInstanceNetwork) GetGlobalId() string {
   485  	return network.IPAddress
   486  }
   487  
   488  func (network *SDBInstanceNetwork) GetINetworkId() string {
   489  	return network.VSwitchId
   490  }
   491  
   492  func (network *SDBInstanceNetwork) GetIP() string {
   493  	return network.IPAddress
   494  }
   495  
   496  func (region *SRegion) GetDBInstanceNetInfo(instanceId string) ([]SDBInstanceNetwork, error) {
   497  	params := map[string]string{}
   498  	params["RegionId"] = region.RegionId
   499  	params["DBInstanceId"] = instanceId
   500  	body, err := region.rdsRequest("DescribeDBInstanceNetInfo", params)
   501  	if err != nil {
   502  		return nil, errors.Wrapf(err, "GetDBInstanceNetwork")
   503  	}
   504  	networks := []SDBInstanceNetwork{}
   505  	err = body.Unmarshal(&networks, "DBInstanceNetInfos", "DBInstanceNetInfo")
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  	return networks, nil
   510  }
   511  
   512  func (rds *SDBInstance) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) {
   513  	parameters, err := rds.region.GetDBInstanceParameters(rds.DBInstanceId)
   514  	if err != nil {
   515  		return nil, err
   516  	}
   517  	iparameters := []cloudprovider.ICloudDBInstanceParameter{}
   518  	for i := 0; i < len(parameters); i++ {
   519  		iparameters = append(iparameters, &parameters[i])
   520  	}
   521  	return iparameters, nil
   522  }
   523  
   524  func (region *SRegion) GetIDBInstanceBackupById(backupId string) (cloudprovider.ICloudDBInstanceBackup, error) {
   525  	backups, err := region.GetIDBInstanceBackups()
   526  	if err != nil {
   527  		return nil, errors.Wrap(err, "region.GetIDBInstanceBackups")
   528  	}
   529  	for _, backup := range backups {
   530  		if backup.GetGlobalId() == backupId {
   531  			return backup, nil
   532  		}
   533  	}
   534  	return nil, cloudprovider.ErrNotFound
   535  }
   536  
   537  func (rds *SDBInstance) Reboot() error {
   538  	return rds.region.RebootDBInstance(rds.DBInstanceId)
   539  }
   540  
   541  func (rds *SDBInstance) Delete() error {
   542  	return rds.region.DeleteDBInstance(rds.DBInstanceId)
   543  }
   544  
   545  func (region *SRegion) RebootDBInstance(instanceId string) error {
   546  	params := map[string]string{}
   547  	params["RegionId"] = region.RegionId
   548  	params["DBInstanceId"] = instanceId
   549  	_, err := region.rdsRequest("RestartDBInstance", params)
   550  	return err
   551  }
   552  
   553  func (rds *SDBInstance) GetIDBInstanceDatabases() ([]cloudprovider.ICloudDBInstanceDatabase, error) {
   554  	databases := []SDBInstanceDatabase{}
   555  	for {
   556  		parts, total, err := rds.region.GetDBInstanceDatabases(rds.DBInstanceId, "", len(databases), 500)
   557  		if err != nil {
   558  			return nil, err
   559  		}
   560  		databases = append(databases, parts...)
   561  		if len(databases) >= total {
   562  			break
   563  		}
   564  	}
   565  
   566  	idatabase := []cloudprovider.ICloudDBInstanceDatabase{}
   567  	for i := 0; i < len(databases); i++ {
   568  		databases[i].instance = rds
   569  		idatabase = append(idatabase, &databases[i])
   570  	}
   571  	return idatabase, nil
   572  }
   573  
   574  func (rds *SDBInstance) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) {
   575  	accounts := []SDBInstanceAccount{}
   576  	for {
   577  		parts, total, err := rds.region.GetDBInstanceAccounts(rds.DBInstanceId, len(accounts), 50)
   578  		if err != nil {
   579  			return nil, err
   580  		}
   581  		accounts = append(accounts, parts...)
   582  		if len(accounts) >= total {
   583  			break
   584  		}
   585  	}
   586  
   587  	iaccounts := []cloudprovider.ICloudDBInstanceAccount{}
   588  	for i := 0; i < len(accounts); i++ {
   589  		accounts[i].instance = rds
   590  		iaccounts = append(iaccounts, &accounts[i])
   591  	}
   592  	return iaccounts, nil
   593  }
   594  
   595  func (rds *SDBInstance) ChangeConfig(cxt context.Context, desc *cloudprovider.SManagedDBInstanceChangeConfig) error {
   596  	return rds.region.ChangeDBInstanceConfig(rds.DBInstanceId, string(rds.PayType), desc)
   597  }
   598  
   599  func (region *SRegion) ChangeDBInstanceConfig(instanceId, payType string, desc *cloudprovider.SManagedDBInstanceChangeConfig) error {
   600  	params := map[string]string{
   601  		"RegionId":          region.RegionId,
   602  		"DBInstanceId":      instanceId,
   603  		"PayType":           payType,
   604  		"DBInstanceClass":   desc.InstanceType,
   605  		"DBInstanceStorage": fmt.Sprintf("%d", desc.DiskSizeGB),
   606  	}
   607  
   608  	_, err := region.rdsRequest("ModifyDBInstanceSpec", params)
   609  	if err != nil {
   610  		return errors.Wrap(err, "region.rdsRequest.ModifyDBInstanceSpec")
   611  	}
   612  	return nil
   613  }
   614  
   615  func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) {
   616  	params := map[string]string{
   617  		"RegionId":              region.RegionId,
   618  		"Engine":                desc.Engine,
   619  		"EngineVersion":         desc.EngineVersion,
   620  		"DBInstanceStorage":     fmt.Sprintf("%d", desc.DiskSizeGB),
   621  		"DBInstanceNetType":     "Intranet",
   622  		"PayType":               "Postpaid",
   623  		"SecurityIPList":        "0.0.0.0/0",
   624  		"DBInstanceDescription": desc.Name,
   625  		"InstanceNetworkType":   "VPC",
   626  		"VPCId":                 desc.VpcId,
   627  		"VSwitchId":             desc.NetworkId,
   628  		"DBInstanceStorageType": desc.StorageType,
   629  		"DBInstanceClass":       desc.InstanceType,
   630  		"ZoneId":                desc.ZoneId,
   631  		"ClientToken":           utils.GenRequestId(20),
   632  	}
   633  	switch desc.Category {
   634  	case api.ALIYUN_DBINSTANCE_CATEGORY_HA:
   635  		params["Category"] = "HighAvailability"
   636  	case api.ALIYUN_DBINSTANCE_CATEGORY_BASIC:
   637  		params["Category"] = "Basic"
   638  	case api.ALIYUN_DBINSTANCE_CATEGORY_ALWAYSON:
   639  		params["Category"] = "AlwaysOn"
   640  	case api.ALIYUN_DBINSTANCE_CATEGORY_FINANCE:
   641  		params["Category"] = "Finance"
   642  	}
   643  
   644  	if len(desc.Address) > 0 {
   645  		params["PrivateIpAddress"] = desc.Address
   646  	}
   647  	if len(desc.ProjectId) > 0 {
   648  		params["ResourceGroupId"] = desc.ProjectId
   649  	}
   650  	if desc.BillingCycle != nil {
   651  		params["PayType"] = "Prepaid"
   652  		if desc.BillingCycle.GetMonths() > 0 {
   653  			params["Period"] = "Month"
   654  			params["UsedTime"] = fmt.Sprintf("%d", desc.BillingCycle.GetMonths())
   655  		} else {
   656  			params["Period"] = "Year"
   657  			params["UsedTime"] = fmt.Sprintf("%d", desc.BillingCycle.GetYears())
   658  		}
   659  		params["AutoRenew"] = "False"
   660  	}
   661  
   662  	action := "CreateDBInstance"
   663  	if len(desc.MasterInstanceId) > 0 {
   664  		action = "CreateReadOnlyDBInstance"
   665  		params["DBInstanceId"] = desc.MasterInstanceId
   666  	}
   667  
   668  	resp, err := region.rdsRequest(action, params)
   669  	if err != nil {
   670  		return nil, errors.Wrapf(err, "rdsRequest")
   671  	}
   672  	instanceId, err := resp.GetString("DBInstanceId")
   673  	if err != nil {
   674  		return nil, errors.Wrap(err, `resp.GetString("DBInstanceId")`)
   675  	}
   676  	region.SetResourceTags("rds", "INSTANCE", []string{instanceId}, desc.Tags, false)
   677  	return region.GetIDBInstanceById(instanceId)
   678  }
   679  
   680  func (rds *SDBInstance) GetMasterInstanceId() string {
   681  	if len(rds.MasterInstanceId) > 0 {
   682  		return rds.MasterInstanceId
   683  	}
   684  	rds.Refresh()
   685  	return rds.MasterInstanceId
   686  }
   687  
   688  func (region *SRegion) OpenPublicConnection(instanceId string) error {
   689  	rds, err := region.GetDBInstanceDetail(instanceId)
   690  	if err != nil {
   691  		return err
   692  	}
   693  	params := map[string]string{
   694  		"RegionId":               region.RegionId,
   695  		"ConnectionStringPrefix": rds.DBInstanceId + rand.String(3),
   696  		"DBInstanceId":           rds.DBInstanceId,
   697  		"Port":                   fmt.Sprintf("%d", rds.Port),
   698  	}
   699  	_, err = rds.region.rdsRequest("AllocateInstancePublicConnection", params)
   700  	if err != nil {
   701  		return errors.Wrap(err, "rdsRequest(AllocateInstancePublicConnection)")
   702  	}
   703  	return nil
   704  }
   705  
   706  func (rds *SDBInstance) OpenPublicConnection() error {
   707  	if url := rds.GetConnectionStr(); len(url) == 0 {
   708  		err := rds.region.OpenPublicConnection(rds.DBInstanceId)
   709  		if err != nil {
   710  			return err
   711  		}
   712  		rds.netInfo = []SDBInstanceNetwork{}
   713  	}
   714  	return nil
   715  }
   716  
   717  func (region *SRegion) ClosePublicConnection(instanceId string) error {
   718  	netInfo, err := region.GetDBInstanceNetInfo(instanceId)
   719  	if err != nil {
   720  		return errors.Wrap(err, "GetDBInstanceNetInfo")
   721  	}
   722  
   723  	for _, net := range netInfo {
   724  		if net.IPType == "Public" {
   725  			params := map[string]string{
   726  				"RegionId":                region.RegionId,
   727  				"CurrentConnectionString": net.ConnectionString,
   728  				"DBInstanceId":            instanceId,
   729  			}
   730  			_, err = region.rdsRequest("ReleaseInstancePublicConnection", params)
   731  			if err != nil {
   732  				return errors.Wrap(err, "rdsRequest(ReleaseInstancePublicConnection)")
   733  			}
   734  
   735  		}
   736  	}
   737  	return nil
   738  
   739  }
   740  
   741  func (rds *SDBInstance) ClosePublicConnection() error {
   742  	return rds.region.ClosePublicConnection(rds.DBInstanceId)
   743  }
   744  
   745  func (rds *SDBInstance) RecoveryFromBackup(conf *cloudprovider.SDBInstanceRecoveryConfig) error {
   746  	if len(conf.OriginDBInstanceExternalId) == 0 {
   747  		conf.OriginDBInstanceExternalId = rds.DBInstanceId
   748  	}
   749  	return rds.region.RecoveryDBInstanceFromBackup(conf.OriginDBInstanceExternalId, rds.DBInstanceId, conf.BackupId, conf.Databases)
   750  }
   751  
   752  func (region *SRegion) RecoveryDBInstanceFromBackup(srcId, destId string, backupId string, databases map[string]string) error {
   753  	params := map[string]string{
   754  		"RegionId":           region.RegionId,
   755  		"DBInstanceId":       srcId,
   756  		"TargetDBInstanceId": destId,
   757  		"BackupId":           backupId,
   758  		"DbNames":            jsonutils.Marshal(databases).String(),
   759  	}
   760  	_, err := region.rdsRequest("RecoveryDBInstance", params)
   761  	if err != nil {
   762  		return errors.Wrap(err, "rdsRequest.RecoveryDBInstance")
   763  	}
   764  	return nil
   765  }
   766  
   767  func (rds *SDBInstance) CreateDatabase(conf *cloudprovider.SDBInstanceDatabaseCreateConfig) error {
   768  	return rds.region.CreateDBInstanceDatabae(rds.DBInstanceId, conf.CharacterSet, conf.Name, conf.Description)
   769  }
   770  
   771  func (rds *SDBInstance) CreateAccount(conf *cloudprovider.SDBInstanceAccountCreateConfig) error {
   772  	return rds.region.CreateDBInstanceAccount(rds.DBInstanceId, conf.Name, conf.Password, conf.Description)
   773  }
   774  
   775  func (rds *SDBInstance) Renew(bc billing.SBillingCycle) error {
   776  	return rds.region.RenewInstance(rds.DBInstanceId, bc)
   777  }
   778  
   779  func (region *SRegion) RenewDBInstance(instanceId string, bc billing.SBillingCycle) error {
   780  	params := map[string]string{
   781  		"DBInstanceId": instanceId,
   782  		"Period":       fmt.Sprintf("%d", bc.GetMonths()),
   783  		"ClientToken":  utils.GenRequestId(20),
   784  	}
   785  	_, err := region.rdsRequest("RenewInstance", params)
   786  	return err
   787  }
   788  
   789  func (rds *SDBInstance) GetTags() (map[string]string, error) {
   790  	tags, err := rds.region.ListResourceTags("rds", "INSTANCE", []string{rds.GetId()})
   791  	if err != nil {
   792  		return nil, errors.Wrap(err, "rds.region.ListResourceTags")
   793  	}
   794  	if _, ok := tags[rds.GetId()]; !ok {
   795  		return map[string]string{}, nil
   796  	}
   797  	return *tags[rds.GetId()], nil
   798  }
   799  
   800  func (rds *SDBInstance) SetTags(tags map[string]string, replace bool) error {
   801  	return rds.region.SetResourceTags("rds", "INSTANCE", []string{rds.GetId()}, tags, replace)
   802  }