From a7e832488e17a915e38f94982c0c071e38276ef1 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Mon, 9 Nov 2020 19:59:00 +0800 Subject: [PATCH] fix: support rds set auto renew --- pkg/apis/compute/dbinstance.go | 5 ++ pkg/apis/compute/dbinstance_const.go | 38 ++++++----- pkg/compute/models/dbinstances.go | 58 ++++++++++++++++ pkg/compute/models/regiondrivers.go | 1 + pkg/compute/regiondrivers/aliyun.go | 4 ++ pkg/compute/regiondrivers/base.go | 4 ++ pkg/compute/regiondrivers/managedvirtual.go | 1 + pkg/compute/regiondrivers/qcloud.go | 4 ++ .../tasks/dbinstance_set_auto_renew_task.go | 68 +++++++++++++++++++ pkg/multicloud/aliyun/dbinstance.go | 25 +++++++ 10 files changed, 190 insertions(+), 18 deletions(-) create mode 100644 pkg/compute/tasks/dbinstance_set_auto_renew_task.go diff --git a/pkg/apis/compute/dbinstance.go b/pkg/apis/compute/dbinstance.go index 881335fc5c..2df0a39de7 100644 --- a/pkg/apis/compute/dbinstance.go +++ b/pkg/apis/compute/dbinstance.go @@ -357,3 +357,8 @@ type DBInstanceNetworkListInput struct { NetworkFilterListInput } + +type DBInstanceAutoRenewInput struct { + // 是否自动续费 + AutoRenew bool `json:"auto_renew"` +} diff --git a/pkg/apis/compute/dbinstance_const.go b/pkg/apis/compute/dbinstance_const.go index c7b3aba822..8145ed14c5 100644 --- a/pkg/apis/compute/dbinstance_const.go +++ b/pkg/apis/compute/dbinstance_const.go @@ -16,24 +16,26 @@ package compute const ( //实例状态 - DBINSTANCE_INIT = "init" //初始化 - DBINSTANCE_DEPLOYING = "deploying" //部署中 - DBINSTANCE_RUNNING = "running" //运行中 - DBINSTANCE_REBOOTING = "rebooting" //重启中 - DBINSTANCE_MIGRATING = "migrating" //迁移中 - DBINSTANCE_BACKING_UP = "backing_up" //备份中 - DBINSTANCE_BACKING_UP_FAILED = "backing_up_failed" //备份失败 - DBINSTANCE_RESTORING = "restoring" //备份恢复中 - DBINSTANCE_RESTORE_FAILED = "restore_failed" - DBINSTANCE_IMPORTING = "importing" //数据导入中 - DBINSTANCE_CLONING = "cloning" //克隆中 - DBINSTANCE_DELETING = "deleting" //删除中 - DBINSTANCE_DELETE_FAILED = "delete_failed" //删除失败 - DBINSTANCE_MAINTENANCE = "maintenance" //维护中 - DBINSTANCE_ISOLATING = "isolating" //隔离中 - DBINSTANCE_ISOLATE = "isolate" //已隔离 - DBINSTANCE_UPGRADING = "upgrading" //升级中 - DBINSTANCE_UNKNOWN = "unknown" + DBINSTANCE_INIT = "init" //初始化 + DBINSTANCE_DEPLOYING = "deploying" //部署中 + DBINSTANCE_RUNNING = "running" //运行中 + DBINSTANCE_REBOOTING = "rebooting" //重启中 + DBINSTANCE_MIGRATING = "migrating" //迁移中 + DBINSTANCE_BACKING_UP = "backing_up" //备份中 + DBINSTANCE_BACKING_UP_FAILED = "backing_up_failed" //备份失败 + DBINSTANCE_RESTORING = "restoring" //备份恢复中 + DBINSTANCE_RESTORE_FAILED = "restore_failed" + DBINSTANCE_IMPORTING = "importing" //数据导入中 + DBINSTANCE_CLONING = "cloning" //克隆中 + DBINSTANCE_DELETING = "deleting" //删除中 + DBINSTANCE_DELETE_FAILED = "delete_failed" //删除失败 + DBINSTANCE_MAINTENANCE = "maintenance" //维护中 + DBINSTANCE_ISOLATING = "isolating" //隔离中 + DBINSTANCE_ISOLATE = "isolate" //已隔离 + DBINSTANCE_UPGRADING = "upgrading" //升级中 + DBINSTANCE_SET_AUTO_RENEW = "set_auto_renew" //设置自动续费中 + DBINSTANCE_SET_AUTO_RENEW_FAILED = "set_auto_renew_failed" //设置自动续费失败 + DBINSTANCE_UNKNOWN = "unknown" DBINSTANCE_CHANGE_CONFIG = "change_config" //调整配置 DBINSTANCE_CHANGE_CONFIG_FAILED = "change_config_failed" //调整配置失败 diff --git a/pkg/compute/models/dbinstances.go b/pkg/compute/models/dbinstances.go index 6c89f4c2d7..e63655a75f 100644 --- a/pkg/compute/models/dbinstances.go +++ b/pkg/compute/models/dbinstances.go @@ -901,6 +901,64 @@ func (self *SDBInstance) PerformRenew(ctx context.Context, userCred mcclient.Tok return nil, self.StartDBInstanceRenewTask(ctx, userCred, durationStr, "") } +func (self *SDBInstance) AllowPerformSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "set-auto-renew") +} + +func (self *SDBInstance) SetAutoRenew(autoRenew bool) error { + _, err := db.Update(self, func() error { + self.AutoRenew = autoRenew + return nil + }) + return err +} + +// 设置自动续费 +// 要求RDS状态为running +// 要求RDS计费类型为包年包月(预付费) +func (self *SDBInstance) PerformSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DBInstanceAutoRenewInput) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(self.Status, []string{api.DBINSTANCE_RUNNING}) { + return nil, httperrors.NewUnsupportOperationError("The dbinstance status need be %s, current is %s", api.DBINSTANCE_RUNNING, self.Status) + } + + if self.BillingType != billing_api.BILLING_TYPE_PREPAID { + return nil, httperrors.NewUnsupportOperationError("Only %s dbinstance support this operation", billing_api.BILLING_TYPE_PREPAID) + } + + if self.AutoRenew == input.AutoRenew { + return nil, nil + } + + driver, err := self.GetRegionDriver() + if err != nil { + return nil, errors.Wrapf(err, "GetRegionDriver") + } + + if !driver.IsSupportedDBInstanceAutoRenew() { + err := self.SetAutoRenew(input.AutoRenew) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + + logclient.AddSimpleActionLog(self, logclient.ACT_SET_AUTO_RENEW, jsonutils.Marshal(input), userCred, true) + return nil, nil + } + + return nil, self.StartSetAutoRenewTask(ctx, userCred, input.AutoRenew, "") +} + +func (self *SDBInstance) StartSetAutoRenewTask(ctx context.Context, userCred mcclient.TokenCredential, autoRenew bool, parentTaskId string) error { + data := jsonutils.NewDict() + data.Set("auto_renew", jsonutils.NewBool(autoRenew)) + task, err := taskman.TaskManager.NewTask(ctx, "DBInstanceSetAutoRenewTask", self, userCred, data, parentTaskId, "", nil) + if err != nil { + return errors.Wrap(err, "NewTask") + } + self.SetStatus(userCred, api.DBINSTANCE_SET_AUTO_RENEW, "") + task.ScheduleRun(nil) + return nil +} + func (self *SDBInstance) AllowPerformPublicConnection(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "public-connection") } diff --git a/pkg/compute/models/regiondrivers.go b/pkg/compute/models/regiondrivers.go index 4fd8b2d83b..ea0230ca86 100644 --- a/pkg/compute/models/regiondrivers.go +++ b/pkg/compute/models/regiondrivers.go @@ -162,6 +162,7 @@ type IDBInstanceDriver interface { RequestChangeDBInstanceConfig(ctx context.Context, userCred mcclient.TokenCredential, instance *SDBInstance, task taskman.ITask) error IsSupportedDBInstance() bool + IsSupportedDBInstanceAutoRenew() bool IsSupportDBInstancePublicConnection() bool IsSupportKeepDBInstanceManualBackup() bool diff --git a/pkg/compute/regiondrivers/aliyun.go b/pkg/compute/regiondrivers/aliyun.go index c880b086d9..ca1fb2bc2f 100644 --- a/pkg/compute/regiondrivers/aliyun.go +++ b/pkg/compute/regiondrivers/aliyun.go @@ -1573,6 +1573,10 @@ func (self *SAliyunRegionDriver) IsSupportedDBInstance() bool { return true } +func (self *SAliyunRegionDriver) IsSupportedDBInstanceAutoRenew() bool { + return true +} + func (self *SAliyunRegionDriver) IsSupportedElasticcache() bool { return true } diff --git a/pkg/compute/regiondrivers/base.go b/pkg/compute/regiondrivers/base.go index 478e26bd50..a3805d10c9 100644 --- a/pkg/compute/regiondrivers/base.go +++ b/pkg/compute/regiondrivers/base.go @@ -364,6 +364,10 @@ func (self *SBaseRegionDriver) IsSupportedDBInstance() bool { return false } +func (self *SBaseRegionDriver) IsSupportedDBInstanceAutoRenew() bool { + return false +} + func (self *SBaseRegionDriver) IsSupportedElasticcache() bool { return false } diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index b4bf3feeea..a54efda86a 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -1720,6 +1720,7 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstance(ctx cont log.Errorf("failed to parse billing cycle %s: %v", dbinstance.BillingCycle, err) } else if bc.IsValid() { desc.BillingCycle = &bc + desc.BillingCycle.AutoRenew = dbinstance.AutoRenew } } diff --git a/pkg/compute/regiondrivers/qcloud.go b/pkg/compute/regiondrivers/qcloud.go index 5ec2d90791..c19d0e80f5 100644 --- a/pkg/compute/regiondrivers/qcloud.go +++ b/pkg/compute/regiondrivers/qcloud.go @@ -1392,6 +1392,10 @@ func (self *SQcloudRegionDriver) IsSupportedDBInstance() bool { return true } +func (self *SQcloudRegionDriver) IsSupportedDBInstanceAutoRenew() bool { + return true +} + func (self *SQcloudRegionDriver) GetRdsSupportSecgroupCount() int { return 5 } diff --git a/pkg/compute/tasks/dbinstance_set_auto_renew_task.go b/pkg/compute/tasks/dbinstance_set_auto_renew_task.go new file mode 100644 index 0000000000..8b75e67f6a --- /dev/null +++ b/pkg/compute/tasks/dbinstance_set_auto_renew_task.go @@ -0,0 +1,68 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type DBInstanceSetAutoRenewTask struct { + taskman.ITask +} + +func init() { + taskman.RegisterTask(DBInstanceSetAutoRenewTask{}) +} + +func (self *DBInstanceSetAutoRenewTask) taskFailed(ctx context.Context, rds *models.SDBInstance, err error) { + db.OpsLog.LogEvent(rds, db.ACT_SET_AUTO_RENEW_FAIL, err, self.GetUserCred()) + logclient.AddActionLogWithStartable(self, rds, logclient.ACT_SET_AUTO_RENEW, err, self.GetUserCred(), false) + rds.SetStatus(self.GetUserCred(), api.DBINSTANCE_SET_AUTO_RENEW_FAILED, err.Error()) + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) +} + +func (self *DBInstanceSetAutoRenewTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + rds := obj.(*models.SDBInstance) + autoRenew, _ := self.GetParams().Bool("auto_renew") + iRds, err := rds.GetIDBInstance() + if err != nil { + self.taskFailed(ctx, rds, errors.Wrapf(err, "GetIDBInstance")) + return + } + err = iRds.SetAutoRenew(autoRenew) + if err != nil { + self.taskFailed(ctx, rds, errors.Wrapf(err, "iRds.SetAutoRenew")) + return + } + self.SetStage("OnDBInstanceSyncComplete", nil) + rds.StartDBInstanceSyncTask(ctx, self.GetUserCred(), nil, "") +} + +func (self *DBInstanceSetAutoRenewTask) OnDBInstanceSyncComplete(ctx context.Context, rds *models.SDBInstance, data jsonutils.JSONObject) { + self.SetStageComplete(ctx, nil) +} + +func (self *DBInstanceSetAutoRenewTask) OnDBInstanceSyncCompleteFailed(ctx context.Context, rds *models.SDBInstance, data jsonutils.JSONObject) { + self.SetStageFailed(ctx, data) +} diff --git a/pkg/multicloud/aliyun/dbinstance.go b/pkg/multicloud/aliyun/dbinstance.go index f5a6d3e5f3..3df4b2b570 100644 --- a/pkg/multicloud/aliyun/dbinstance.go +++ b/pkg/multicloud/aliyun/dbinstance.go @@ -645,6 +645,9 @@ func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceC params["UsedTime"] = fmt.Sprintf("%d", desc.BillingCycle.GetYears()) } params["AutoRenew"] = "False" + if desc.BillingCycle.AutoRenew { + params["AutoRenew"] = "True" + } } action := "CreateDBInstance" @@ -768,6 +771,28 @@ func (rds *SDBInstance) Renew(bc billing.SBillingCycle) error { return rds.region.RenewInstance(rds.DBInstanceId, bc) } +func (rds *SDBInstance) SetAutoRenew(autoRenew bool) error { + return rds.region.ModifyInstanceAutoRenewalAttribute(rds.DBInstanceId, 1, autoRenew) +} + +func (region *SRegion) ModifyInstanceAutoRenewalAttribute(rdsId string, month int, autoRenew bool) error { + params := map[string]string{ + "RegionId": region.RegionId, + "DBInstanceId": rdsId, + "AutoRenew": "False", + "ClientToken": utils.GenRequestId(20), + } + if autoRenew { + params["AutoRenew"] = "True" + params["Duration"] = fmt.Sprintf("%d", month) + } + _, err := region.rdsRequest("ModifyInstanceAutoRenewalAttribute", params) + if err != nil { + return errors.Wrap(err, "ModifyInstanceAutoRenewalAttribute") + } + return nil +} + func (region *SRegion) RenewDBInstance(instanceId string, bc billing.SBillingCycle) error { params := map[string]string{ "DBInstanceId": instanceId,