yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/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 aws
    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  
    27  	billing "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  )
    32  
    33  type SDBParameterGroup struct {
    34  	DBParameterGroupName string `xml:"DBParameterGroupName"`
    35  	ParameterApplyStatus string `xml:"ParameterApplyStatus"`
    36  }
    37  
    38  type SOptionGroupMembership struct {
    39  	OptionGroupName string `xml:"OptionGroupName"`
    40  	Status          string `xml:"Status"`
    41  }
    42  
    43  type SEndpoint struct {
    44  	HostedZoneId string `xml:"HostedZoneId"`
    45  	Address      string `xml:"Address"`
    46  	Port         int    `xml:"Port"`
    47  }
    48  
    49  type SSubnetAvailabilityZone struct {
    50  	Name string `xml:"Name"`
    51  }
    52  
    53  type SSubnet struct {
    54  	SubnetIdentifier       string                  `xml:"SubnetIdentifier"`
    55  	SubnetStatus           string                  `xml:"SubnetStatus"`
    56  	SubnetAvailabilityZone SSubnetAvailabilityZone `xml:"SubnetAvailabilityZone"`
    57  }
    58  
    59  type SDBSubnetGroup struct {
    60  	VpcId                    string    `xml:"VpcId"`
    61  	Subnets                  []SSubnet `xml:"Subnets>Subnet"`
    62  	SubnetGroupStatus        string    `xml:"SubnetGroupStatus"`
    63  	DBSubnetGroupDescription string    `xml:"DBSubnetGroupDescription"`
    64  	DBSubnetGroupName        string    `xml:"DBSubnetGroupName"`
    65  }
    66  
    67  type SVpcSecurityGroupMembership struct {
    68  	VpcSecurityGroupId string `xml:"VpcSecurityGroupId"`
    69  	Status             string `xml:"Status"`
    70  }
    71  
    72  type SVpcSecurityGroups struct {
    73  	VpcSecurityGroupMembership SVpcSecurityGroupMembership `xml:"VpcSecurityGroupMembership"`
    74  }
    75  
    76  type SDBInstance struct {
    77  	multicloud.SDBInstanceBase
    78  	AwsTags
    79  
    80  	region *SRegion
    81  
    82  	AllocatedStorage int `xml:"AllocatedStorage"`
    83  	//AssociatedRoles     string             `xml:"AssociatedRoles"`
    84  	DBParameterGroups   []SDBParameterGroup `xml:"DBParameterGroups>DBParameterGroup"`
    85  	AvailabilityZone    string              `xml:"AvailabilityZone"`
    86  	DBSecurityGroups    string              `xml:"DBSecurityGroups"`
    87  	EngineVersion       string              `xml:"EngineVersion"`
    88  	MasterUsername      string              `xml:"MasterUsername"`
    89  	InstanceCreateTime  time.Time           `xml:"InstanceCreateTime"`
    90  	DBInstanceClass     string              `xml:"DBInstanceClass"`
    91  	HttpEndpointEnabled bool                `xml:"HttpEndpointEnabled"`
    92  	//ReadReplicaDBInstanceIdentifiers string             `xml:"ReadReplicaDBInstanceIdentifiers"`
    93  	MonitoringInterval               int                      `xml:"MonitoringInterval"`
    94  	DBInstanceStatus                 string                   `xml:"DBInstanceStatus"`
    95  	BackupRetentionPeriod            int                      `xml:"BackupRetentionPeriod"`
    96  	OptionGroupMemberships           []SOptionGroupMembership `xml:"OptionGroupMemberships>OptionGroupMembership"`
    97  	CACertificateIdentifier          string                   `xml:"CACertificateIdentifier"`
    98  	DbInstancePort                   int                      `xml:"DbInstancePort"`
    99  	DbiResourceId                    string                   `xml:"DbiResourceId"`
   100  	PreferredBackupWindow            string                   `xml:"PreferredBackupWindow"`
   101  	DeletionProtection               bool                     `xml:"DeletionProtection"`
   102  	DBInstanceIdentifier             string                   `xml:"DBInstanceIdentifier"`
   103  	DBInstanceArn                    string                   `xml:"DBInstanceArn"`
   104  	Endpoint                         SEndpoint                `xml:"Endpoint"`
   105  	Engine                           string                   `xml:"Engine"`
   106  	PubliclyAccessible               bool                     `xml:"PubliclyAccessible"`
   107  	IAMDatabaseAuthenticationEnabled bool                     `xml:"IAMDatabaseAuthenticationEnabled"`
   108  	PerformanceInsightsEnabled       bool                     `xml:"PerformanceInsightsEnabled"`
   109  	DBName                           string                   `xml:"DBName"`
   110  	MultiAZ                          bool                     `xml:"MultiAZ"`
   111  	//DomainMemberships                string                  `xml:"DomainMemberships"`
   112  	StorageEncrypted           bool               `xml:"StorageEncrypted"`
   113  	DBSubnetGroup              SDBSubnetGroup     `xml:"DBSubnetGroup"`
   114  	VpcSecurityGroups          SVpcSecurityGroups `xml:"VpcSecurityGroups"`
   115  	LicenseModel               string             `xml:"LicenseModel"`
   116  	PreferredMaintenanceWindow string             `xml:"PreferredMaintenanceWindow"`
   117  	StorageType                string             `xml:"StorageType"`
   118  	AutoMinorVersionUpgrade    bool               `xml:"AutoMinorVersionUpgrade"`
   119  	CopyTagsToSnapshot         bool               `xml:"CopyTagsToSnapshot"`
   120  }
   121  
   122  type SDBInstances struct {
   123  	DBInstances []SDBInstance `xml:"DBInstances>DBInstance"`
   124  	Marker      string        `xml:"Marker"`
   125  }
   126  
   127  func (rds *SDBInstance) GetName() string {
   128  	return rds.DBInstanceIdentifier
   129  }
   130  
   131  func (rds *SDBInstance) GetId() string {
   132  	return rds.DbiResourceId
   133  }
   134  
   135  func (rds *SDBInstance) GetGlobalId() string {
   136  	return rds.GetId()
   137  }
   138  
   139  // https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/accessing-monitoring.html#Overview.DBInstance.Status
   140  func (rds *SDBInstance) GetStatus() string {
   141  	switch rds.DBInstanceStatus {
   142  	case "creating", "backing-up":
   143  		return api.DBINSTANCE_DEPLOYING
   144  	case "available":
   145  		return api.DBINSTANCE_RUNNING
   146  	case "deleting":
   147  		return api.DBINSTANCE_DELETING
   148  	case "rebooting":
   149  		return api.DBINSTANCE_REBOOTING
   150  	default:
   151  		log.Errorf("Unknown db instance status: %s", rds.DBInstanceStatus)
   152  		return api.DBINSTANCE_UNKNOWN
   153  	}
   154  }
   155  
   156  func (rds *SDBInstance) GetBillingType() string {
   157  	return billing.BILLING_TYPE_POSTPAID
   158  }
   159  
   160  func (rds *SDBInstance) GetExpiredAt() time.Time {
   161  	return time.Time{}
   162  }
   163  
   164  func (rds *SDBInstance) GetCreatedAt() time.Time {
   165  	return rds.InstanceCreateTime
   166  }
   167  
   168  func (rds *SDBInstance) Reboot() error {
   169  	return rds.region.RebootDBInstance(rds.DBInstanceIdentifier)
   170  }
   171  
   172  func (self *SDBInstance) GetCategory() string {
   173  	switch self.Engine {
   174  	case "aurora", "aurora-mysql":
   175  		return api.DBINSTANCE_TYPE_MYSQL
   176  	case "aurora-postgresql":
   177  		return api.DBINSTANCE_TYPE_POSTGRESQL
   178  	case "oracle-ee", "sqlserver-ee":
   179  		return api.AWS_DBINSTANCE_CATEGORY_ENTERPRISE_EDITION
   180  	case "oracle-se2":
   181  		return api.AWS_DBINSTANCE_CATEGORY_STANDARD_EDITION_TWO
   182  	case "sqlserver-se":
   183  		return api.AWS_DBINSTANCE_CATEGORY_STANDARD_EDITION
   184  	case "sqlserver-ex":
   185  		return api.AWS_DBINSTANCE_CATEGORY_EXPRESS_EDITION
   186  	case "sqlserver-web":
   187  		return api.AWS_DBINSTANCE_CATEGORY_WEB_EDITION
   188  	default:
   189  		if strings.HasPrefix(self.DBInstanceClass, "db.r") || strings.HasPrefix(self.DBInstanceClass, "db.x") || strings.HasPrefix(self.DBInstanceClass, "db.d") {
   190  			return api.AWS_DBINSTANCE_CATEGORY_MEMORY_OPTIMIZED
   191  		}
   192  		return api.AWS_DBINSTANCE_CATEGORY_GENERAL_PURPOSE
   193  	}
   194  }
   195  
   196  func (rds *SDBInstance) GetStorageType() string {
   197  	return rds.StorageType
   198  }
   199  
   200  func (rds *SDBInstance) GetEngine() string {
   201  	if strings.Contains(rds.Engine, "aurora") {
   202  		return api.DBINSTANCE_TYPE_AURORA
   203  	}
   204  	if strings.Contains(rds.Engine, "oracle") {
   205  		return api.DBINSTANCE_TYPE_ORACLE
   206  	}
   207  	if strings.Contains(rds.Engine, "sqlserver") {
   208  		return api.DBINSTANCE_TYPE_SQLSERVER
   209  	}
   210  	for k, v := range map[string]string{
   211  		"mariadb":  api.DBINSTANCE_TYPE_MARIADB,
   212  		"mysql":    api.DBINSTANCE_TYPE_MYSQL,
   213  		"postgres": api.DBINSTANCE_TYPE_POSTGRESQL,
   214  	} {
   215  		if rds.Engine == k {
   216  			return v
   217  		}
   218  	}
   219  	return rds.Engine
   220  }
   221  
   222  func (rds *SDBInstance) GetEngineVersion() string {
   223  	return rds.EngineVersion
   224  }
   225  
   226  func (rds *SDBInstance) GetInstanceType() string {
   227  	return rds.DBInstanceClass
   228  }
   229  
   230  func (rds *SDBInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedDBInstanceChangeConfig) error {
   231  	params := map[string]string{
   232  		"DBInstanceIdentifier": rds.DBInstanceIdentifier,
   233  		"ApplyImmediately":     "true",
   234  	}
   235  	if config.DiskSizeGB > 0 && rds.GetEngine() != api.DBINSTANCE_TYPE_AURORA {
   236  		params["AllocatedStorage"] = fmt.Sprintf("%d", config.DiskSizeGB)
   237  	}
   238  	if len(config.InstanceType) > 0 {
   239  		params["DBInstanceClass"] = config.InstanceType
   240  	}
   241  	return rds.region.rdsRequest("ModifyDBInstance", params, nil)
   242  }
   243  
   244  func (rds *SDBInstance) GetVcpuCount() int {
   245  	if spec, ok := DBInstanceSpecs[rds.DBInstanceClass]; ok {
   246  		return spec.VcpuCount
   247  	}
   248  	return 0
   249  }
   250  
   251  func (rds *SDBInstance) GetVmemSizeMB() int {
   252  	if spec, ok := DBInstanceSpecs[rds.DBInstanceClass]; ok {
   253  		return spec.VmemSizeMb
   254  	}
   255  	return 0
   256  }
   257  
   258  func (rds *SDBInstance) GetDiskSizeGB() int {
   259  	return rds.AllocatedStorage
   260  }
   261  
   262  func (rds *SDBInstance) GetPort() int {
   263  	return rds.Endpoint.Port
   264  }
   265  
   266  func (rds *SDBInstance) GetMaintainTime() string {
   267  	return rds.PreferredMaintenanceWindow
   268  }
   269  
   270  func (rds *SDBInstance) GetIVpcId() string {
   271  	return rds.DBSubnetGroup.VpcId
   272  }
   273  
   274  func (rds *SDBInstance) Refresh() error {
   275  	instance, err := rds.region.GetDBInstance(rds.DbiResourceId)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	rds.AwsTags = instance.AwsTags
   280  	return jsonutils.Update(rds, instance)
   281  }
   282  
   283  func (region *SRegion) GetDBInstance(instanceId string) (*SDBInstance, error) {
   284  	instances, _, err := region.GetDBInstances(instanceId, "")
   285  	if err != nil {
   286  		return nil, errors.Wrap(err, "GetDBInstances")
   287  	}
   288  
   289  	if len(instances) == 1 {
   290  		if instances[0].DbiResourceId == instanceId {
   291  			instances[0].region = region
   292  			return &instances[0], nil
   293  		}
   294  		return nil, cloudprovider.ErrNotFound
   295  	}
   296  
   297  	if len(instances) == 0 {
   298  		return nil, cloudprovider.ErrNotFound
   299  	}
   300  
   301  	return nil, cloudprovider.ErrDuplicateId
   302  }
   303  
   304  func (rds *SDBInstance) GetZone1Id() string {
   305  	if len(rds.AvailabilityZone) > 0 {
   306  		zone, err := rds.region.getZoneById(rds.AvailabilityZone)
   307  		if err != nil {
   308  			log.Errorf("rds.GetIZoneId %s error: %v", rds.DBInstanceIdentifier, err)
   309  			return ""
   310  		}
   311  		return zone.GetGlobalId()
   312  	}
   313  	return ""
   314  }
   315  
   316  func (rds *SDBInstance) GetZone2Id() string {
   317  	return ""
   318  }
   319  
   320  func (rds *SDBInstance) GetZone3Id() string {
   321  	return ""
   322  }
   323  
   324  func (rds *SDBInstance) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) {
   325  	accounts := []cloudprovider.ICloudDBInstanceAccount{}
   326  	if len(rds.MasterUsername) > 0 {
   327  		account := &SDBInstanceAccount{instance: rds, AccountName: rds.MasterUsername}
   328  		accounts = append(accounts, account)
   329  	}
   330  	return accounts, nil
   331  }
   332  
   333  func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) {
   334  	return []cloudprovider.SDBInstanceNetwork{}, nil
   335  }
   336  
   337  func (rds *SDBInstance) GetInternalConnectionStr() string {
   338  	return rds.Endpoint.Address
   339  }
   340  
   341  func (rds *SDBInstance) GetConnectionStr() string {
   342  	if rds.PubliclyAccessible {
   343  		return rds.Endpoint.Address
   344  	}
   345  	return ""
   346  }
   347  
   348  func (rds *SDBInstance) OpenPublicConnection() error {
   349  	params := map[string]string{
   350  		"DBInstanceIdentifier": rds.DBInstanceIdentifier,
   351  		"PubliclyAccessible":   "true",
   352  	}
   353  	return rds.region.rdsRequest("ModifyDBInstance", params, nil)
   354  }
   355  
   356  func (rds *SDBInstance) ClosePublicConnection() error {
   357  	params := map[string]string{
   358  		"DBInstanceIdentifier": rds.DBInstanceIdentifier,
   359  		"PubliclyAccessible":   "false",
   360  	}
   361  	return rds.region.rdsRequest("ModifyDBInstance", params, nil)
   362  }
   363  
   364  func (rds *SDBInstance) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) {
   365  	parameters, err := rds.region.GetDBInstanceParameters(rds.DBParameterGroups[0].DBParameterGroupName)
   366  	if err != nil {
   367  		return nil, errors.Wrap(err, "GetDBInstanceParameters")
   368  	}
   369  	iparams := []cloudprovider.ICloudDBInstanceParameter{}
   370  	for i := 0; i < len(parameters); i++ {
   371  		parameters[i].instance = rds
   372  		iparams = append(iparams, &parameters[i])
   373  	}
   374  	return iparams, nil
   375  }
   376  
   377  func (rds *SDBInstance) GetIDBInstanceDatabases() ([]cloudprovider.ICloudDBInstanceDatabase, error) {
   378  	idatabases := []cloudprovider.ICloudDBInstanceDatabase{}
   379  	if len(rds.DBName) > 0 {
   380  		database := &SDBInstanceDatabase{DBName: rds.DBName}
   381  		idatabases = append(idatabases, database)
   382  	}
   383  	return idatabases, nil
   384  }
   385  
   386  func (rds *SDBInstance) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) {
   387  	backups, err := rds.region.GetDBInstanceSnapshots(rds.DBInstanceIdentifier, "")
   388  	if err != nil {
   389  		return nil, err
   390  	}
   391  	ret := []cloudprovider.ICloudDBInstanceBackup{}
   392  	for i := range backups {
   393  		backups[i].region = rds.region
   394  		ret = append(ret, &backups[i])
   395  	}
   396  	return ret, nil
   397  }
   398  
   399  func (rds *SDBInstance) CreateIBackup(conf *cloudprovider.SDBInstanceBackupCreateConfig) (string, error) {
   400  	params := map[string]string{
   401  		"DBInstanceIdentifier": rds.DBInstanceIdentifier,
   402  		"DBSnapshotIdentifier": conf.Name,
   403  	}
   404  	ret := struct {
   405  		DBSnapshot SDBInstanceSnapshot `xml:"DBSnapshot"`
   406  	}{}
   407  	err := rds.region.rdsRequest("CreateDBSnapshot", params, &ret)
   408  	if err != nil {
   409  		return "", err
   410  	}
   411  	ret.DBSnapshot.region = rds.region
   412  	cloudprovider.WaitStatus(&ret.DBSnapshot, api.DBINSTANCE_BACKUP_READY, time.Second*10, time.Hour*2)
   413  	return ret.DBSnapshot.GetGlobalId(), nil
   414  }
   415  
   416  func (region *SRegion) GetDBInstances(instanceId, marker string) ([]SDBInstance, string, error) {
   417  	instances := SDBInstances{}
   418  	params := map[string]string{}
   419  	idx := 1
   420  	if len(instanceId) > 0 {
   421  		params[fmt.Sprintf("Filters.Filter.%d.Name", idx)] = "dbi-resource-id"
   422  		params[fmt.Sprintf("Filters.Filter.%d.Values.Value.1", idx)] = instanceId
   423  	}
   424  
   425  	if len(marker) > 0 {
   426  		params["Marker"] = marker
   427  	}
   428  
   429  	err := region.rdsRequest("DescribeDBInstances", params, &instances)
   430  	if err != nil {
   431  		return nil, "", errors.Wrap(err, "DescribeDBInstances")
   432  	}
   433  	return instances.DBInstances, instances.Marker, nil
   434  }
   435  
   436  func (region *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) {
   437  	idbinstances := []cloudprovider.ICloudDBInstance{}
   438  	instances, marker, err := region.GetDBInstances("", "")
   439  	if err != nil {
   440  		return nil, errors.Wrap(err, "GetDBInstances")
   441  	}
   442  	for i := 0; i < len(instances); i++ {
   443  		instances[i].region = region
   444  		idbinstances = append(idbinstances, &instances[i])
   445  	}
   446  	for len(marker) > 0 {
   447  		instances, marker, err = region.GetDBInstances("", marker)
   448  		if err != nil {
   449  			return nil, errors.Wrap(err, "GetDBInstances")
   450  		}
   451  		for i := 0; i < len(instances); i++ {
   452  			instances[i].region = region
   453  			idbinstances = append(idbinstances, &instances[i])
   454  		}
   455  	}
   456  	return idbinstances, nil
   457  }
   458  
   459  func (self *SRegion) GetIDBInstanceById(id string) (cloudprovider.ICloudDBInstance, error) {
   460  	instances, _, err := self.GetDBInstances(id, "")
   461  	if err != nil {
   462  		return nil, errors.Wrap(err, "GetDBInstances")
   463  	}
   464  
   465  	if len(instances) > 1 {
   466  		return nil, errors.Wrapf(cloudprovider.ErrDuplicateId, id)
   467  	}
   468  
   469  	if len(instances) == 0 {
   470  		return nil, errors.Wrapf(cloudprovider.ErrNotFound, id)
   471  	}
   472  
   473  	instances[0].region = self
   474  	return &instances[0], nil
   475  }
   476  
   477  func (self *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) {
   478  	params := map[string]string{
   479  		"DBInstanceClass":      desc.InstanceType,
   480  		"DBInstanceIdentifier": desc.Name,
   481  		"EngineVersion":        desc.EngineVersion,
   482  		"MasterUsername":       "admin",
   483  	}
   484  	if len(desc.Password) > 0 {
   485  		params["MasterUserPassword"] = desc.Password
   486  	}
   487  
   488  	if desc.Engine != api.DBINSTANCE_TYPE_AURORA {
   489  		params["StorageType"] = desc.StorageType
   490  		params["AllocatedStorage"] = fmt.Sprintf("%d", desc.DiskSizeGB)
   491  		for i, sec := range desc.SecgroupIds {
   492  			params[fmt.Sprintf("VpcSecurityGroupIds.VpcSecurityGroupId.%d", i+1)] = sec
   493  		}
   494  	}
   495  	if desc.MultiAz {
   496  		params["MultiAZ"] = "true"
   497  	}
   498  	if desc.StorageType == api.STORAGE_IO1_SSD {
   499  		params["Iops"] = "3000"
   500  	}
   501  	switch desc.Engine {
   502  	case api.DBINSTANCE_TYPE_MYSQL:
   503  		params["Engine"] = "mysql"
   504  	case api.DBINSTANCE_TYPE_POSTGRESQL:
   505  		params["Engine"] = "postgres"
   506  		params["MasterUsername"] = "postgres"
   507  	case api.DBINSTANCE_TYPE_MARIADB:
   508  		params["Engine"] = "mariadb"
   509  	case api.DBINSTANCE_TYPE_SQLSERVER:
   510  		params["LicenseModel"] = "license-included"
   511  		switch desc.Category {
   512  		case api.AWS_DBINSTANCE_CATEGORY_ENTERPRISE_EDITION:
   513  			params["Engine"] = "sqlserver-ee"
   514  		case api.AWS_DBINSTANCE_CATEGORY_EXPRESS_EDITION:
   515  			params["Engine"] = "sqlserver-ex"
   516  			params["MultiAZ"] = "false"
   517  		case api.AWS_DBINSTANCE_CATEGORY_STANDARD_EDITION:
   518  			params["Engine"] = "sqlserver-se"
   519  		case api.AWS_DBINSTANCE_CATEGORY_WEB_EDITION:
   520  			params["Engine"] = "sqlserver-web"
   521  			params["MultiAZ"] = "false"
   522  		default:
   523  			return nil, fmt.Errorf("invalid category %s for engine %s", desc.Category, desc.Engine)
   524  		}
   525  	case api.DBINSTANCE_TYPE_AURORA:
   526  		delete(params, "MultiAZ")
   527  		switch desc.Category {
   528  		case api.DBINSTANCE_TYPE_MYSQL:
   529  			params["Engine"] = "aurora"
   530  			if !strings.HasPrefix(desc.EngineVersion, "5.6") {
   531  				params["Engine"] = "aurora-mysql"
   532  			}
   533  		case api.DBINSTANCE_TYPE_POSTGRESQL:
   534  			params["Engine"] = "aurora-postgresql"
   535  			params["MasterUsername"] = "postgres"
   536  		default:
   537  			return nil, fmt.Errorf("invalid category %s for engine %s", desc.Category, desc.Engine)
   538  		}
   539  	case api.DBINSTANCE_TYPE_ORACLE:
   540  		params["LicenseModel"] = "bring-your-own-license"
   541  		switch desc.Category {
   542  		case api.AWS_DBINSTANCE_CATEGORY_ENTERPRISE_EDITION:
   543  			params["Engine"] = "oracle-ee"
   544  		case api.AWS_DBINSTANCE_CATEGORY_STANDARD_EDITION_TWO:
   545  			params["Engine"] = "oracle-se2"
   546  		default:
   547  			return nil, fmt.Errorf("invalid category %s for engine %s", desc.Category, desc.Engine)
   548  		}
   549  	}
   550  	i := 1
   551  	for k, v := range desc.Tags {
   552  		params[fmt.Sprintf("Tags.Tag.%d.Key", i)] = k
   553  		params[fmt.Sprintf("Tags.Tag.%d.Value", i)] = v
   554  		i++
   555  	}
   556  	result := struct {
   557  		DBInstance SDBInstance `xml:"DBInstance"`
   558  	}{}
   559  	result.DBInstance.region = self
   560  	return &result.DBInstance, self.rdsRequest("CreateDBInstance", params, &result)
   561  }
   562  
   563  func (self *SDBInstance) Delete() error {
   564  	params := map[string]string{
   565  		"DBInstanceIdentifier": self.DBInstanceIdentifier,
   566  		"SkipFinalSnapshot":    "true",
   567  	}
   568  	return self.region.rdsRequest("DeleteDBInstance", params, nil)
   569  }
   570  
   571  func (self *SRegion) RebootDBInstance(id string) error {
   572  	params := map[string]string{
   573  		"DBInstanceIdentifier": id,
   574  	}
   575  	return self.rdsRequest("RebootDBInstance", params, nil)
   576  }
   577  
   578  func (self *SDBInstance) SetTags(tags map[string]string, replace bool) error {
   579  	oldTags, err := self.region.ListRdsResourceTags(self.DBInstanceArn)
   580  	if err != nil {
   581  		return errors.Wrapf(err, "ListRdsResourceTags")
   582  	}
   583  	added, removed := map[string]string{}, map[string]string{}
   584  	for k, v := range tags {
   585  		oldValue, ok := oldTags[k]
   586  		if !ok {
   587  			added[k] = v
   588  		} else if oldValue != v {
   589  			removed[k] = oldValue
   590  			added[k] = v
   591  		}
   592  	}
   593  	if replace {
   594  		for k, v := range oldTags {
   595  			newValue, ok := tags[k]
   596  			if !ok {
   597  				removed[k] = v
   598  			} else if v != newValue {
   599  				added[k] = newValue
   600  				removed[k] = v
   601  			}
   602  		}
   603  	}
   604  	if len(removed) > 0 {
   605  		err = self.region.RemoveRdsTagsFromResource(self.DBInstanceArn, removed)
   606  		if err != nil {
   607  			return errors.Wrapf(err, "RemoveRdsTagsFromResource %s", removed)
   608  		}
   609  	}
   610  	if len(added) > 0 {
   611  		return self.region.AddRdsTagsToResource(self.DBInstanceArn, added)
   612  	}
   613  	return nil
   614  }
   615  
   616  func (self *SRegion) ListRdsResourceTags(arn string) (map[string]string, error) {
   617  	params := map[string]string{
   618  		"ResourceName": arn,
   619  	}
   620  	tags := AwsTags{}
   621  	err := self.rdsRequest("ListTagsForResource", params, &tags)
   622  	if err != nil {
   623  		return nil, errors.Wrapf(err, "ListTagsForResource")
   624  	}
   625  	return tags.GetTags()
   626  }
   627  
   628  func (self *SRegion) AddRdsTagsToResource(arn string, tags map[string]string) error {
   629  	if len(tags) == 0 {
   630  		return nil
   631  	}
   632  	params := map[string]string{
   633  		"ResourceName": arn,
   634  	}
   635  	i := 1
   636  	for k, v := range tags {
   637  		params[fmt.Sprintf("Tags.member.%d.Key", i)] = k
   638  		params[fmt.Sprintf("Tags.member.%d.Value", i)] = v
   639  	}
   640  	return self.rdsRequest("AddTagsToResource", params, nil)
   641  }
   642  
   643  func (self *SRegion) RemoveRdsTagsFromResource(arn string, tags map[string]string) error {
   644  	if len(tags) == 0 {
   645  		return nil
   646  	}
   647  	params := map[string]string{
   648  		"ResourceName": arn,
   649  	}
   650  	i := 1
   651  	for k := range tags {
   652  		params[fmt.Sprintf("TagKeys.member.%d", i)] = k
   653  	}
   654  	return self.rdsRequest("RemoveTagsFromResource", params, nil)
   655  }