diff --git a/.gitignore b/.gitignore index 4677ea017f..62d8a41609 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ _output /structargtest .idea .vscode +intellij.yaml GPATH GRTAGS GTAGS diff --git a/cmd/climc/shell/compute/elasticcache.go b/cmd/climc/shell/compute/elasticcache.go index 053e5aabd1..851a42db1f 100644 --- a/cmd/climc/shell/compute/elasticcache.go +++ b/cmd/climc/shell/compute/elasticcache.go @@ -437,4 +437,53 @@ func init() { printObject(result) return nil }) + + R(&options.ElasticCacheAutoRenewOptions{}, "elastic-cache-auto-renew", "Set elastisc cache auto renew", func(s *mcclient.ClientSession, opts *options.ElasticCacheAutoRenewOptions) error { + params, err := options.StructToParams(opts) + if err != nil { + return err + } + result, err := modules.ElasticCache.PerformAction(s, opts.ID, "set-auto-renew", params) + if err != nil { + return err + } + + printObject(result) + return nil + }) + + R(&options.ElasticCacheRenewOptions{}, "elastic-cache-renew", "Renew elastisc cache", func(s *mcclient.ClientSession, opts *options.ElasticCacheRenewOptions) error { + params, err := options.StructToParams(opts) + if err != nil { + return err + } + result, err := modules.ElasticCache.PerformAction(s, opts.ID, "renew", params) + if err != nil { + return err + } + + printObject(result) + return nil + }) + + type ElasticcacheSecgroupListOptions struct { + options.BaseListOptions + Elasticcache string `help:"ID or Name of elastic cache" json:"elasticcache"` + Secgroup string `help:"Secgroup ID or name"` + } + + R(&ElasticcacheSecgroupListOptions{}, "elastic-cache-secgroup-list", "List elastisc cache secgroups", func(s *mcclient.ClientSession, opts *ElasticcacheSecgroupListOptions) error { + params, err := options.ListStructToParams(opts) + if err != nil { + return err + } + + result, err := modules.ElasticCacheSecgroup.List(s, params) + if err != nil { + return err + } + + printList(result, nil) + return nil + }) } diff --git a/pkg/apis/compute/elasticcache_const.go b/pkg/apis/compute/elasticcache_const.go index 43e239aa37..5fd5a66907 100644 --- a/pkg/apis/compute/elasticcache_const.go +++ b/pkg/apis/compute/elasticcache_const.go @@ -44,6 +44,10 @@ const ( ELASTIC_CACHE_STATUS_UNKNOWN = "unknown" //(未知状态) ELASTIC_CACHE_STATUS_SYNCING = "syncing" //(同步中) ELASTIC_CACHE_STATUS_SYNC_FAILED = "sync_failed" //(同步失败) + ELASTIC_CACHE_RENEWING = "renewing" //(续费中) + ELASTIC_CACHE_RENEW_FAILED = "renew_failed" //(续费失败) + ELASTIC_CACHE_SET_AUTO_RENEW = "set_auto_renew" //(设置自动续费) + ELASTIC_CACHE_SET_AUTO_RENEW_FAILED = "set_auto_renew_failed" //(设置自动续费失败) ) const ( diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index a0cf5cd3ca..ade0d15af0 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -921,6 +921,7 @@ type ICloudElasticcache interface { UpdateAuthMode(noPasswordAccess bool, password string) error UpdateInstanceParameters(config jsonutils.JSONObject) error UpdateBackupPolicy(config SCloudElasticCacheBackupPolicyUpdateInput) error + Renew(bc billing.SBillingCycle) error SetMetadata(tags map[string]string, replace bool) error UpdateSecurityGroups(secgroupIds []string) error diff --git a/pkg/compute/models/cloudsyncelasticcache.go b/pkg/compute/models/cloudsyncelasticcache.go index dbddff5379..a760d61a55 100644 --- a/pkg/compute/models/cloudsyncelasticcache.go +++ b/pkg/compute/models/cloudsyncelasticcache.go @@ -149,7 +149,7 @@ func syncElasticcacheSecgroups(ctx context.Context, userCred mcclient.TokenCrede } result := localInstance.SyncElasticcacheSecgroups(ctx, userCred, secgroupIds) - syncResults.Add(ElasticcacheAclManager, result) + syncResults.Add(ElasticcachesecgroupManager, result) msg := result.Result() log.Infof("SyncElasticcacheSecgroups for dbinstance %s result: %s", localInstance.Name, msg) diff --git a/pkg/compute/models/elasticcache_instances.go b/pkg/compute/models/elasticcache_instances.go index 6b2b6dc384..c978cd3084 100644 --- a/pkg/compute/models/elasticcache_instances.go +++ b/pkg/compute/models/elasticcache_instances.go @@ -31,7 +31,6 @@ import ( "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" - "yunion.io/x/onecloud/pkg/apis/billing" billing_api "yunion.io/x/onecloud/pkg/apis/billing" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" @@ -556,6 +555,19 @@ func (self *SElasticcache) SyncWithCloudElasticcache(ctx context.Context, userCr self.MaintainEndTime = extInstance.GetMaintainEndTime() self.AuthMode = extInstance.GetAuthMode() + factory, err := provider.GetProviderFactory() + if err != nil { + return errors.Wrap(err, "SyncWithCloudElasticcache.GetProviderFactory") + } + + if factory.IsSupportPrepaidResources() { + self.BillingType = extInstance.GetBillingType() + if expired := extInstance.GetExpiredAt(); !expired.IsZero() { + self.ExpiredAt = expired + } + self.AutoRenew = extInstance.IsAutoRenew() + } + return nil }) if err != nil { @@ -847,7 +859,7 @@ func (self *SElasticcache) GetCreateAliyunElasticcacheParams(data *jsonutils.JSO } switch self.BillingType { - case billing.BILLING_TYPE_PREPAID: + case billing_api.BILLING_TYPE_PREPAID: input.ChargeType = "PrePaid" billingCycle, err := bc.ParseBillingCycle(self.BillingCycle) if err != nil { @@ -938,7 +950,7 @@ func (self *SElasticcache) GetCreateHuaweiElasticcacheParams(data *jsonutils.JSO } switch self.BillingType { - case billing.BILLING_TYPE_PREPAID: + case billing_api.BILLING_TYPE_PREPAID: input.ChargeType = "PrePaid" billingCycle, err := bc.ParseBillingCycle(self.BillingCycle) if err != nil { @@ -1025,7 +1037,7 @@ func (self *SElasticcache) GetCreateQCloudElasticcacheParams(data *jsonutils.JSO } switch self.BillingType { - case billing.BILLING_TYPE_PREPAID: + case billing_api.BILLING_TYPE_PREPAID: input.ChargeType = "PrePaid" billingCycle, err := bc.ParseBillingCycle(self.BillingCycle) if err != nil { @@ -2165,3 +2177,113 @@ func (self *SElasticcache) StartSyncSecgroupsTask(ctx context.Context, userCred task.ScheduleRun(nil) return nil } + +func (self *SElasticcache) AllowPerformSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "set-auto-renew") +} + +func (self *SElasticcache) SetAutoRenew(autoRenew bool) error { + _, err := db.Update(self, func() error { + self.AutoRenew = autoRenew + return nil + }) + return err +} + +/* +设置自动续费 +要求实例状态为running +要求实例计费类型为包年包月(预付费) +*/ +func (self *SElasticcache) PerformSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestAutoRenewInput) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(self.Status, []string{api.ELASTIC_CACHE_STATUS_RUNNING}) { + return nil, httperrors.NewUnsupportOperationError("The elastic cache status need be %s, current is %s", api.ELASTIC_CACHE_STATUS_RUNNING, self.Status) + } + + if self.BillingType != billing_api.BILLING_TYPE_PREPAID { + return nil, httperrors.NewUnsupportOperationError("Only %s elastic cache support set auto renew operation", billing_api.BILLING_TYPE_PREPAID) + } + + if self.AutoRenew == input.AutoRenew { + return nil, nil + } + + region := self.GetRegion() + if region == nil { + return nil, httperrors.NewResourceNotFoundError("elastic cache no related region found") + } + + if !region.GetDriver().IsSupportedElasticcacheAutoRenew() { + 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 *SElasticcache) StartSetAutoRenewTask(ctx context.Context, userCred mcclient.TokenCredential, autoRenew bool, parentTaskId string) error { + self.SetStatus(userCred, api.ELASTIC_CACHE_SET_AUTO_RENEW, "") + + data := jsonutils.NewDict() + data.Set("auto_renew", jsonutils.NewBool(autoRenew)) + task, err := taskman.TaskManager.NewTask(ctx, "ElasticcacheRenewTask", self, userCred, data, parentTaskId, "", nil) + if err != nil { + return errors.Wrap(err, "ElasticcacheRenewTask") + } + task.ScheduleRun(nil) + return nil +} + +func (self *SElasticcache) AllowPerformRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "renew") +} + +func (self *SElasticcache) PerformRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.BillingType != billing_api.BILLING_TYPE_PREPAID { + return nil, httperrors.NewUnsupportOperationError("Only %s elastic cache support renew operation", billing_api.BILLING_TYPE_PREPAID) + } + + durationStr, _ := data.GetString("duration") + if len(durationStr) == 0 { + return nil, httperrors.NewInputParameterError("missong duration") + } + + bc, err := bc.ParseBillingCycle(durationStr) + if err != nil { + return nil, httperrors.NewInputParameterError("invalid duration %s: %s", durationStr, err) + } + + region := self.GetRegion() + if region == nil { + return nil, httperrors.NewResourceNotFoundError("elastic cache no related region found") + } + + if !region.GetDriver().IsSupportedBillingCycle(bc, self.KeywordPlural()) { + return nil, httperrors.NewInputParameterError("unsupported duration %s", durationStr) + } + + err = self.startRenewTask(ctx, userCred, durationStr, "") + if err != nil { + return nil, err + } + + return nil, nil +} + +func (self *SElasticcache) startRenewTask(ctx context.Context, userCred mcclient.TokenCredential, duration string, parentTaskId string) error { + self.SetStatus(userCred, api.ELASTIC_CACHE_RENEWING, "") + data := jsonutils.NewDict() + data.Add(jsonutils.NewString(duration), "duration") + task, err := taskman.TaskManager.NewTask(ctx, "ElasticcacheRenewTask", self, userCred, data, parentTaskId, "", nil) + if err != nil { + log.Errorf("fail to crate ElasticcacheRenewTask %s", err) + return err + } + task.ScheduleRun(nil) + return nil +} diff --git a/pkg/compute/models/regiondrivers.go b/pkg/compute/models/regiondrivers.go index 2ebfc05914..4fd8b2d83b 100644 --- a/pkg/compute/models/regiondrivers.go +++ b/pkg/compute/models/regiondrivers.go @@ -177,6 +177,7 @@ type IElasticcacheDriver interface { IsSupportedElasticcache() bool // capability IsSupportedElasticcacheSecgroup() bool + IsSupportedElasticcacheAutoRenew() bool GetMaxElasticcacheSecurityGroupCount() int AllowCreateElasticcacheBackup(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, elasticcache *SElasticcache) error @@ -189,6 +190,8 @@ type IElasticcacheDriver interface { RequestCreateElasticcacheAccount(ctx context.Context, userCred mcclient.TokenCredential, elasticcacheAccount *SElasticcacheAccount, task taskman.ITask) error RequestCreateElasticcacheAcl(ctx context.Context, userCred mcclient.TokenCredential, elasticcacheAcl *SElasticcacheAcl, task taskman.ITask) error RequestCreateElasticcacheBackup(ctx context.Context, userCred mcclient.TokenCredential, elasticcacheBackup *SElasticcacheBackup, task taskman.ITask) error + RequestRenewElasticcache(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *SElasticcache, bc billing.SBillingCycle) (time.Time, error) + RequestElasticcacheSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *SElasticcache, autoRenew bool, task taskman.ITask) error RequestRestartElasticcache(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *SElasticcache, task taskman.ITask) error RequestSyncElasticcache(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *SElasticcache, task taskman.ITask) error RequestSyncSecgroupsForElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *SElasticcache, task taskman.ITask) error diff --git a/pkg/compute/models/secgroupcache.go b/pkg/compute/models/secgroupcache.go index 7b87293eec..48b92c354f 100644 --- a/pkg/compute/models/secgroupcache.go +++ b/pkg/compute/models/secgroupcache.go @@ -548,7 +548,7 @@ func (self *SSecurityGroupCache) StartSecurityGroupCacheDeleteTask(ctx context.C } func (manager *SSecurityGroupCacheManager) InitializeData() error { - providerIds := CloudproviderManager.Query("id").In("provider", []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_CTYUN}).SubQuery() + providerIds := CloudproviderManager.Query("id").In("provider", []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_CTYUN, api.CLOUD_PROVIDER_QCLOUD}).SubQuery() deprecatedSecgroups := []SSecurityGroupCache{} q := manager.Query().In("manager_id", providerIds).NotEquals("vpc_id", api.NORMAL_VPC_ID) diff --git a/pkg/compute/regiondrivers/aliyun.go b/pkg/compute/regiondrivers/aliyun.go index db7c787a87..c880b086d9 100644 --- a/pkg/compute/regiondrivers/aliyun.go +++ b/pkg/compute/regiondrivers/aliyun.go @@ -1090,7 +1090,7 @@ func (self *SAliyunRegionDriver) ValidateCreateDBInstanceData(ctx context.Contex func (self *SAliyunRegionDriver) IsSupportedBillingCycle(bc billing.SBillingCycle, resource string) bool { switch resource { - case models.DBInstanceManager.KeywordPlural(): + case models.DBInstanceManager.KeywordPlural(), models.ElasticcacheManager.KeywordPlural(): years := bc.GetYears() months := bc.GetMonths() if (years >= 1 && years <= 3) || (months >= 1 && months <= 9) { diff --git a/pkg/compute/regiondrivers/base.go b/pkg/compute/regiondrivers/base.go index fb7a1fcb81..478e26bd50 100644 --- a/pkg/compute/regiondrivers/base.go +++ b/pkg/compute/regiondrivers/base.go @@ -408,3 +408,15 @@ func (self *SBaseRegionDriver) IsDBInstanceNeedSecgroup() bool { func (self *SBaseRegionDriver) GetRdsSupportSecgroupCount() int { return 0 } + +func (self *SBaseRegionDriver) RequestRenewElasticcache(ctx context.Context, userCred mcclient.TokenCredential, instance *models.SElasticcache, bc billing.SBillingCycle) (time.Time, error) { + return time.Time{}, fmt.Errorf("Not Implement RequestRenewElasticcache") +} + +func (self *SBaseRegionDriver) IsSupportedElasticcacheAutoRenew() bool { + return false +} + +func (self *SBaseRegionDriver) RequestElasticcacheSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, autoRenew bool, task taskman.ITask) error { + return fmt.Errorf("Not Implement RequestElasticcacheSetAutoRenew") +} diff --git a/pkg/compute/regiondrivers/huawei.go b/pkg/compute/regiondrivers/huawei.go index cebbf036ab..63c146ab3a 100644 --- a/pkg/compute/regiondrivers/huawei.go +++ b/pkg/compute/regiondrivers/huawei.go @@ -2652,3 +2652,7 @@ func (self *SHuaWeiRegionDriver) GetBackendStatusForAdd() []string { func (self *SHuaWeiRegionDriver) GetRdsSupportSecgroupCount() int { return 1 } + +func (self *SHuaWeiRegionDriver) IsSupportedElasticcacheAutoRenew() bool { + return false +} diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 7041460241..b4bf3feeea 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -2861,3 +2861,65 @@ func (self *SManagedVirtualizationRegionDriver) RequestRemoteUpdateElasticcache( func (self *SManagedVirtualizationRegionDriver) RequestSyncSecgroupsForElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask) error { return fmt.Errorf("Not Implement RequestSyncSecgroupsForElasticcache") } + +func (self *SManagedVirtualizationRegionDriver) RequestRenewElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, bc billing.SBillingCycle) (time.Time, error) { + iregion, err := ec.GetIRegion() + if err != nil { + return time.Time{}, errors.Wrap(err, "GetIRegion") + } + + if len(ec.GetExternalId()) == 0 { + return time.Time{}, errors.Wrap(err, "ExternalId is empty") + } + + iec, err := iregion.GetIElasticcacheById(ec.GetExternalId()) + if err != nil { + return time.Time{}, errors.Wrap(err, "GetIElasticcacheById") + } + + oldExpired := iec.GetExpiredAt() + err = iec.Renew(bc) + if err != nil { + return time.Time{}, err + } + //避免有些云续费后过期时间刷新比较慢问题 + cloudprovider.WaitCreated(15*time.Second, 5*time.Minute, func() bool { + err := iec.Refresh() + if err != nil { + log.Errorf("failed refresh instance %s error: %v", ec.Name, err) + } + newExipred := iec.GetExpiredAt() + if newExipred.After(oldExpired) { + return true + } + return false + }) + return iec.GetExpiredAt(), nil +} + +func (self *SManagedVirtualizationRegionDriver) IsSupportedElasticcacheAutoRenew() bool { + return true +} + +func (self *SManagedVirtualizationRegionDriver) RequestElasticcacheSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, autoRenew bool, task taskman.ITask) error { + iregion, err := ec.GetIRegion() + if err != nil { + return errors.Wrap(err, "GetIRegion") + } + + if len(ec.GetExternalId()) == 0 { + return errors.Wrap(err, "ExternalId is empty") + } + + iec, err := iregion.GetIElasticcacheById(ec.GetExternalId()) + if err != nil { + return errors.Wrap(err, "GetIElasticcacheById") + } + + err = iec.SetAutoRenew(autoRenew) + if err != nil { + return errors.Wrap(err, "SetAutoRenew") + } + + return ec.SetAutoRenew(autoRenew) +} diff --git a/pkg/compute/regiondrivers/qcloud.go b/pkg/compute/regiondrivers/qcloud.go index f3f9326828..5ec2d90791 100644 --- a/pkg/compute/regiondrivers/qcloud.go +++ b/pkg/compute/regiondrivers/qcloud.go @@ -1405,7 +1405,7 @@ func (self *SQcloudRegionDriver) ValidateCreateDBInstanceData(ctx context.Contex func (self *SQcloudRegionDriver) IsSupportedBillingCycle(bc billing.SBillingCycle, resource string) bool { switch resource { - case models.DBInstanceManager.KeywordPlural(): + case models.DBInstanceManager.KeywordPlural(), models.ElasticcacheManager.KeywordPlural(): years := bc.GetYears() months := bc.GetMonths() if (years >= 1 && years <= 3) || (months >= 1 && months <= 12) { @@ -1648,8 +1648,13 @@ func (self *SQcloudRegionDriver) RequestSyncSecgroupsForElasticcache(ctx context } vpc := ec.GetVpc() + vpcId, err := self.GetSecurityGroupVpcId(ctx, userCred, ec.GetRegion(), nil, vpc, false) + if err != nil { + return nil, errors.Wrap(err, "GetSecurityGroupVpcId") + } + for i := range ess { - externalId, err := self.RequestSyncSecurityGroup(ctx, task.GetUserCred(), vpc.GetId(), vpc, ess[i].GetSecGroup(), extProjectId) + externalId, err := self.RequestSyncSecurityGroup(ctx, task.GetUserCred(), vpcId, vpc, ess[i].GetSecGroup(), extProjectId) if err != nil { return nil, errors.Wrap(err, "RequestSyncSecurityGroup") } diff --git a/pkg/compute/tasks/elasticcache_renew_task.go b/pkg/compute/tasks/elasticcache_renew_task.go new file mode 100644 index 0000000000..0c9852bab0 --- /dev/null +++ b/pkg/compute/tasks/elasticcache_renew_task.go @@ -0,0 +1,60 @@ +package tasks + +import ( + "context" + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + + 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/billing" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type ElasticcacheRenewTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(ElasticcacheRenewTask{}) +} + +func (self *ElasticcacheRenewTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + instance := obj.(*models.SElasticcache) + + durationStr, _ := self.GetParams().GetString("duration") + bc, _ := billing.ParseBillingCycle(durationStr) + + exp, err := instance.GetRegion().GetDriver().RequestRenewElasticcache(ctx, self.UserCred, instance, bc) + if err != nil { + db.OpsLog.LogEvent(instance, db.ACT_REW_FAIL, err, self.UserCred) + logclient.AddActionLogWithStartable(self, instance, logclient.ACT_RENEW, err, self.UserCred, false) + instance.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_RENEW_FAILED, err.Error()) + self.SetStageFailed(ctx, jsonutils.Marshal(err)) + return + } + + err = instance.SaveRenewInfo(ctx, self.UserCred, &bc, &exp, "") + if err != nil { + msg := fmt.Sprintf("SaveRenewInfo fail %s", err) + log.Errorf(msg) + self.SetStageFailed(ctx, jsonutils.NewString(msg)) + return + } + + logclient.AddActionLogWithStartable(self, instance, logclient.ACT_RENEW, nil, self.UserCred, true) + self.SetStage("OnElasticcacheSyncstatus", nil) + models.StartResourceSyncStatusTask(ctx, self.UserCred, instance, "ElasticcacheSyncstatusTask", self.GetTaskId()) +} + +func (self *ElasticcacheRenewTask) OnElasticcacheSyncstatusComplete(ctx context.Context, ec *models.SElasticcache, data jsonutils.JSONObject) { + self.SetStageComplete(ctx, nil) +} + +func (self *ElasticcacheRenewTask) OnElasticcacheSyncstatusCompleteFailed(ctx context.Context, ec *models.SElasticcache, data jsonutils.JSONObject) { + self.SetStageFailed(ctx, data) +} diff --git a/pkg/compute/tasks/elasticcache_set_auto_renew_task.go b/pkg/compute/tasks/elasticcache_set_auto_renew_task.go new file mode 100644 index 0000000000..7c78915042 --- /dev/null +++ b/pkg/compute/tasks/elasticcache_set_auto_renew_task.go @@ -0,0 +1,47 @@ +package tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + + 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 ElasticcacheSetAutoRenewTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(ElasticcacheSetAutoRenewTask{}) +} + +func (self *ElasticcacheSetAutoRenewTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + ec := obj.(*models.SElasticcache) + + autoRenew, _ := self.GetParams().Bool("auto_renew") + err := ec.GetRegion().GetDriver().RequestElasticcacheSetAutoRenew(ctx, self.UserCred, ec, autoRenew, self) + if err != nil { + db.OpsLog.LogEvent(ec, db.ACT_SET_AUTO_RENEW_FAIL, err, self.UserCred) + logclient.AddActionLogWithStartable(self, ec, logclient.ACT_SET_AUTO_RENEW, err, self.UserCred, false) + ec.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_SET_AUTO_RENEW_FAILED, err.Error()) + self.SetStageFailed(ctx, jsonutils.Marshal(err)) + return + } + + logclient.AddActionLogWithStartable(self, ec, logclient.ACT_SET_AUTO_RENEW, nil, self.UserCred, true) + self.SetStage("OnElasticcacheSyncstatus", nil) + models.StartResourceSyncStatusTask(ctx, self.UserCred, ec, "ElasticcacheSyncstatusTask", self.GetTaskId()) +} + +func (self *ElasticcacheSetAutoRenewTask) OnElasticcacheSyncstatusComplete(ctx context.Context, ec *models.SElasticcache, data jsonutils.JSONObject) { + self.SetStageComplete(ctx, nil) +} + +func (self *ElasticcacheSetAutoRenewTask) OnElasticcacheSyncstatusCompleteFailed(ctx context.Context, ec *models.SElasticcache, data jsonutils.JSONObject) { + self.SetStageFailed(ctx, data) +} diff --git a/pkg/compute/tasks/elasticcache_syncsecgroups_task.go b/pkg/compute/tasks/elasticcache_syncsecgroups_task.go index 2072dfb62e..9388d7f3ae 100644 --- a/pkg/compute/tasks/elasticcache_syncsecgroups_task.go +++ b/pkg/compute/tasks/elasticcache_syncsecgroups_task.go @@ -87,6 +87,7 @@ func (self *ElasticcacheSyncsecgroupsTask) OnElasticcacheSyncSecgroupsComplete(c return } + cache.SetStatus(self.UserCred, iec.GetStatus(), "UpdateSecurityGroups") self.SetStageComplete(ctx, nil) } diff --git a/pkg/mcclient/modules/mod_elasticcache.go b/pkg/mcclient/modules/mod_elasticcache.go index be2f0d6399..5c30c806c6 100644 --- a/pkg/mcclient/modules/mod_elasticcache.go +++ b/pkg/mcclient/modules/mod_elasticcache.go @@ -29,6 +29,7 @@ var ( ElasticCacheBackup modulebase.ResourceManager ElasticCacheAcl modulebase.ResourceManager ElasticCacheParameter modulebase.ResourceManager + ElasticCacheSecgroup modulebase.JointResourceManager ) type ElasticCacheManager struct { @@ -60,11 +61,19 @@ func init() { []string{}, []string{}) + ElasticCacheSecgroup = NewJointComputeManager("elasticcachesecgroup", + "elasticcachesecgroups", + []string{}, + []string{}, + &ElasticCache, + &SecGroups) + registerCompute(&ElasticCache) registerCompute(&ElasticCacheAccount) registerCompute(&ElasticCacheAcl) registerCompute(&ElasticCacheBackup) registerCompute(&ElasticCacheParameter) + registerCompute(&ElasticCacheSecgroup) } func (self *ElasticCacheManager) GetLoginInfo(s *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) { diff --git a/pkg/mcclient/options/elasticcache.go b/pkg/mcclient/options/elasticcache.go index 3506b219d7..2652122f85 100644 --- a/pkg/mcclient/options/elasticcache.go +++ b/pkg/mcclient/options/elasticcache.go @@ -102,3 +102,19 @@ type ElasticCacheRemoteUpdateOptions struct { ID string `json:"-"` computeapi.ElasticcacheRemoteUpdateInput } + +type ElasticCacheAutoRenewOptions struct { + ElasticCacheIdOptions + AutoRenew bool `help:"Set elastic cache auto renew or manual renew"` +} + +func (o *ElasticCacheAutoRenewOptions) Params() (jsonutils.JSONObject, error) { + params := jsonutils.NewDict() + params.Set("auto_renew", jsonutils.NewBool(o.AutoRenew)) + return params, nil +} + +type ElasticCacheRenewOptions struct { + ID string `json:"-"` + Duration string `help:"valid duration of the elastic cache, e.g. 1H, 1D, 1W, 1M, 1Y, ADMIN ONLY option"` +} diff --git a/pkg/multicloud/aliyun/elasticcache_instance.go b/pkg/multicloud/aliyun/elasticcache_instance.go index 9490bde2f5..0b3c425852 100644 --- a/pkg/multicloud/aliyun/elasticcache_instance.go +++ b/pkg/multicloud/aliyun/elasticcache_instance.go @@ -26,10 +26,11 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/apis/billing" + billingapi "yunion.io/x/onecloud/pkg/apis/billing" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/multicloud" + "yunion.io/x/onecloud/pkg/util/billing" ) // https://help.aliyun.com/document_detail/60933.html?spm=a2c4g.11186623.6.726.38f82ca9U1Gtxw @@ -631,7 +632,7 @@ func (self *SRegion) CreateIElasticcaches(ec *cloudprovider.SCloudElasticCacheIn params["VpcId"] = ec.VpcId params["VSwitchId"] = ec.NetworkId params["ChargeType"] = ec.ChargeType - if strings.ToLower(ec.ChargeType) == billing.BILLING_TYPE_PREPAID && ec.BC != nil { + if strings.ToLower(ec.ChargeType) == billingapi.BILLING_TYPE_PREPAID && ec.BC != nil { if ec.BC.GetMonths() >= 1 && ec.BC.GetMonths() <= 9 { params["Period"] = strconv.Itoa(ec.BC.GetMonths()) } else if ec.BC.GetMonths() == 12 || ec.BC.GetMonths() == 24 || ec.BC.GetMonths() == 36 { @@ -938,3 +939,41 @@ func (instance *SElasticcache) SetMetadata(tags map[string]string, replace bool) func (self *SElasticcache) UpdateSecurityGroups(secgroupIds []string) error { return errors.Wrap(cloudprovider.ErrNotSupported, "UpdateSecurityGroups") } + +// https://help.aliyun.com/document_detail/60903.html?spm=a2c4g.11174283.6.877.34653436jn6t4C +// https://help.aliyun.com/document_detail/95584.html?spm=a2c4g.11186623.6.907.60e3292bWsVIOE +// https://help.aliyun.com/document_detail/60909.html?spm=a2c4g.11186623.6.910.17664ce0jfir7F +func (self *SElasticcache) Renew(bc billing.SBillingCycle) error { + month := bc.GetMonths() + if month <= 0 { + return errors.Wrap(fmt.Errorf("month should great than 0"), "GetMonths") + } + + if (month >= 1 && month <= 9) || month == 12 || month == 24 || month == 36 { + return errors.Wrap(fmt.Errorf("month %d is invalid, required: 1~9、12、24、36", month), "GetMonths") + } + + params := map[string]string{} + params["InstanceId"] = self.GetId() + params["Period"] = fmt.Sprintf("%d", month) + params["AutoPay"] = "true" + _, err := self.region.kvsRequest("RenewInstance", params) + if err != nil { + return errors.Wrap(err, "RenewInstance") + } + + return nil +} + +func (self *SElasticcache) SetAutoRenew(autoRenew bool) error { + params := map[string]string{} + params["DBInstanceId"] = self.GetId() + params["Duration"] = "1" + params["AutoRenew"] = fmt.Sprintf("%v", autoRenew) + _, err := self.region.kvsRequest("ModifyInstanceAutoRenewalAttribute", params) + if err != nil { + return errors.Wrap(err, "ModifyInstanceAutoRenewalAttribute") + } + + return nil +} diff --git a/pkg/multicloud/huawei/elasticcache_instance.go b/pkg/multicloud/huawei/elasticcache_instance.go index e2f07715a6..4987ad39a5 100644 --- a/pkg/multicloud/huawei/elasticcache_instance.go +++ b/pkg/multicloud/huawei/elasticcache_instance.go @@ -28,6 +28,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/multicloud" + "yunion.io/x/onecloud/pkg/util/billing" ) // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html @@ -748,3 +749,11 @@ func (instance *SElasticcache) SetMetadata(tags map[string]string, replace bool) func (self *SElasticcache) UpdateSecurityGroups(secgroupIds []string) error { return errors.Wrap(cloudprovider.ErrNotSupported, "UpdateSecurityGroups") } + +func (self *SElasticcache) Renew(bc billing.SBillingCycle) error { + return cloudprovider.ErrNotSupported +} + +func (self *SElasticcache) SetAutoRenew(autoRenew bool) error { + return cloudprovider.ErrNotSupported +} diff --git a/pkg/multicloud/qcloud/elasticcache_instance.go b/pkg/multicloud/qcloud/elasticcache_instance.go index 4bda3d885c..a2c4324bbb 100644 --- a/pkg/multicloud/qcloud/elasticcache_instance.go +++ b/pkg/multicloud/qcloud/elasticcache_instance.go @@ -14,6 +14,7 @@ import ( billing_api "yunion.io/x/onecloud/pkg/apis/billing" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/util/billing" ) type SElasticcache struct { @@ -269,8 +270,23 @@ func (self *SElasticcache) GetExpiredAt() time.Time { return t } +// https://cloud.tencent.com/document/product/239/31785 func (self *SElasticcache) SetAutoRenew(autoRenew bool) error { - panic("implement me") + params := map[string]string{} + params["Operation"] = "modifyAutoRenew" + params["InstanceId.0"] = self.GetId() + if autoRenew { + params["AutoRenews.0"] = "1" + } else { + params["AutoRenews.0"] = "0" + } + + _, err := self.region.redisRequest("ModifyInstance", params) + if err != nil { + return errors.Wrap(err, "ModifyInstance") + } + + return nil } func (self *SElasticcache) IsAutoRenew() bool { @@ -922,6 +938,23 @@ func (self *SElasticcache) UpdateSecurityGroups(secgroupIds []string) error { return nil } +func (self *SElasticcache) Renew(bc billing.SBillingCycle) error { + month := bc.GetMonths() + if month <= 0 { + return errors.Wrap(fmt.Errorf("month should great than 0"), "GetMonths") + } + + params := map[string]string{} + params["InstanceId"] = self.GetId() + params["Period"] = fmt.Sprintf("%d", month) + _, err := self.region.redisRequest("RenewInstance", params) + if err != nil { + return errors.Wrap(err, "RenewInstance") + } + + return nil +} + // https://cloud.tencent.com/document/api/239/38924 func (self *SRegion) GetCloudElasticcacheAccounts(instanceId string) ([]SElasticcacheAccount, error) { params := map[string]string{}