diff --git a/cmd/climc/shell/events.go b/cmd/climc/shell/events.go index 85d099f2e7..974f33b3ca 100644 --- a/cmd/climc/shell/events.go +++ b/cmd/climc/shell/events.go @@ -15,6 +15,7 @@ type BaseEventListOptions struct { Offset int64 `help:"Offset"` Ascending bool `help:"Ascending order"` Descending bool `help:"Descending order"` + OrderBy string `help:"order by specific field"` Action []string `help:"Log action"` } @@ -62,6 +63,9 @@ func doEventList(man modules.ResourceManager, s *mcclient.ClientSession, args *E } else if !args.Ascending && args.Descending { params.Add(jsonutils.NewString("desc"), "order") } + if len(args.OrderBy) > 0 { + params.Add(jsonutils.NewString(args.OrderBy), "order_by") + } if len(args.Action) > 0 { params.Add(jsonutils.NewStringArray(args.Action), "action") } 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/cmd/climc/shell/zones.go b/cmd/climc/shell/zones.go index 7430f05eab..000bc97a02 100644 --- a/cmd/climc/shell/zones.go +++ b/cmd/climc/shell/zones.go @@ -126,4 +126,21 @@ func init() { return nil }) + type ZoneStatusOptions struct { + ID string `help:"ID or name of zone"` + STATUS string `help:"zone status" choices:"enable|disable|soldout"` + REASON string `help:"why update status"` + } + R(&ZoneStatusOptions{}, "zone-update-status", "Update zone status", func(s *mcclient.ClientSession, args *ZoneStatusOptions) error { + params := jsonutils.NewDict() + params.Set("status", jsonutils.NewString(args.STATUS)) + params.Set("reason", jsonutils.NewString(args.REASON)) + result, err := modules.Zones.PerformAction(s, args.ID, "status", params) + if err != nil { + return err + } + printObject(result) + return nil + }) + } diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index b487df6d58..074dcd0761 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -447,7 +447,6 @@ func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok limit = maxLimit } orderBy := jsonutils.GetQueryStringArray(queryDict, "order_by") - order := sqlchemy.SQL_ORDER_DESC if len(orderBy) == 0 { colSpec := manager.TableSpec().ColumnSpec("id") if err == nil && colSpec != nil && colSpec.IsNumeric() { @@ -455,11 +454,11 @@ func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok } else { orderBy = []string{"created_at"} } - } else { - orderStr, _ := queryDict.GetString("order") - if orderStr == "asc" { - order = sqlchemy.SQL_ORDER_ASC - } + } + order := sqlchemy.SQL_ORDER_DESC + orderStr, _ := queryDict.GetString("order") + if orderStr == "asc" { + order = sqlchemy.SQL_ORDER_ASC } if order == sqlchemy.SQL_ORDER_ASC { for _, orderByField := range orderBy { diff --git a/pkg/cloudcommon/policy/defaults.go b/pkg/cloudcommon/policy/defaults.go index 93c43befbe..1d4a96f99f 100644 --- a/pkg/cloudcommon/policy/defaults.go +++ b/pkg/cloudcommon/policy/defaults.go @@ -9,6 +9,12 @@ var ( Action: PolicyActionPerform, Result: rbacutils.UserAllow, }, + { + Service: "compute", + Resource: "hosts", + Action: PolicyActionList, + Result: rbacutils.UserAllow, + }, { Service: "compute", Resource: "zones", diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index c3515f8c06..7fc6802013 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -63,6 +63,14 @@ func (self *SBaseGuestDriver) OnGuestCreateTaskComplete(ctx context.Context, gue if err == nil && guest.ExpiredAt.IsZero() { guest.SaveRenewInfo(ctx, task.GetUserCred(), &bc, nil) } + if jsonutils.QueryBoolean(task.GetParams(), "auto_prepaid_recycle", false) { + err := guest.CanPerformPrepaidRecycle() + if err == nil { + task.SetStageComplete(ctx, nil) + guest.DoPerformPrepaidRecycle(ctx, task.GetUserCred(), true) + return nil + } + } } if jsonutils.QueryBoolean(task.GetParams(), "auto_start", false) { task.SetStage("on_auto_start_guest", nil) diff --git a/pkg/compute/models/capabilities.go b/pkg/compute/models/capabilities.go index 950adde07d..d4b0c9d526 100644 --- a/pkg/compute/models/capabilities.go +++ b/pkg/compute/models/capabilities.go @@ -52,7 +52,7 @@ func getHypervisors(zone *SZone) []string { q = q.Equals("zone_id", zone.Id) } q = q.IsNotEmpty("host_type").IsNotNull("host_type") - q = q.Equals("host_status", HOST_ONLINE) + // q = q.Equals("host_status", HOST_ONLINE) q = q.IsTrue("enabled") q = q.Distinct() rows, err := q.Rows() 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..a51321f8d7 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" ) @@ -56,7 +58,11 @@ func (self *SGuest) PerformPrepaidRecycle(ctx context.Context, userCred mcclient return nil, httperrors.NewInvalidStatusError(err.Error()) } - err = self.doPrepaidRecycle(ctx, userCred) + return self.DoPerformPrepaidRecycle(ctx, userCred, jsonutils.QueryBoolean(data, "auto_delete", false)) +} + +func (self *SGuest) DoPerformPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential, autoDelete bool) (jsonutils.JSONObject, error) { + err := self.doPrepaidRecycle(ctx, userCred) if err != nil { logclient.AddActionLog(self, logclient.ACT_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred, false) return nil, httperrors.NewGeneralError(err) @@ -65,6 +71,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) + if autoDelete { + self.StartDeleteGuestTask(ctx, userCred, "", false, true) + } + return nil, nil } @@ -656,3 +666,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..d77ddfb971 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) + 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" 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..c2901beac0 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/models/zones.go b/pkg/compute/models/zones.go index df25512179..7d99351597 100644 --- a/pkg/compute/models/zones.go +++ b/pkg/compute/models/zones.go @@ -20,7 +20,7 @@ const ( ZONE_ENABLE = "enable" ZONE_DISABLE = "disable" ZONE_SOLDOUT = "soldout" - ZONE_LACK = "lack" + // ZONE_LACK = "lack" ) type SZoneManager struct { @@ -469,6 +469,12 @@ func (manager *SZoneManager) InitializeData() error { return nil }) } + if z.Status == "init" { + manager.TableSpec().Update(&z, func() error { + z.Status = ZONE_ENABLE + return nil + }) + } } return nil } @@ -529,6 +535,21 @@ func (manager *SZoneManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQu sqlchemy.In(q.Field("id"), sq.SubQuery()), sqlchemy.In(q.Field("id"), sq2.SubQuery()), )) + q = q.Equals("status", ZONE_ENABLE) + } + managerStr, _ := query.GetString("manager") + if len(managerStr) > 0 { + provider := CloudproviderManager.FetchCloudproviderByIdOrName(managerStr) + if provider == nil { + return nil, httperrors.NewResourceNotFoundError("Cloud provider/manager %s not found", managerStr) + } + subq := CloudregionManager.Query("id").Equals("provider", provider.Provider).SubQuery() + q = q.In("cloudregion_id", subq) + } + providerStr, _ := query.GetString("provider") + if len(providerStr) > 0 { + subq := CloudregionManager.Query("id").Equals("provider", providerStr).SubQuery() + q = q.In("cloudregion_id", subq) } return q, nil } @@ -595,5 +616,6 @@ func (manager *SZoneManager) ValidateCreateData(ctx context.Context, userCred mc regionId = "default" } data.Add(jsonutils.NewString(regionId), "cloudregion_id") + data.Set("status", jsonutils.NewString(ZONE_ENABLE)) return manager.SStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data) } diff --git a/pkg/compute/tasks/guest_create_task.go b/pkg/compute/tasks/guest_create_task.go index 32cd45facc..30597090a2 100644 --- a/pkg/compute/tasks/guest_create_task.go +++ b/pkg/compute/tasks/guest_create_task.go @@ -7,6 +7,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" 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/mcclient/options/servers.go b/pkg/mcclient/options/servers.go index dacbdfe44c..6fd0d8b643 100644 --- a/pkg/mcclient/options/servers.go +++ b/pkg/mcclient/options/servers.go @@ -94,8 +94,9 @@ func ParseServerDeployInfoList(list []string) (*jsonutils.JSONDict, error) { type ServerCreateOptions struct { ScheduleOptions - NAME string `help:"Name of server" json:"-"` - MEMSPEC string `help:"Memory size Or Instance Type" metavar:"MEMSPEC" json:"-"` + NAME string `help:"Name of server" json:"-"` + MEMSPEC string `help:"Memory size Or Instance Type" metavar:"MEMSPEC" json:"-"` + Disk []string `help:"Disk descriptions" nargs:"+"` Net []string `help:"Network descriptions" metavar:"NETWORK"` IsolatedDevice []string `help:"Isolated device model or ID" metavar:"ISOLATED_DEVICE"` @@ -127,6 +128,8 @@ type ServerCreateOptions struct { Duration string `help:"valid duration of the server, e.g. 1H, 1D, 1W, 1M, 1Y, ADMIN ONLY option"` + AutoPrepaidRecycle bool `help:"automatically enable prepaid recycling after server is created successfully" json:",omitfalse"` + GenerateName bool `help:"name is generated by pattern" json:"-"` } 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