dbinstance.go 23 KB

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