From c477a05f39b374b2f4e6c56492801e12c97ffdf2 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Sat, 5 Jan 2019 09:51:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=EF=BC=9A1.=20=E5=8C=85?= =?UTF-8?q?=E5=B9=B4=E5=8C=85=E6=9C=88=E4=B8=BB=E6=9C=BA=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=B1=A0=E5=90=8E=EF=BC=8C=E5=A6=82=E6=9E=9C?= =?UTF-8?q?auto=5Fdelete=3Dtrue=EF=BC=8C=E5=88=99=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=AF=A5=E4=B8=BB=E6=9C=BA=202.=20=E5=8C=85?= =?UTF-8?q?=E5=B9=B4=E5=8C=85=E6=9C=88=E8=B5=84=E6=BA=90=E6=B1=A0=E7=9A=84?= =?UTF-8?q?=E5=AE=BF=E4=B8=BB=E6=9C=BA=E6=94=AF=E6=8C=81=E7=BB=AD=E8=B4=B9?= =?UTF-8?q?=E6=93=8D=E4=BD=9CPerform(host.id,=20"renew-prepaid-recycle",?= =?UTF-8?q?=20{"duration":=20"1m"})?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/climc/shell/hosts.go | 15 ++++++ cmd/climc/shell/servers.go | 6 ++- pkg/compute/models/guests.go | 3 +- pkg/compute/models/host_recycle.go | 66 ++++++++++++++++++++++++++ pkg/compute/models/hosts.go | 4 +- pkg/compute/models/storages.go | 2 +- pkg/compute/tasks/guest_delete_task.go | 5 +- pkg/compute/tasks/guest_renew_task.go | 48 +++++++++++++++++++ pkg/util/aliyun/aliyun.go | 2 +- pkg/util/aliyun/instance.go | 12 +++-- pkg/util/aliyun/zone.go | 3 +- 11 files changed, 154 insertions(+), 12 deletions(-) diff --git a/cmd/climc/shell/hosts.go b/cmd/climc/shell/hosts.go index a58877802d..24368e75ac 100644 --- a/cmd/climc/shell/hosts.go +++ b/cmd/climc/shell/hosts.go @@ -497,4 +497,19 @@ func init() { printObject(result) return nil }) + + type PrepaidRecycleHostRenewOptions struct { + ID string `help:"ID or name of server to renew"` + DURATION string `help:"Duration of renew, ADMIN only command"` + } + R(&PrepaidRecycleHostRenewOptions{}, "host-renew-prepaid-recycle", "Renew a prepaid recycle host", func(s *mcclient.ClientSession, args *PrepaidRecycleHostRenewOptions) error { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(args.DURATION), "duration") + result, err := modules.Hosts.PerformAction(s, args.ID, "renew-prepaid-recycle", params) + if err != nil { + return err + } + printObject(result) + return nil + }) } diff --git a/cmd/climc/shell/servers.go b/cmd/climc/shell/servers.go index 93d159abbd..8790256b7c 100644 --- a/cmd/climc/shell/servers.go +++ b/cmd/climc/shell/servers.go @@ -634,10 +634,14 @@ func init() { }) type ServerPrepaidRecycleOptions struct { - ID string `help:"ID or name of server to recycle"` + ID string `help:"ID or name of server to recycle"` + AutoDelete bool `help:"after joining the pool, remove the server automatically"` } R(&ServerPrepaidRecycleOptions{}, "server-enable-recycle", "Put a prepaid server into recycle pool, so that it can be shared", func(s *mcclient.ClientSession, args *ServerPrepaidRecycleOptions) error { params := jsonutils.NewDict() + if args.AutoDelete { + params.Add(jsonutils.JSONTrue, "auto_delete") + } result, err := modules.Servers.PerformAction(s, args.ID, "prepaid-recycle", params) if err != nil { return err diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index d1dc92bd30..fbbf5e60a3 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -3429,7 +3429,8 @@ func (manager *SGuestManager) DeleteExpiredPrepaidServers(ctx context.Context, u return } for i := 0; i < len(guests); i += 1 { - guests[i].StartDeleteGuestTask(ctx, userCred, "", false, true) + // fake delete expired prepaid servers + guests[i].StartDeleteGuestTask(ctx, userCred, "", false, false) } } diff --git a/pkg/compute/models/host_recycle.go b/pkg/compute/models/host_recycle.go index 25388cb347..db3bb52c68 100644 --- a/pkg/compute/models/host_recycle.go +++ b/pkg/compute/models/host_recycle.go @@ -15,10 +15,12 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/baremetal" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/billing" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -65,6 +67,10 @@ func (self *SGuest) PerformPrepaidRecycle(ctx context.Context, userCred mcclient db.OpsLog.LogEvent(self, db.ACT_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred) logclient.AddActionLog(self, logclient.ACT_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred, true) + autoDelete := jsonutils.QueryBoolean(data, "auto_delete", false) + if autoDelete { + self.StartDeleteGuestTask(ctx, userCred, "", false, true) + } return nil, nil } @@ -656,3 +662,63 @@ func (manager *SHostManager) GetHostByRealExternalId(eid string) *SHost { return &host } + +func (self *SHost) AllowPerformRenewPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "renew-prepaid-recycle") +} + +func (self *SHost) PerformRenewPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + durationStr := jsonutils.GetAnyString(data, []string{"duration"}) + if len(durationStr) == 0 { + return nil, httperrors.NewInputParameterError("missing duration") + } + + bc, err := billing.ParseBillingCycle(durationStr) + if err != nil { + return nil, httperrors.NewInputParameterError("invalid duration %s: %s", durationStr, err) + } + + if !GetDriver(HOSTTYPE_HYPERVISOR[self.HostType]).IsSupportedBillingCycle(bc) { + return nil, httperrors.NewInputParameterError("unsupported duration %s", durationStr) + } + + err = self.startPrepaidRecycleHostRenewTask(ctx, userCred, durationStr, "") + if err != nil { + return nil, err + } + + return nil, nil +} + +func (self *SHost) startPrepaidRecycleHostRenewTask(ctx context.Context, userCred mcclient.TokenCredential, duration string, parentTaskId string) error { + data := jsonutils.NewDict() + data.Add(jsonutils.NewString(duration), "duration") + task, err := taskman.TaskManager.NewTask(ctx, "PrepaidRecycleHostRenewTask", self, userCred, data, parentTaskId, "", nil) + if err != nil { + log.Errorf("fail to crate GuestRenewTask %s", err) + return err + } + task.ScheduleRun(nil) + return nil +} + +func (self *SHost) DoSaveRenewInfo(ctx context.Context, userCred mcclient.TokenCredential, bc *billing.SBillingCycle, expireAt *time.Time) error { + _, err := self.GetModelManager().TableSpec().Update(self, func() error { + if self.BillingType != BILLING_TYPE_PREPAID { + self.BillingType = BILLING_TYPE_PREPAID + } + if expireAt != nil && !expireAt.IsZero() { + self.ExpiredAt = *expireAt + } else { + self.BillingCycle = bc.String() + self.ExpiredAt = bc.EndAt(self.ExpiredAt) + } + return nil + }) + if err != nil { + log.Errorf("Update error %s", err) + return err + } + db.OpsLog.LogEvent(self, db.ACT_RENEW, self.GetShortDesc(ctx), userCred) + return nil +} diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 67f7ce4272..60683f1396 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -138,11 +138,11 @@ type SHost struct { CpuMhz int `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True) # cpu MHz CpuCache int `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True) # cpu Cache in KB CpuReserved int8 `nullable:"true" default:"0" list:"admin" update:"admin" create:"admin_optional"` // Column(TINYINT, nullable=True, default=0) - CpuCmtbound float32 `nullable:"true" default:"8.0" list:"admin" update:"admin" create:"admin_optional"` // = Column(Float, nullable=True) + CpuCmtbound float32 `nullable:"true" default:"8" list:"admin" update:"admin" create:"admin_optional"` // = Column(Float, nullable=True) MemSize int `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True) # memory size in MB MemReserved int `nullable:"true" default:"0" list:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True, default=0) # memory reserved in MB - MemCmtbound float32 `nullable:"true" default:"1.0" list:"admin" update:"admin" create:"admin_optional"` // = Column(Float, nullable=True) + MemCmtbound float32 `nullable:"true" default:"1" list:"admin" update:"admin" create:"admin_optional"` // = Column(Float, nullable=True) StorageSize int `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True) # storage size in MB StorageType string `width:"20" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(20, charset='ascii'), nullable=True) diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index 5582bbc752..e3a3fc6e99 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -120,7 +120,7 @@ type SStorage struct { Reserved int `nullable:"true" default:"0" list:"admin" update:"admin"` // Column(Integer, nullable=True, default=0) StorageType string `width:"32" charset:"ascii" nullable:"false" list:"user" update:"admin" create:"admin_required"` // Column(VARCHAR(32, charset='ascii'), nullable=False) MediumType string `width:"32" charset:"ascii" nullable:"false" list:"user" update:"admin" create:"admin_required"` // Column(VARCHAR(32, charset='ascii'), nullable=False) - Cmtbound float32 `nullable:"true" default:"1.0" list:"admin" update:"admin"` // Column(Float, nullable=True) + Cmtbound float32 `nullable:"true" default:"1" list:"admin" update:"admin"` // Column(Float, nullable=True) StorageConf jsonutils.JSONObject `nullable:"true" get:"admin" update:"admin"` // = Column(JSONEncodedDict, nullable=True) ZoneId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"admin_required"` diff --git a/pkg/compute/tasks/guest_delete_task.go b/pkg/compute/tasks/guest_delete_task.go index 8a7746daca..5521851d8d 100644 --- a/pkg/compute/tasks/guest_delete_task.go +++ b/pkg/compute/tasks/guest_delete_task.go @@ -153,8 +153,9 @@ func (self *GuestDeleteTask) OnSyncConfigComplete(ctx context.Context, obj db.IS } func (self *GuestDeleteTask) OnSyncConfigCompleteFailed(ctx context.Context, obj db.IStandaloneModel, err jsonutils.JSONObject) { - guest := obj.(*models.SGuest) - self.OnFailed(ctx, guest, err) + // guest := obj.(*models.SGuest) + // self.OnFailed(ctx, guest, err) + self.OnSyncConfigComplete(ctx, obj, err) // ignore sync config failed error } func (self *GuestDeleteTask) OnGuestDeleteFailed(ctx context.Context, obj db.IStandaloneModel, err jsonutils.JSONObject) { diff --git a/pkg/compute/tasks/guest_renew_task.go b/pkg/compute/tasks/guest_renew_task.go index 7a11b94cc4..4401fa2a26 100644 --- a/pkg/compute/tasks/guest_renew_task.go +++ b/pkg/compute/tasks/guest_renew_task.go @@ -19,6 +19,7 @@ type GuestRenewTask struct { func init() { taskman.RegisterTask(GuestRenewTask{}) + taskman.RegisterTask(PrepaidRecycleHostRenewTask{}) } func (self *GuestRenewTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { @@ -45,3 +46,50 @@ func (self *GuestRenewTask) OnInit(ctx context.Context, obj db.IStandaloneModel, self.SetStageComplete(ctx, nil) } + +type PrepaidRecycleHostRenewTask struct { + taskman.STask +} + +func (self *PrepaidRecycleHostRenewTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + host := obj.(*models.SHost) + + durationStr, _ := self.GetParams().GetString("duration") + bc, _ := billing.ParseBillingCycle(durationStr) + + ihost, err := host.GetIHost() + if err != nil { + msg := fmt.Sprintf("host.GetIHost fail %s", err) + log.Errorf(msg) + self.SetStageFailed(ctx, msg) + return + } + + iVM, err := ihost.GetIVMById(host.RealExternalId) + if err != nil { + msg := fmt.Sprintf("ihost.GetIVMById fail %s", err) + log.Errorf(msg) + self.SetStageFailed(ctx, msg) + return + } + + err = iVM.Renew(bc) + if err != nil { + msg := fmt.Sprintf("iVM.Renew fail %s", err) + log.Errorf(msg) + self.SetStageFailed(ctx, msg) + return + } + + exp := iVM.GetExpiredAt() + + err = host.DoSaveRenewInfo(ctx, self.UserCred, &bc, &exp) + if err != nil { + msg := fmt.Sprintf("SaveRenewInfo fail %s", err) + log.Errorf(msg) + self.SetStageFailed(ctx, msg) + return + } + + self.SetStageComplete(ctx, nil) +} diff --git a/pkg/util/aliyun/aliyun.go b/pkg/util/aliyun/aliyun.go index 507a76615d..bc67531e8f 100644 --- a/pkg/util/aliyun/aliyun.go +++ b/pkg/util/aliyun/aliyun.go @@ -83,7 +83,7 @@ func _jsonRequest(client *sdk.Client, domain string, version string, apiName str resp, err := processCommonRequest(client, req) if err != nil { - log.Errorf("request error %s", err) + log.Errorf("request error %s parameters %s", err, params) return nil, err } body, err := jsonutils.Parse(resp.GetHttpContentBytes()) diff --git a/pkg/util/aliyun/instance.go b/pkg/util/aliyun/instance.go index 2c7f101f1b..89cb8dbf13 100644 --- a/pkg/util/aliyun/instance.go +++ b/pkg/util/aliyun/instance.go @@ -905,7 +905,13 @@ func (self *SInstance) GetBillingType() string { } func (self *SInstance) GetExpiredAt() time.Time { - return self.ExpiredTime + if !self.ExpiredTime.IsZero() { + now := time.Now() + if self.ExpiredTime.Sub(now) < time.Hour*24*365*6 { + return self.ExpiredTime + } + } + return time.Time{} } func (self *SInstance) UpdateUserData(userData string) error { @@ -925,10 +931,10 @@ func (region *SRegion) RenewInstance(instanceId string, bc billing.SBillingCycle params["InstanceId"] = instanceId if bc.GetWeeks() <= 4 { params["PeriodUnit"] = "Week" - params["Period"] = fmt.Sprintf("%s", bc.GetWeeks()) + params["Period"] = fmt.Sprintf("%d", bc.GetWeeks()) } else { params["PeriodUnit"] = "Month" - params["Period"] = fmt.Sprintf("%s", bc.GetMonths()) + params["Period"] = fmt.Sprintf("%d", bc.GetMonths()) } params["ClientToken"] = utils.GenRequestId(20) _, err := region.ecsRequest("RenewInstance", params) diff --git a/pkg/util/aliyun/zone.go b/pkg/util/aliyun/zone.go index bba20ddd28..249554c058 100644 --- a/pkg/util/aliyun/zone.go +++ b/pkg/util/aliyun/zone.go @@ -5,6 +5,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/utils" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" @@ -134,7 +135,7 @@ func (self *SZone) IsEmulated() bool { } func (self *SZone) GetStatus() string { - if len(self.AvailableResourceCreation.ResourceTypes) == 0 { + if len(self.AvailableResourceCreation.ResourceTypes) == 0 || !utils.IsInStringArray("Instance", self.AvailableResourceCreation.ResourceTypes) { return models.ZONE_SOLDOUT } else { return models.ZONE_ENABLE