diff --git a/cmd/climc/shell/compute/servers.go b/cmd/climc/shell/compute/servers.go index b21496aaf3..2b45359a38 100644 --- a/cmd/climc/shell/compute/servers.go +++ b/cmd/climc/shell/compute/servers.go @@ -51,6 +51,7 @@ func init() { cmd.BatchPut(new(options.ServerUpdateOptions)) cmd.GetMetadata(new(options.ServerIdOptions)) cmd.Perform("clone", new(options.ServerCloneOptions)) + cmd.Perform("change-billing-type", new(options.ServerChangeBillingTypeOptions)) cmd.BatchPerform("start", new(options.ServerStartOptions)) cmd.BatchPerform("syncstatus", new(options.ServerIdsOptions)) cmd.BatchPerform("sync", new(options.ServerIdsOptions)) diff --git a/go.mod b/go.mod index 9a0b43bdc6..e3a43e173c 100644 --- a/go.mod +++ b/go.mod @@ -87,7 +87,7 @@ require ( k8s.io/client-go v0.19.3 k8s.io/cluster-bootstrap v0.19.3 moul.io/http2curl/v2 v2.3.0 - yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250212024113-8e46641f76b2 + yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250214014137-798f62b12a5f yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256 yunion.io/x/jsonutils v1.0.1-0.20240930100528-1671a2d0d22f yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91 diff --git a/go.sum b/go.sum index b0fa93e7ba..59a2df7a96 100644 --- a/go.sum +++ b/go.sum @@ -1274,8 +1274,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250212024113-8e46641f76b2 h1:v6icJ6xiuEWUjTLECzUtwB/Yc9EjrvaQuB+oHNyRD2I= -yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250212024113-8e46641f76b2/go.mod h1:KQ/jWx7bZlmjCE711KEWuvHW/dzpdr/UTlBjjutkj0Y= +yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250214014137-798f62b12a5f h1:UMh7Guhl+M7Rdj+vKEkIyqM2j0rg620YPv/74Bkh/Sc= +yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250214014137-798f62b12a5f/go.mod h1:KQ/jWx7bZlmjCE711KEWuvHW/dzpdr/UTlBjjutkj0Y= yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256 h1:kLKQ6zbgPDQflRwoHFAjxNChcbhXIFgsUVLkJwiXu/8= yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws= yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634= diff --git a/pkg/apis/compute/guest_const.go b/pkg/apis/compute/guest_const.go index 849f166780..421e5dc5ab 100644 --- a/pkg/apis/compute/guest_const.go +++ b/pkg/apis/compute/guest_const.go @@ -178,6 +178,10 @@ const ( VM_QGA_SET_NETWORK = "qga_set_network" VM_QGA_SET_NETWORK_FAILED = "qga_set_network_failed" + // 更改计费模式 + VM_CHANGE_BILLING_TYPE = "change_billing_type" + VM_CHANGE_BILLING_TYPE_FAILED = "change_billing_type_failed" + SHUTDOWN_STOP = "stop" SHUTDOWN_TERMINATE = "terminate" diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index 6fb8319518..68b024cde5 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -624,6 +624,7 @@ type ServerStopInput struct { TimeoutSecs int `json:"timeout_secs"` // 是否关机停止计费, 若平台不支持停止计费,此参数无作用 + // 若包年包月机器关机设置此参数,则先转换计费模式到按量计费,再关机不收费 // 目前仅阿里云,腾讯云此参数生效 StopCharging bool `json:"stop_charging"` } @@ -1247,6 +1248,8 @@ type GuestPerformStartInput struct { // 指定启动虚拟机的Qemu版本,可选值:2.12.1, 4.2.0 // 仅适用于KVM虚拟机 QemuVersion string `json:"qemu_version"` + // 按量机器自动转换为包年包月 + AutoPrepaid bool `json:"auto_prepaid"` } type ServerSetOSInfoInput struct { @@ -1350,3 +1353,10 @@ func (conf ServerChangeConfigSettings) AddedDisk() int { } return size } + +type ServerChangeBillingTypeInput struct { + // 仅在虚拟机开机或关机状态下调用 + // enmu: [postpaid, prepaid] + // required: true + BillingType string `json:"billing_type"` +} diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index 866afa5dec..8e3428422c 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -125,6 +125,10 @@ func (drv *SBaseGuestDriver) RequestDetachDisk(ctx context.Context, guest *model return nil } +func (drv *SBaseGuestDriver) RequestChangeBillingType(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { + return errors.Wrapf(cloudprovider.ErrNotImplemented, "RequestChangeBillingType") +} + func (drv *SBaseGuestDriver) RequestAttachDisk(ctx context.Context, guest *models.SGuest, disk *models.SDisk, task taskman.ITask) error { task.ScheduleRun(nil) return nil diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index b9ae89c3c7..1782c8a5ba 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -475,6 +475,14 @@ func (drv *SManagedVirtualizedGuestDriver) RequestStartOnHost(ctx context.Contex result := jsonutils.NewDict() if ivm.GetStatus() != api.VM_RUNNING { + + if guest.BillingType == billing_api.BILLING_TYPE_POSTPAID && jsonutils.QueryBoolean(task.GetParams(), "auto_prepaid", false) { + err = ivm.ChangeBillingType(billing_api.BILLING_TYPE_PREPAID) + if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented { + logclient.AddSimpleActionLog(guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, errors.Wrapf(err, billing_api.BILLING_TYPE_PREPAID), userCred, false) + } + } + err := ivm.StartVM(ctx) if err != nil { return errors.Wrapf(err, "StartVM") @@ -936,6 +944,15 @@ func (drv *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Context if ivm.GetStatus() != api.VM_READY { opts := &cloudprovider.ServerStopOptions{} task.GetParams().Unmarshal(opts) + + // 包年包月实例关机不收费,先转按量付费再关机 + if opts.StopCharging && guest.BillingType == billing_api.BILLING_TYPE_PREPAID { + err = ivm.ChangeBillingType(billing_api.BILLING_TYPE_POSTPAID) + if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented { + logclient.AddSimpleActionLog(guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, errors.Wrapf(err, billing_api.BILLING_TYPE_POSTPAID), task.GetUserCred(), false) + } + } + err = ivm.StopVM(ctx, opts) if err != nil { return nil, errors.Wrapf(err, "ivm.StopVM") @@ -952,6 +969,51 @@ func (drv *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Context return nil } +func (drv *SManagedVirtualizedGuestDriver) RequestChangeBillingType(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + ivm, err := guest.GetIVM(ctx) + if err != nil { + return nil, errors.Wrapf(err, "guest.GetIVM") + } + billType := "" + switch guest.BillingType { + case billing_api.BILLING_TYPE_POSTPAID: + billType = billing_api.BILLING_TYPE_PREPAID + case billing_api.BILLING_TYPE_PREPAID: + billType = billing_api.BILLING_TYPE_POSTPAID + } + err = ivm.ChangeBillingType(billType) + if err != nil { + return nil, errors.Wrapf(err, "ChangeBillingType") + } + err = cloudprovider.Wait(time.Second*5, time.Minute*3, func() (bool, error) { + err = ivm.Refresh() + if err != nil { + return false, err + } + if ivm.GetBillingType() != billType { + return false, nil + } + return true, nil + }) + if err != nil { + return nil, errors.Wrapf(err, "Wait vm billing type changed") + } + _, err = db.Update(guest, func() error { + guest.BillingType = ivm.GetBillingType() + guest.Status = ivm.GetStatus() + guest.ExpiredAt = time.Time{} + if guest.BillingType == billing_api.BILLING_TYPE_PREPAID { + guest.AutoRenew = ivm.IsAutoRenew() + guest.ExpiredAt = ivm.GetExpiredAt() + } + return nil + }) + return nil, err + }) + return nil +} + func (drv *SManagedVirtualizedGuestDriver) RequestSyncstatusOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error { taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { ihost, err := host.GetIHost(ctx) diff --git a/pkg/compute/models/billingresource.go b/pkg/compute/models/billingresource.go index c09eb880d8..f8565b1645 100644 --- a/pkg/compute/models/billingresource.go +++ b/pkg/compute/models/billingresource.go @@ -28,6 +28,7 @@ import ( "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/billing" + billing_api "yunion.io/x/onecloud/pkg/apis/billing" notifyapi "yunion.io/x/onecloud/pkg/apis/notify" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" @@ -247,7 +248,21 @@ func fetchExpiredModels(manager db.IModelManager, advanceDay int) ([]IBillingMod upLimit := time.Now().AddDate(0, 0, advanceDay+1) downLimit := time.Now().AddDate(0, 0, advanceDay) v := reflect.MakeSlice(reflect.SliceOf(manager.TableSpec().DataType()), 0, 0) - q := manager.Query().LE("expired_at", upLimit).GE("expired_at", downLimit) + q := manager.Query() + q = q.Filter( + sqlchemy.OR( + sqlchemy.AND( + sqlchemy.Equals(q.Field("billing_type"), billing_api.BILLING_TYPE_POSTPAID), + sqlchemy.LE(q.Field("expired_at"), upLimit), + sqlchemy.GE(q.Field("expired_at"), downLimit), + ), + // 跳过自动续费实例 + sqlchemy.AND( + sqlchemy.Equals(q.Field("billing_type"), billing_api.BILLING_TYPE_PREPAID), + sqlchemy.LE(q.Field("expired_at"), upLimit), + sqlchemy.GE(q.Field("expired_at"), downLimit), + sqlchemy.IsFalse(q.Field("auto_renew")), + ))) vp := reflect.New(v.Type()) vp.Elem().Set(v) diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 5d258114d8..3d4632c2df 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -6238,3 +6238,29 @@ func (g *SGuest) PerformSetRootDiskMatcher(ctx context.Context, userCred mcclien } return nil, nil } + +func (g *SGuest) PerformChangeBillingType(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ServerChangeBillingTypeInput) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(g.Status, []string{api.VM_RUNNING, api.VM_READY}) { + return nil, httperrors.NewServerStatusError("Cannot change guest billing type in status %s", g.Status) + } + if len(input.BillingType) == 0 { + return nil, httperrors.NewMissingParameterError("billing_type") + } + if !utils.IsInStringArray(input.BillingType, []string{billing_api.BILLING_TYPE_POSTPAID, billing_api.BILLING_TYPE_PREPAID}) { + return nil, httperrors.NewInputParameterError("invalid billing_type %s", input.BillingType) + } + if g.BillingType == input.BillingType { + return nil, nil + } + return nil, g.StartChangeBillingTypeTask(ctx, userCred, "") +} + +func (self *SGuest) StartChangeBillingTypeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { + self.SetStatus(ctx, userCred, api.VM_CHANGE_BILLING_TYPE, "") + kwargs := jsonutils.NewDict() + task, err := taskman.TaskManager.NewTask(ctx, "GuestChangeBillingTypeTask", self, userCred, kwargs, parentTaskId, "", nil) + if err != nil { + return err + } + return task.ScheduleRun(nil) +} diff --git a/pkg/compute/models/guestdrivers.go b/pkg/compute/models/guestdrivers.go index 426d67229d..1d9082a5f5 100644 --- a/pkg/compute/models/guestdrivers.go +++ b/pkg/compute/models/guestdrivers.go @@ -112,6 +112,7 @@ type IGuestDriver interface { RequestStartOnHost(ctx context.Context, guest *SGuest, host *SHost, userCred mcclient.TokenCredential, task taskman.ITask) error RequestStopOnHost(ctx context.Context, guest *SGuest, host *SHost, task taskman.ITask, syncStatus bool) error + RequestChangeBillingType(ctx context.Context, guest *SGuest, task taskman.ITask) error StartDeleteGuestTask(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, params *jsonutils.JSONDict, parentTaskId string) error diff --git a/pkg/compute/tasks/guest_change_billing_type_task.go b/pkg/compute/tasks/guest_change_billing_type_task.go new file mode 100644 index 0000000000..26b74f565e --- /dev/null +++ b/pkg/compute/tasks/guest_change_billing_type_task.go @@ -0,0 +1,56 @@ +// 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" + + 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 GuestChangeBillingTypeTask struct { + SGuestBaseTask +} + +func init() { + taskman.RegisterTask(GuestChangeBillingTypeTask{}) +} + +func (self *GuestChangeBillingTypeTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + self.SetStage("OnGuestChangeBillingTypeTaskComplete", nil) + drv := guest.GetDriver() + err := drv.RequestChangeBillingType(ctx, guest, self) + if err != nil { + self.OnGuestChangeBillingTypeTaskCompleteFailed(ctx, guest, jsonutils.NewString(err.Error())) + } +} + +func (self *GuestChangeBillingTypeTask) OnGuestChangeBillingTypeTaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, guest.BillingType, self.UserCred, false) + self.SetStageComplete(ctx, nil) +} + +func (self *GuestChangeBillingTypeTask) OnGuestChangeBillingTypeTaskCompleteFailed(ctx context.Context, guest *models.SGuest, reason jsonutils.JSONObject) { + guest.SetStatus(ctx, self.GetUserCred(), api.VM_CHANGE_BILLING_TYPE_FAILED, "") + logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, reason.String(), self.UserCred, false) + self.SetStageFailed(ctx, reason) +} diff --git a/pkg/mcclient/options/compute/servers.go b/pkg/mcclient/options/compute/servers.go index 6cc282655a..e1f1f1a81d 100644 --- a/pkg/mcclient/options/compute/servers.go +++ b/pkg/mcclient/options/compute/servers.go @@ -125,6 +125,7 @@ type ServerStartOptions struct { ServerIdsOptions QemuVersion string `help:"prefer qemu version" json:"qemu_version"` + AutoPrepaid *bool `help:"Auto convert postpaid to prepaid"` } func (o *ServerStartOptions) Params() (jsonutils.JSONObject, error) { @@ -1522,3 +1523,12 @@ func (o *ServerSetRootDiskMatcher) Params() (jsonutils.JSONObject, error) { } return jsonutils.Marshal(matcher), nil } + +type ServerChangeBillingTypeOptions struct { + ServerIdOptions + BillingType string `choices:"prepaid|postpaid"` +} + +func (o *ServerChangeBillingTypeOptions) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(map[string]string{"billing_type": o.BillingType}), nil +} diff --git a/pkg/util/logclient/consts.go b/pkg/util/logclient/consts.go index bffaebef4c..69377438a0 100644 --- a/pkg/util/logclient/consts.go +++ b/pkg/util/logclient/consts.go @@ -73,6 +73,7 @@ const ( ACT_VM_SETSECGROUP = "vm_setsecgroup" ACT_VM_CPUSET = "vm_cpuset" ACT_VM_CPUSET_REMOVE = "vm_cpuset_remove" + ACT_VM_CHANGE_BILLING_TYPE = "vm_change_billing_type" ACT_RESET_DISK = "reset_disk" ACT_SYNC_STATUS = "sync_status" ACT_SYNC_CONF = "sync_conf" diff --git a/pkg/util/logclient/consts_i18n.go b/pkg/util/logclient/consts_i18n.go index 6073104f5a..6fced7f036 100644 --- a/pkg/util/logclient/consts_i18n.go +++ b/pkg/util/logclient/consts_i18n.go @@ -1533,4 +1533,8 @@ func init() { EN("Set Commit Bound"). CN("设置超售比"), ) + o.Set(ACT_VM_CHANGE_BILLING_TYPE, i18n.NewTableEntry(). + EN("Change Billing Type"). + CN("更改计费模式"), + ) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 0737c50eeb..eb6dcdb08d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1576,7 +1576,7 @@ sigs.k8s.io/structured-merge-diff/v4/value # sigs.k8s.io/yaml v1.2.0 ## explicit; go 1.12 sigs.k8s.io/yaml -# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250212024113-8e46641f76b2 +# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250214014137-798f62b12a5f ## explicit; go 1.21 yunion.io/x/cloudmux/pkg/apis yunion.io/x/cloudmux/pkg/apis/billing diff --git a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go index 3b411a559c..0550882306 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go +++ b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go @@ -63,6 +63,7 @@ type IBillingResource interface { GetExpiredAt() time.Time SetAutoRenew(bc billing.SBillingCycle) error Renew(bc billing.SBillingCycle) error + ChangeBillingType(billType string) error IsAutoRenew() bool } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/instance.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/instance.go index 1d79eca2f2..2a0a594e5c 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/instance.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/instance.go @@ -995,6 +995,43 @@ func (self *SInstance) GetBillingType() string { return convertChargeType(self.InstanceChargeType) } +func (self *SInstance) ChangeBillingType(billingType string) error { + return self.host.zone.region.ModifyInstanceChargeType(self.InstanceId, billingType) +} + +func (region *SRegion) ModifyInstanceChargeType(vmId string, billingType string) error { + params := map[string]string{ + "RegionId": region.RegionId, + "IncludeDataDisks": "true", + "AutoPay": "true", + "ClientToken": utils.GenRequestId(20), + "InstanceIds": jsonutils.Marshal([]string{vmId}).String(), + } + switch billingType { + case billing_api.BILLING_TYPE_POSTPAID: + params["InstanceChargeType"] = "PostPaid" + case billing_api.BILLING_TYPE_PREPAID: + params["InstanceChargeType"] = "PrePaid" + params["Period"] = "1" + params["PeriodUnit"] = "Month" + default: + return fmt.Errorf("invalid billing_type %s", billingType) + } + _, err := region.ecsRequest("ModifyInstanceChargeType", params) + if err != nil { + return err + } + if billingType == billing_api.BILLING_TYPE_PREPAID { + cycle := billing.SBillingCycle{ + AutoRenew: true, + Count: 1, + Unit: billing.BillingCycleMonth, + } + region.SetInstanceAutoRenew(vmId, cycle) + } + return nil +} + func (self *SInstance) GetCreatedAt() time.Time { return self.CreationTime } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/billing_base.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/billing_base.go index b8dc0402d2..e02b9c3e76 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/billing_base.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/billing_base.go @@ -44,3 +44,7 @@ func (self *SBillingBase) IsAutoRenew() bool { func (self *SBillingBase) Renew(bc billing.SBillingCycle) error { return errors.Wrap(cloudprovider.ErrNotImplemented, "Renew") } + +func (self *SBillingBase) ChangeBillingType(billType string) error { + return errors.Wrap(cloudprovider.ErrNotImplemented, "ChangeBillingType") +} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/hcso/modelarts.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/hcso/modelarts.go index 92c02094db..5104b717ac 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/hcso/modelarts.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/hcso/modelarts.go @@ -32,6 +32,7 @@ import ( type SModelartsPool struct { region *SRegion multicloud.SResourceBase + multicloud.SBillingBase Metadata SModelartsPoolMetadata `json:"metadata"` Spec SModelartsPoolSpec `json:"spec"` diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/huawei/modelarts_pool.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/huawei/modelarts_pool.go index 014b4be60d..e03e7945f5 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/huawei/modelarts_pool.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/huawei/modelarts_pool.go @@ -34,6 +34,7 @@ import ( type SModelartsPool struct { region *SRegion multicloud.SResourceBase + multicloud.SBillingBase Metadata SModelartsPoolMetadata `json:"metadata"` Spec SModelartsPoolSpec `json:"spec"` diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/instance.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/instance.go index 106c5484a5..3390ff75ef 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/instance.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/instance.go @@ -911,6 +911,30 @@ func (self *SInstance) GetBillingType() string { } } +func (self *SInstance) ChangeBillingType(billingType string) error { + return self.host.zone.region.ModifyInstanceChargeType(self.InstanceId, billingType) +} + +func (region *SRegion) ModifyInstanceChargeType(vmId string, billingType string) error { + params := map[string]string{ + "Region": region.Region, + "ModifyPortableDataDisk": "true", + "InstanceIds.0": vmId, + } + switch billingType { + case billing_api.BILLING_TYPE_POSTPAID: + params["InstanceChargeType"] = "POSTPAID_BY_HOUR" + case billing_api.BILLING_TYPE_PREPAID: + params["InstanceChargeType"] = "PREPAID" + params["InstanceChargePrepaid.Period"] = "1" + params["InstanceChargePrepaid.RenewFlag"] = "NOTIFY_AND_AUTO_RENEW" + default: + return fmt.Errorf("invalid billing_type %s", billingType) + } + _, err := region.cvmRequest("ModifyInstancesChargeType", params, false) + return err +} + func (self *SInstance) GetCreatedAt() time.Time { return self.CreatedTime }