From 11c3f6a6ef1e5e7023e0e6158ad2c18c9381ee63 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Tue, 17 Nov 2020 16:45:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=E5=85=B3=E6=9C=BA?= =?UTF-8?q?=E4=B8=8D=E6=94=B6=E8=B4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/apis/compute/guests.go | 9 +++++++++ pkg/cloudprovider/instance.go | 5 +++++ pkg/cloudprovider/resources.go | 2 +- pkg/compute/guestdrivers/baremetals.go | 2 +- pkg/compute/guestdrivers/managedvirtual.go | 9 ++++++--- pkg/compute/guestdrivers/openstack.go | 5 ++++- pkg/compute/guestdrivers/virtualization.go | 2 +- pkg/compute/models/guest_actions.go | 17 +++++++---------- pkg/compute/models/hosts.go | 4 +++- pkg/compute/tasks/guest_deploy_task.go | 2 +- pkg/compute/tasks/guest_rebuild_root_task.go | 2 +- pkg/compute/tasks/guest_reset_task.go | 4 ++-- pkg/compute/tasks/guest_save_image_task.go | 2 +- pkg/hostman/storageman/storage_agent.go | 5 ++++- pkg/mcclient/options/servers.go | 5 +++-- pkg/multicloud/aliyun/instance.go | 13 ++++++++----- pkg/multicloud/aliyun/shell/instance.go | 7 ++++--- pkg/multicloud/aws/instance.go | 4 ++-- pkg/multicloud/azure/classic_instance.go | 4 ++-- pkg/multicloud/azure/instance.go | 14 ++++++++++---- pkg/multicloud/ctyun/instance.go | 2 +- pkg/multicloud/esxi/virtualmachine.go | 4 ++-- pkg/multicloud/google/instance.go | 2 +- pkg/multicloud/huawei/instance.go | 4 ++-- pkg/multicloud/openstack/instance.go | 4 ++-- pkg/multicloud/qcloud/instance.go | 20 +++++++++++++------- pkg/multicloud/qcloud/shell/instance.go | 12 +++++++++--- pkg/multicloud/ucloud/instance.go | 2 +- pkg/multicloud/zstack/instance.go | 4 ++-- 29 files changed, 108 insertions(+), 63 deletions(-) diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index 8f1d0ec590..c3dc465fba 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -438,3 +438,12 @@ type ServerResetInput struct { // 自动启动 AutoStart *bool `json:"auto_start"` } + +type ServerStopInput struct { + // 是否强制关机 + IsForce bool `json:"is_force"` + + // 是否关机停止计费, 若平台不支持停止计费,此参数无作用 + // 目前仅阿里云,腾讯云此参数生效 + StopCharging bool `json:"stop_charging"` +} diff --git a/pkg/cloudprovider/instance.go b/pkg/cloudprovider/instance.go index 862ba6264f..699328b2b7 100644 --- a/pkg/cloudprovider/instance.go +++ b/pkg/cloudprovider/instance.go @@ -46,6 +46,11 @@ type SPublicIpInfo struct { PublicIpChargeType TElasticipChargeType } +type ServerStopOptions struct { + IsForce bool + StopCharging bool +} + type SManagedVMCreateConfig struct { Name string ExternalImageId string diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 65b6353d27..badd70ee6f 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -293,7 +293,7 @@ type ICloudVM interface { // GetSecurityGroup() ICloudSecurityGroup StartVM(ctx context.Context) error - StopVM(ctx context.Context, isForce bool) error + StopVM(ctx context.Context, opts *ServerStopOptions) error DeleteVM(ctx context.Context) error UpdateVM(ctx context.Context, name string) error diff --git a/pkg/compute/guestdrivers/baremetals.go b/pkg/compute/guestdrivers/baremetals.go index d1c2d4fb9f..7165c0dea0 100644 --- a/pkg/compute/guestdrivers/baremetals.go +++ b/pkg/compute/guestdrivers/baremetals.go @@ -322,7 +322,7 @@ func (self *SBaremetalGuestDriver) RequestStopGuestForDelete(ctx context.Context !guest.PendingDeleted && !overridePendingDelete && !purge { - return guest.StartGuestStopTask(ctx, task.GetUserCred(), true, task.GetTaskId()) + return guest.StartGuestStopTask(ctx, task.GetUserCred(), true, false, task.GetTaskId()) } if host != nil && !host.GetEnabled() && !purge { return fmt.Errorf("fail to contact baremetal") diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index 5b0830312d..7a9b7a2860 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -706,13 +706,16 @@ func (self *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Contex taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { ihost, err := host.GetIHost() if err != nil { - return nil, err + return nil, errors.Wrapf(err, "host.GetIHost") } ivm, err := ihost.GetIVMById(guest.ExternalId) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "ihost.GetIVMById") } - err = ivm.StopVM(ctx, true) + opts := &cloudprovider.ServerStopOptions{} + task.GetParams().Unmarshal(&opts) + + err = ivm.StopVM(ctx, opts) return nil, err }) return nil diff --git a/pkg/compute/guestdrivers/openstack.go b/pkg/compute/guestdrivers/openstack.go index 208fa4bf2f..ccab167ee2 100644 --- a/pkg/compute/guestdrivers/openstack.go +++ b/pkg/compute/guestdrivers/openstack.go @@ -268,7 +268,10 @@ func (self *SOpenStackGuestDriver) RemoteDeployGuestForRebuildRoot(ctx context.C log.Debugf("VMrebuildRoot %s new instance, wait status %s ...", iVM.GetGlobalId(), initialState) cloudprovider.WaitStatus(iVM, initialState, time.Second*5, time.Second*1800) - iVM.StopVM(ctx, true) + opts := &cloudprovider.ServerStopOptions{ + IsForce: true, + } + iVM.StopVM(ctx, opts) iDisks, err = iVM.GetIDisks() if err != nil { diff --git a/pkg/compute/guestdrivers/virtualization.go b/pkg/compute/guestdrivers/virtualization.go index 51fbb4d951..89a86026aa 100644 --- a/pkg/compute/guestdrivers/virtualization.go +++ b/pkg/compute/guestdrivers/virtualization.go @@ -226,7 +226,7 @@ func (self *SVirtualizedGuestDriver) RequestStopGuestForDelete(ctx context.Conte host = guest.GetHost() } if host != nil && host.GetEnabled() && host.HostStatus == api.HOST_ONLINE { - return guest.StartGuestStopTask(ctx, task.GetUserCred(), true, task.GetTaskId()) + return guest.StartGuestStopTask(ctx, task.GetUserCred(), true, false, task.GetTaskId()) } if host != nil && !jsonutils.QueryBoolean(task.GetParams(), "purge", false) { return fmt.Errorf("fail to contact host") diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 11ea57f45e..37ee6b8336 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -902,14 +902,13 @@ func (self *SGuest) NotifyAdminServerEvent(ctx context.Context, event string, pr notifyclient.SystemNotifyWithCtx(ctx, priority, event, kwargs) } -func (self *SGuest) StartGuestStopTask(ctx context.Context, userCred mcclient.TokenCredential, isForce bool, parentTaskId string) error { +func (self *SGuest) StartGuestStopTask(ctx context.Context, userCred mcclient.TokenCredential, isForce, stopCharging bool, parentTaskId string) error { if len(parentTaskId) == 0 { self.SetStatus(userCred, api.VM_START_STOP, "") } params := jsonutils.NewDict() - if isForce { - params.Add(jsonutils.JSONTrue, "is_force") - } + params.Add(jsonutils.NewBool(isForce), "is_force") + params.Add(jsonutils.NewBool(stopCharging), "stop_charging") if len(parentTaskId) > 0 { params.Add(jsonutils.JSONTrue, "subtask") } @@ -2786,14 +2785,12 @@ func (self *SGuest) AllowPerformStop(ctx context.Context, } func (self *SGuest) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, - data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + input api.ServerStopInput) (jsonutils.JSONObject, error) { // XXX if is force, force stop guest - var isForce = jsonutils.QueryBoolean(data, "is_force", false) - if isForce || utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_STOP_FAILED}) { - return nil, self.StartGuestStopTask(ctx, userCred, isForce, "") - } else { - return nil, httperrors.NewInvalidStatusError("Cannot stop server in status %s", self.Status) + if input.IsForce || utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_STOP_FAILED}) { + return nil, self.StartGuestStopTask(ctx, userCred, input.IsForce, input.StopCharging, "") } + return nil, httperrors.NewInvalidStatusError("Cannot stop server in status %s", self.Status) } func (self *SGuest) AllowPerformRestart(ctx context.Context, diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index b2dd1f1524..26f7e51ecc 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -3585,7 +3585,9 @@ func (self *SHost) PerformStop(ctx context.Context, userCred mcclient.TokenCrede return nil, self.InitializedGuestStop(ctx, userCred, guest) } self.SetStatus(userCred, api.BAREMETAL_START_MAINTAIN, "") - return guest.PerformStop(ctx, userCred, query, data) + input := api.ServerStopInput{} + data.Unmarshal(&input) + return guest.PerformStop(ctx, userCred, query, input) } } return nil, self.StartBaremetalUnmaintenanceTask(ctx, userCred, false, "stop") diff --git a/pkg/compute/tasks/guest_deploy_task.go b/pkg/compute/tasks/guest_deploy_task.go index 3db594cee8..692ffcc088 100644 --- a/pkg/compute/tasks/guest_deploy_task.go +++ b/pkg/compute/tasks/guest_deploy_task.go @@ -44,7 +44,7 @@ func (self *GuestDeployTask) OnInit(ctx context.Context, obj db.IStandaloneModel func (self *GuestDeployTask) OnGuestNetworkReady(ctx context.Context, guest *models.SGuest) { self.SetStage("OnDeployWaitServerStop", nil) if jsonutils.QueryBoolean(self.Params, "restart", false) { - guest.StartGuestStopTask(ctx, self.UserCred, false, self.GetTaskId()) + guest.StartGuestStopTask(ctx, self.UserCred, false, false, self.GetTaskId()) } else { // Note: have to use LocalTaskRun, run to another place implement OnDeployWaitServerStop taskman.LocalTaskRun(self, func() (jsonutils.JSONObject, error) { diff --git a/pkg/compute/tasks/guest_rebuild_root_task.go b/pkg/compute/tasks/guest_rebuild_root_task.go index 84ce1b8b6e..9846d87c48 100644 --- a/pkg/compute/tasks/guest_rebuild_root_task.go +++ b/pkg/compute/tasks/guest_rebuild_root_task.go @@ -45,7 +45,7 @@ func (self *GuestRebuildRootTask) OnInit(ctx context.Context, obj db.IStandalone guest := obj.(*models.SGuest) if jsonutils.QueryBoolean(self.Params, "need_stop", false) { self.SetStage("OnStopServerComplete", nil) - guest.StartGuestStopTask(ctx, self.UserCred, false, self.GetTaskId()) + guest.StartGuestStopTask(ctx, self.UserCred, false, false, self.GetTaskId()) } else { self.StartRebuildRootDisk(ctx, guest) } diff --git a/pkg/compute/tasks/guest_reset_task.go b/pkg/compute/tasks/guest_reset_task.go index 620c14ce93..ddf04baf2b 100644 --- a/pkg/compute/tasks/guest_reset_task.go +++ b/pkg/compute/tasks/guest_reset_task.go @@ -57,7 +57,7 @@ func (self *GuestHardResetTask) OnInit(ctx context.Context, obj db.IStandaloneMo func (self *GuestHardResetTask) StopServer(ctx context.Context, guest *models.SGuest) { guest.SetStatus(self.UserCred, api.VM_STOPPING, "") self.SetStage("OnServerStopComplete", nil) - guest.StartGuestStopTask(ctx, self.UserCred, false, self.GetTaskId()) + guest.StartGuestStopTask(ctx, self.UserCred, false, false, self.GetTaskId()) // logclient.AddActionLogWith(guest, logclient.ACT_VM_RESTART, `{"is_force": true}`, self.UserCred, true) } @@ -81,6 +81,6 @@ type GuestRestartTask struct { func (self *GuestRestartTask) StopServer(ctx context.Context, guest *models.SGuest) { self.SetStage("OnServerStopComplete", nil) isForce := jsonutils.QueryBoolean(self.Params, "is_force", false) - guest.StartGuestStopTask(ctx, self.UserCred, isForce, self.GetTaskId()) + guest.StartGuestStopTask(ctx, self.UserCred, isForce, false, self.GetTaskId()) // logclient.AddActionLog(guest, logclient.ACT_VM_RESTART, `{"is_force": false}`, self.UserCred, true) } diff --git a/pkg/compute/tasks/guest_save_image_task.go b/pkg/compute/tasks/guest_save_image_task.go index 5ae0645d19..df86d41fcd 100644 --- a/pkg/compute/tasks/guest_save_image_task.go +++ b/pkg/compute/tasks/guest_save_image_task.go @@ -39,7 +39,7 @@ func (self *GuestSaveImageTask) OnInit(ctx context.Context, obj db.IStandaloneMo log.Infof("Saving server image: %s", guest.Name) if restart, _ := self.GetParams().Bool("restart"); restart { self.SetStage("OnStopServerComplete", nil) - guest.StartGuestStopTask(ctx, self.GetUserCred(), false, self.GetTaskId()) + guest.StartGuestStopTask(ctx, self.GetUserCred(), false, false, self.GetTaskId()) } else { self.OnStopServerComplete(ctx, guest, nil) } diff --git a/pkg/hostman/storageman/storage_agent.go b/pkg/hostman/storageman/storage_agent.go index 96168781c0..208cf325aa 100644 --- a/pkg/hostman/storageman/storage_agent.go +++ b/pkg/hostman/storageman/storage_agent.go @@ -358,7 +358,10 @@ func (as *SAgentStorage) waitVmToolsVersion(ctx context.Context, vm *esxi.SVirtu } timeUpper = time.Now().Add(timeout) for vm.GetStatus() == api.VM_RUNNING && time.Now().Before(timeUpper) { - vm.StopVM(ctx, true) + opts := &cloudprovider.ServerStopOptions{ + IsForce: true, + } + vm.StopVM(ctx, opts) time.Sleep(5 * time.Second) } return diff --git a/pkg/mcclient/options/servers.go b/pkg/mcclient/options/servers.go index 873c0f525a..a266dc8024 100644 --- a/pkg/mcclient/options/servers.go +++ b/pkg/mcclient/options/servers.go @@ -552,8 +552,9 @@ func (opts *ServerCreateOptions) Params() (*computeapi.ServerCreateInput, error) } type ServerStopOptions struct { - ID []string `help:"ID or Name of server" json:"-"` - Force *bool `help:"Stop server forcefully" json:"is_force"` + ID []string `help:"ID or Name of server" json:"-"` + Force *bool `help:"Stop server forcefully" json:"is_force"` + StopCharging *bool `help:"Stop charging when server stop"` } func (o *ServerStopOptions) GetIds() []string { diff --git a/pkg/multicloud/aliyun/instance.go b/pkg/multicloud/aliyun/instance.go index 58506d12ea..ac70bac3ec 100644 --- a/pkg/multicloud/aliyun/instance.go +++ b/pkg/multicloud/aliyun/instance.go @@ -454,8 +454,8 @@ func (self *SInstance) StartVM(ctx context.Context) error { return cloudprovider.ErrTimeout } -func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { - err := self.host.zone.region.StopVM(self.InstanceId, isForce) +func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + err := self.host.zone.region.StopVM(self.InstanceId, opts.IsForce, opts.StopCharging) if err != nil { return err } @@ -657,7 +657,7 @@ func (self *SRegion) doStartVM(instanceId string) error { return self.instanceOperation(instanceId, "StartInstance", nil) } -func (self *SRegion) doStopVM(instanceId string, isForce bool) error { +func (self *SRegion) doStopVM(instanceId string, isForce, stopCharging bool) error { params := make(map[string]string) if isForce { params["ForceStop"] = "true" @@ -665,6 +665,9 @@ func (self *SRegion) doStopVM(instanceId string, isForce bool) error { params["ForceStop"] = "false" } params["StoppedMode"] = "KeepCharging" + if stopCharging { + params["StoppedMode"] = "StopCharging" + } return self.instanceOperation(instanceId, "StopInstance", params) } @@ -711,7 +714,7 @@ func (self *SRegion) StartVM(instanceId string) error { // return self.waitInstanceStatus(instanceId, InstanceStatusRunning, time.Second*5, time.Second*180) // 3 minutes to timeout } -func (self *SRegion) StopVM(instanceId string, isForce bool) error { +func (self *SRegion) StopVM(instanceId string, isForce, stopCharging bool) error { status, err := self.GetInstanceStatus(instanceId) if err != nil { log.Errorf("Fail to get instance status on StopVM: %s", err) @@ -724,7 +727,7 @@ func (self *SRegion) StopVM(instanceId string, isForce bool) error { log.Errorf("StopVM: vm status is %s expect %s", status, InstanceStatusRunning) return cloudprovider.ErrInvalidStatus } - return self.doStopVM(instanceId, isForce) + return self.doStopVM(instanceId, isForce, stopCharging) // if err != nil { // return err // } diff --git a/pkg/multicloud/aliyun/shell/instance.go b/pkg/multicloud/aliyun/shell/instance.go index c9172d250e..477af327c9 100644 --- a/pkg/multicloud/aliyun/shell/instance.go +++ b/pkg/multicloud/aliyun/shell/instance.go @@ -116,11 +116,12 @@ func init() { }) type InstanceStopOptions struct { - ID string `help:"instance ID"` - Force bool `help:"Force stop instance"` + ID string `help:"instance ID"` + Force bool `help:"Force stop instance"` + StopCharging bool `help:"Stop Charging"` } shellutils.R(&InstanceStopOptions{}, "instance-stop", "Stop a instance", func(cli *aliyun.SRegion, args *InstanceStopOptions) error { - err := cli.StopVM(args.ID, args.Force) + err := cli.StopVM(args.ID, args.Force, args.StopCharging) if err != nil { return err } diff --git a/pkg/multicloud/aws/instance.go b/pkg/multicloud/aws/instance.go index 21002cb2ce..df4ada28aa 100644 --- a/pkg/multicloud/aws/instance.go +++ b/pkg/multicloud/aws/instance.go @@ -378,8 +378,8 @@ func (self *SInstance) StartVM(ctx context.Context) error { return cloudprovider.ErrTimeout } -func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { - err := self.host.zone.region.StopVM(self.InstanceId, isForce) +func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + err := self.host.zone.region.StopVM(self.InstanceId, opts.IsForce) if err != nil { return err } diff --git a/pkg/multicloud/azure/classic_instance.go b/pkg/multicloud/azure/classic_instance.go index 6f21e741e9..28bdc58b62 100644 --- a/pkg/multicloud/azure/classic_instance.go +++ b/pkg/multicloud/azure/classic_instance.go @@ -448,8 +448,8 @@ func (self *SClassicInstance) StartVM(ctx context.Context) error { return cloudprovider.WaitStatus(self, api.VM_RUNNING, 10*time.Second, 300*time.Second) } -func (self *SClassicInstance) StopVM(ctx context.Context, isForce bool) error { - err := self.host.zone.region.StopClassicVM(self.ID, isForce) +func (self *SClassicInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + err := self.host.zone.region.StopClassicVM(self.ID, opts.IsForce) if err != nil { return err } diff --git a/pkg/multicloud/azure/instance.go b/pkg/multicloud/azure/instance.go index f5bfca00b4..fbaa35a67c 100644 --- a/pkg/multicloud/azure/instance.go +++ b/pkg/multicloud/azure/instance.go @@ -781,7 +781,10 @@ func (region *SRegion) DeployVM(ctx context.Context, instanceId, name, password, func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { cpu := self.GetVcpuCount() memoryMb := self.GetVmemSizeMB() - self.StopVM(ctx, true) + opts := &cloudprovider.ServerStopOptions{ + IsForce: true, + } + self.StopVM(ctx, opts) return self.host.zone.region.ReplaceSystemDisk(self, cpu, memoryMb, desc.ImageId, desc.Password, desc.PublicKey, desc.SysSizeGB) } @@ -832,7 +835,10 @@ func (region *SRegion) ReplaceSystemDisk(instance *SInstance, cpu int, memoryMb return "", err } - newInstance.StopVM(context.Background(), true) + opts := &cloudprovider.ServerStopOptions{ + IsForce: true, + } + newInstance.StopVM(context.Background(), opts) cloudprovider.WaitStatus(newInstance, api.VM_READY, time.Second*5, time.Minute*5) newInstance.deleteVM(context.Background(), true) @@ -1064,8 +1070,8 @@ func (self *SInstance) StartVM(ctx context.Context) error { return cloudprovider.WaitStatus(self, api.VM_RUNNING, 10*time.Second, 300*time.Second) } -func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { - err := self.host.zone.region.StopVM(self.ID, isForce) +func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + err := self.host.zone.region.StopVM(self.ID, opts.IsForce) if err != nil { return err } diff --git a/pkg/multicloud/ctyun/instance.go b/pkg/multicloud/ctyun/instance.go index 829fab68c9..7edf59d6f3 100644 --- a/pkg/multicloud/ctyun/instance.go +++ b/pkg/multicloud/ctyun/instance.go @@ -476,7 +476,7 @@ func (self *SInstance) StartVM(ctx context.Context) error { return nil } -func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { +func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { err := self.host.zone.region.StopVM(self.GetId()) if err != nil { return errors.Wrap(err, "Instance.StopVM") diff --git a/pkg/multicloud/esxi/virtualmachine.go b/pkg/multicloud/esxi/virtualmachine.go index a1639abf62..32e32f25ea 100644 --- a/pkg/multicloud/esxi/virtualmachine.go +++ b/pkg/multicloud/esxi/virtualmachine.go @@ -456,11 +456,11 @@ func makeNicStartConnected(nic *SVirtualNIC) *types.VirtualDeviceConfigSpec { return &editSpec } -func (self *SVirtualMachine) StopVM(ctx context.Context, isForce bool) error { +func (self *SVirtualMachine) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { if self.GetStatus() == api.VM_READY { return nil } - if !isForce && self.isToolsOk() { + if !opts.IsForce && self.isToolsOk() { return self.shutdownVM(ctx) } else { return self.poweroffVM(ctx) diff --git a/pkg/multicloud/google/instance.go b/pkg/multicloud/google/instance.go index 388189f48c..520f6a1dbe 100644 --- a/pkg/multicloud/google/instance.go +++ b/pkg/multicloud/google/instance.go @@ -430,7 +430,7 @@ func (instance *SInstance) StartVM(ctx context.Context) error { return instance.host.zone.region.StartInstance(instance.SelfLink) } -func (instance *SInstance) StopVM(ctx context.Context, isForce bool) error { +func (instance *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { return instance.host.zone.region.StopInstance(instance.SelfLink) } diff --git a/pkg/multicloud/huawei/instance.go b/pkg/multicloud/huawei/instance.go index 8183f0fe6c..0a00736aae 100644 --- a/pkg/multicloud/huawei/instance.go +++ b/pkg/multicloud/huawei/instance.go @@ -470,7 +470,7 @@ func (self *SInstance) StartVM(ctx context.Context) error { return cloudprovider.ErrTimeout } -func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { +func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { if self.Status == InstanceStatusStopped { return nil } @@ -480,7 +480,7 @@ func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { return nil } - err := self.host.zone.region.StopVM(self.GetId(), isForce) + err := self.host.zone.region.StopVM(self.GetId(), opts.IsForce) if err != nil { return err } diff --git a/pkg/multicloud/openstack/instance.go b/pkg/multicloud/openstack/instance.go index e33a784dae..db207288b5 100644 --- a/pkg/multicloud/openstack/instance.go +++ b/pkg/multicloud/openstack/instance.go @@ -406,8 +406,8 @@ func (instance *SInstance) StartVM(ctx context.Context) error { return cloudprovider.WaitStatus(instance, api.VM_RUNNING, 10*time.Second, 8*time.Minute) } -func (instance *SInstance) StopVM(ctx context.Context, isForce bool) error { - err := instance.host.zone.region.StopVM(instance.Id, isForce) +func (instance *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + err := instance.host.zone.region.StopVM(instance.Id, opts.IsForce) if err != nil { return errors.Wrapf(err, "StopVM(%s)", instance.Id) } diff --git a/pkg/multicloud/qcloud/instance.go b/pkg/multicloud/qcloud/instance.go index 46eacc70ab..47c023ba5f 100644 --- a/pkg/multicloud/qcloud/instance.go +++ b/pkg/multicloud/qcloud/instance.go @@ -417,8 +417,8 @@ func (self *SInstance) StartVM(ctx context.Context) error { return cloudprovider.ErrTimeout } -func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { - err := self.host.zone.region.StopVM(self.InstanceId, isForce) +func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + err := self.host.zone.region.StopVM(self.InstanceId, opts) if err != nil { return err } @@ -467,7 +467,10 @@ func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SMan if err != nil { return "", err } - self.StopVM(ctx, true) + opts := &cloudprovider.ServerStopOptions{ + IsForce: true, + } + self.StopVM(ctx, opts) instance, err := self.host.zone.region.GetInstance(self.InstanceId) if err != nil { return "", err @@ -638,15 +641,18 @@ func (self *SRegion) doStartVM(instanceId string) error { return self.instanceOperation(instanceId, "StartInstances", nil, true) } -func (self *SRegion) doStopVM(instanceId string, isForce bool) error { +func (self *SRegion) doStopVM(instanceId string, opts *cloudprovider.ServerStopOptions) error { params := make(map[string]string) - if isForce { + if opts.IsForce { // params["ForceStop"] = "FALSE" params["StopType"] = "HARD" } else { // params["ForceStop"] = "FALSE" params["StopType"] = "SOFT" } + if opts.StopCharging { + params["StoppedMode"] = "STOP_CHARGING" + } return self.instanceOperation(instanceId, "StopInstances", params, true) } @@ -672,7 +678,7 @@ func (self *SRegion) StartVM(instanceId string) error { return self.doStartVM(instanceId) } -func (self *SRegion) StopVM(instanceId string, isForce bool) error { +func (self *SRegion) StopVM(instanceId string, opts *cloudprovider.ServerStopOptions) error { status, err := self.GetInstanceStatus(instanceId) if err != nil { log.Errorf("Fail to get instance status on StopVM: %s", err) @@ -681,7 +687,7 @@ func (self *SRegion) StopVM(instanceId string, isForce bool) error { if status == InstanceStatusStopped { return nil } - return self.doStopVM(instanceId, isForce) + return self.doStopVM(instanceId, opts) } func (self *SRegion) DeleteVM(instanceId string) error { diff --git a/pkg/multicloud/qcloud/shell/instance.go b/pkg/multicloud/qcloud/shell/instance.go index a44fa437a8..b36d1080ac 100644 --- a/pkg/multicloud/qcloud/shell/instance.go +++ b/pkg/multicloud/qcloud/shell/instance.go @@ -18,6 +18,7 @@ import ( "fmt" "strings" + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/multicloud/qcloud" "yunion.io/x/onecloud/pkg/util/shellutils" ) @@ -122,11 +123,16 @@ func init() { }) type InstanceStopOptions struct { - ID string `help:"instance ID"` - Force bool `help:"Force stop instance"` + ID string `help:"instance ID"` + Force bool `help:"Force stop instance"` + StopCharging bool `help:"Stop charging"` } shellutils.R(&InstanceStopOptions{}, "instance-stop", "Stop a instance", func(cli *qcloud.SRegion, args *InstanceStopOptions) error { - err := cli.StopVM(args.ID, args.Force) + opts := &cloudprovider.ServerStopOptions{ + IsForce: args.Force, + StopCharging: args.StopCharging, + } + err := cli.StopVM(args.ID, opts) if err != nil { return err } diff --git a/pkg/multicloud/ucloud/instance.go b/pkg/multicloud/ucloud/instance.go index ce46b864b4..8c1e189fbd 100644 --- a/pkg/multicloud/ucloud/instance.go +++ b/pkg/multicloud/ucloud/instance.go @@ -400,7 +400,7 @@ func (self *SInstance) StartVM(ctx context.Context) error { return nil } -func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { +func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { err := self.host.zone.region.StopVM(self.GetId()) if err != nil { return err diff --git a/pkg/multicloud/zstack/instance.go b/pkg/multicloud/zstack/instance.go index 62038461f8..b9c6bf9e5d 100644 --- a/pkg/multicloud/zstack/instance.go +++ b/pkg/multicloud/zstack/instance.go @@ -279,8 +279,8 @@ func (region *SRegion) StartVM(instanceId string) error { return err } -func (instance *SInstance) StopVM(ctx context.Context, isForce bool) error { - err := instance.host.zone.region.StopVM(instance.UUID, isForce) +func (instance *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + err := instance.host.zone.region.StopVM(instance.UUID, opts.IsForce) if err != nil { return err }