dbinstance.go 22 KB

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