From cf0b2f55659aa8ac8ae42ddbdebe65cc46c72d87 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Tue, 9 Mar 2021 11:38:50 +0800 Subject: [PATCH 1/2] fix: support prepaid instance change config --- pkg/multicloud/aliyun/instance.go | 87 ++++++++++++++++++++----------- pkg/multicloud/huawei/instance.go | 6 +++ 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/pkg/multicloud/aliyun/instance.go b/pkg/multicloud/aliyun/instance.go index 6e45200be2..3b981eae92 100644 --- a/pkg/multicloud/aliyun/instance.go +++ b/pkg/multicloud/aliyun/instance.go @@ -28,6 +28,7 @@ import ( "yunion.io/x/pkg/util/seclib" "yunion.io/x/pkg/utils" + 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/multicloud" @@ -527,10 +528,43 @@ func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SMan } func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { - if len(config.InstanceType) > 0 { - return self.host.zone.region.ChangeVMConfig2(self.ZoneId, self.InstanceId, config.InstanceType, nil) + isDowngrade, isPrepaid := false, self.GetBillingType() == billing_api.BILLING_TYPE_PREPAID + if (self.GetVcpuCount() > config.Cpu && config.Cpu > 0) || (self.GetVmemSizeMB() > config.MemoryMB && config.MemoryMB > 0) { + isDowngrade = true } - return self.host.zone.region.ChangeVMConfig(self.ZoneId, self.InstanceId, config.Cpu, config.MemoryMB, nil) + + instanceTypes := []string{} + + if len(config.InstanceType) > 0 { + instanceTypes = []string{config.InstanceType} + } else { + specs, err := self.host.zone.region.GetMatchInstanceTypes(config.Cpu, config.MemoryMB, 0, self.ZoneId) + if err != nil { + return errors.Wrapf(err, "GetMatchInstanceTypes") + } + for _, spec := range specs { + instanceTypes = append(instanceTypes, spec.InstanceTypeId) + } + } + + var err error + for _, instanceType := range instanceTypes { + if isPrepaid { + err = self.host.zone.region.ChangePrepaidVMConfig(self.ZoneId, self.InstanceId, instanceType, isDowngrade) + if err != nil { + log.Errorf("ChangePrepaidVMConfig %s error: %v", instanceType, err) + } + } else { + err = self.host.zone.region.ChangeVMConfig(self.ZoneId, self.InstanceId, instanceType) + if err != nil { + log.Errorf("ChangeVMConfig %s error: %v", instanceType, err) + } + } + } + if err != nil { + return errors.Wrapf(err, "ChangeVMConfig") + } + return fmt.Errorf("Failed to change vm config, specification not supported") } func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { @@ -893,38 +927,31 @@ func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd return body.GetString("DiskId") } -func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, vmem int, disks []*SDisk) error { - // todo: support change disk config? - params := make(map[string]string) - instanceTypes, e := self.GetMatchInstanceTypes(ncpu, vmem, 0, zoneId) - if e != nil { - return e - } - - for _, instancetype := range instanceTypes { - params["InstanceType"] = instancetype.InstanceTypeId - params["ClientToken"] = utils.GenRequestId(20) - if err := self.instanceOperation(instanceId, "ModifyInstanceSpec", params); err != nil { - log.Errorf("Failed for %s: %s", instancetype.InstanceTypeId, err) - } else { - return nil - } - } - - return fmt.Errorf("Failed to change vm config, specification not supported") -} - -func (self *SRegion) ChangeVMConfig2(zoneId string, instanceId string, instanceType string, disks []*SDisk) error { +func (self *SRegion) ChangePrepaidVMConfig(zoneId string, instanceId string, instanceType string, isDowngrade bool) error { // todo: support change disk config? params := make(map[string]string) params["InstanceType"] = instanceType params["ClientToken"] = utils.GenRequestId(20) - if err := self.instanceOperation(instanceId, "ModifyInstanceSpec", params); err != nil { - log.Errorf("Failed for %s: %s", instanceType, err) - return fmt.Errorf("Failed to change vm config, specification not supported") - } else { - return nil + if isDowngrade { + params["OperatorType"] = "downgrade" } + err := self.instanceOperation(instanceId, "ModifyPrepayInstanceSpec", params) + if err != nil { + return errors.Wrapf(err, "ModifyPrepayInstanceSpec %s", instanceType) + } + return nil +} + +func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, instanceType string) error { + // todo: support change disk config? + params := make(map[string]string) + params["InstanceType"] = instanceType + params["ClientToken"] = utils.GenRequestId(20) + err := self.instanceOperation(instanceId, "ModifyInstanceSpec", params) + if err != nil { + return errors.Wrapf(err, "ModifyInstanceSpec %s", instanceType) + } + return nil } func (self *SRegion) DetachDisk(instanceId string, diskId string) error { diff --git a/pkg/multicloud/huawei/instance.go b/pkg/multicloud/huawei/instance.go index 4ae58a610e..966c6ffa57 100644 --- a/pkg/multicloud/huawei/instance.go +++ b/pkg/multicloud/huawei/instance.go @@ -1296,6 +1296,9 @@ func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, return err } + self.ecsClient.Servers.SetVersion("v1.1") + defer self.ecsClient.Servers.SetVersion("v1") + for _, t := range instanceTypes { params := jsonutils.NewDict() resizeObj := jsonutils.NewDict() @@ -1318,6 +1321,9 @@ func (self *SRegion) ChangeVMConfig2(zoneId string, instanceId string, instanceT resizeObj.Add(jsonutils.NewString(instanceType), "flavorRef") params.Add(resizeObj, "resize") + self.ecsClient.Servers.SetVersion("v1.1") + defer self.ecsClient.Servers.SetVersion("v1") + _, err := self.ecsClient.Servers.PerformAction2("resize", instanceId, params, "") return err } From 56948a97eebf8de51d0eaf6494cbcbefd1906a9b Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Tue, 9 Mar 2021 14:27:09 +0800 Subject: [PATCH 2/2] fix: optimize change vm config and prepaid change config --- pkg/multicloud/aliyun/instance.go | 12 +++-- pkg/multicloud/aliyun/shell/instance.go | 12 ++--- pkg/multicloud/huawei/instance.go | 70 ++++++++----------------- pkg/multicloud/huawei/shell/instance.go | 13 ++--- pkg/multicloud/qcloud/instance.go | 60 +++++++++------------ pkg/multicloud/qcloud/shell/instance.go | 14 ++--- 6 files changed, 66 insertions(+), 115 deletions(-) diff --git a/pkg/multicloud/aliyun/instance.go b/pkg/multicloud/aliyun/instance.go index 3b981eae92..288ba2d34a 100644 --- a/pkg/multicloud/aliyun/instance.go +++ b/pkg/multicloud/aliyun/instance.go @@ -550,20 +550,24 @@ func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.S var err error for _, instanceType := range instanceTypes { if isPrepaid { - err = self.host.zone.region.ChangePrepaidVMConfig(self.ZoneId, self.InstanceId, instanceType, isDowngrade) + err = self.host.zone.region.ChangePrepaidVMConfig(self.InstanceId, instanceType, isDowngrade) if err != nil { log.Errorf("ChangePrepaidVMConfig %s error: %v", instanceType, err) + continue } } else { - err = self.host.zone.region.ChangeVMConfig(self.ZoneId, self.InstanceId, instanceType) + err = self.host.zone.region.ChangeVMConfig(self.InstanceId, instanceType) if err != nil { log.Errorf("ChangeVMConfig %s error: %v", instanceType, err) + continue } } + return nil } if err != nil { return errors.Wrapf(err, "ChangeVMConfig") } + return fmt.Errorf("Failed to change vm config, specification not supported") } @@ -927,7 +931,7 @@ func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd return body.GetString("DiskId") } -func (self *SRegion) ChangePrepaidVMConfig(zoneId string, instanceId string, instanceType string, isDowngrade bool) error { +func (self *SRegion) ChangePrepaidVMConfig(instanceId string, instanceType string, isDowngrade bool) error { // todo: support change disk config? params := make(map[string]string) params["InstanceType"] = instanceType @@ -942,7 +946,7 @@ func (self *SRegion) ChangePrepaidVMConfig(zoneId string, instanceId string, ins return nil } -func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, instanceType string) error { +func (self *SRegion) ChangeVMConfig(instanceId string, instanceType string) error { // todo: support change disk config? params := make(map[string]string) params["InstanceType"] = instanceType diff --git a/pkg/multicloud/aliyun/shell/instance.go b/pkg/multicloud/aliyun/shell/instance.go index bd781ae787..5448e35254 100644 --- a/pkg/multicloud/aliyun/shell/instance.go +++ b/pkg/multicloud/aliyun/shell/instance.go @@ -177,18 +177,12 @@ func init() { }) type InstanceChangeConfigOptions struct { - ID string `help:"instance ID"` - InstanceTypeId string `help:"instance type"` - Disk []int `help:"Data disk sizes int GB"` + ID string `help:"instance ID"` + InstanceType string `help:"instance type"` } shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Deploy keypair/password to a stopped virtual server", func(cli *aliyun.SRegion, args *InstanceChangeConfigOptions) error { - instance, e := cli.GetInstance(args.ID) - if e != nil { - return e - } - - err := cli.ChangeVMConfig2(instance.ZoneId, args.ID, args.InstanceTypeId, nil) + err := cli.ChangeVMConfig(args.ID, args.InstanceType) if err != nil { return err } diff --git a/pkg/multicloud/huawei/instance.go b/pkg/multicloud/huawei/instance.go index 966c6ffa57..16396f621d 100644 --- a/pkg/multicloud/huawei/instance.go +++ b/pkg/multicloud/huawei/instance.go @@ -692,29 +692,31 @@ func (self *SInstance) DeployVM(ctx context.Context, name string, username strin } func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { + instanceTypes := []string{} if len(config.InstanceType) > 0 { - return self.ChangeConfig2(ctx, config.InstanceType) + instanceTypes = []string{config.InstanceType} + } else { + flavors, err := self.host.zone.region.GetMatchInstanceTypes(config.Cpu, config.MemoryMB, self.OSEXTAZAvailabilityZone) + if err != nil { + return errors.Wrapf(err, "GetMatchInstanceTypes") + } + for _, flavor := range flavors { + instanceTypes = append(instanceTypes, flavor.ID) + } + } + var err error + for _, instanceType := range instanceTypes { + err = self.host.zone.region.ChangeVMConfig(self.GetId(), instanceType) + if err != nil { + log.Warningf("ChangeVMConfig %s for %s error: %v", self.GetId(), instanceType, err) + } else { + return cloudprovider.WaitStatusWithDelay(self, api.VM_READY, 15*time.Second, 15*time.Second, 180*time.Second) + } } - err := self.host.zone.region.ChangeVMConfig(self.OSEXTAZAvailabilityZone, self.GetId(), config.Cpu, config.MemoryMB, nil) if err != nil { - return err + return errors.Wrapf(err, "ChangeVMConfig") } - - return cloudprovider.WaitStatusWithDelay(self, api.VM_READY, 15*time.Second, 15*time.Second, 180*time.Second) -} - -func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error { - err := self.host.zone.region.ChangeVMConfig2(self.OSEXTAZAvailabilityZone, self.GetId(), instanceType, nil) - if err != nil { - return errors.Wrap(err, "Instance.ChangeConfig2.ChangeVMConfig2") - } - - err = cloudprovider.WaitStatusWithDelay(self, api.VM_READY, 15*time.Second, 15*time.Second, 180*time.Second) - if err != nil { - return errors.Wrap(err, "Instance.ChangeConfig2.WaitStatusWithDelay") - } - - return nil + return fmt.Errorf("Failed to change vm config, specification not supported") } // todo:// 返回jsonobject感觉很诡异。不能直接知道内部细节 @@ -1290,42 +1292,16 @@ func (self *SRegion) DeployVM(instanceId string, name string, password string, k } // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212653.html -func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, vmem int, disks []*SDisk) error { - instanceTypes, err := self.GetMatchInstanceTypes(ncpu, vmem, zoneId) - if err != nil { - return err - } - +func (self *SRegion) ChangeVMConfig(instanceId string, instanceType string) error { self.ecsClient.Servers.SetVersion("v1.1") defer self.ecsClient.Servers.SetVersion("v1") - for _, t := range instanceTypes { - params := jsonutils.NewDict() - resizeObj := jsonutils.NewDict() - resizeObj.Add(jsonutils.NewString(t.ID), "flavorRef") - params.Add(resizeObj, "resize") - _, err := self.ecsClient.Servers.PerformAction2("resize", instanceId, params, "") - if err != nil { - log.Errorf("Failed for %s: %s", t.ID, err) - } else { - return nil - } - } - - return fmt.Errorf("Failed to change vm config, specification not supported") -} - -func (self *SRegion) ChangeVMConfig2(zoneId string, instanceId string, instanceType string, disks []*SDisk) error { params := jsonutils.NewDict() resizeObj := jsonutils.NewDict() resizeObj.Add(jsonutils.NewString(instanceType), "flavorRef") params.Add(resizeObj, "resize") - - self.ecsClient.Servers.SetVersion("v1.1") - defer self.ecsClient.Servers.SetVersion("v1") - _, err := self.ecsClient.Servers.PerformAction2("resize", instanceId, params, "") - return err + return errors.Wrapf(err, "PerformAction2(resize)") } // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0142763126.html 微版本2.6及以上? diff --git a/pkg/multicloud/huawei/shell/instance.go b/pkg/multicloud/huawei/shell/instance.go index 95818a8525..d46f364462 100644 --- a/pkg/multicloud/huawei/shell/instance.go +++ b/pkg/multicloud/huawei/shell/instance.go @@ -136,19 +136,12 @@ func init() { }) type InstanceChangeConfigOptions struct { - ID string `help:"instance ID"` - InstanceTypeId string `help:"instance type"` - Disk []int `help:"Data disk sizes int GB"` + ID string `help:"instance ID"` + InstanceType string `help:"instance type"` } shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Deploy keypair/password to a stopped virtual server", func(cli *huawei.SRegion, args *InstanceChangeConfigOptions) error { - instance, e := cli.GetInstanceByID(args.ID) - if e != nil { - return e - } - - // todo : add create disks - err := cli.ChangeVMConfig2(instance.GetId(), args.ID, args.InstanceTypeId, nil) + err := cli.ChangeVMConfig(args.ID, args.InstanceType) if err != nil { return err } diff --git a/pkg/multicloud/qcloud/instance.go b/pkg/multicloud/qcloud/instance.go index 3c1a86fe9b..ce1f653ee3 100644 --- a/pkg/multicloud/qcloud/instance.go +++ b/pkg/multicloud/qcloud/instance.go @@ -535,14 +535,33 @@ func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SMan } func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { + instanceTypes := []string{} if len(config.InstanceType) > 0 { - return self.ChangeConfig2(ctx, config.InstanceType) + instanceTypes = []string{config.InstanceType} + } else { + specs, err := self.host.zone.region.GetMatchInstanceTypes(config.Cpu, config.MemoryMB, 0, self.Placement.Zone) + if err != nil { + return errors.Wrapf(err, "GetMatchInstanceTypes") + } + for _, spec := range specs { + instanceTypes = append(instanceTypes, spec.InstanceType) + } } - return self.host.zone.region.ChangeVMConfig(self.Placement.Zone, self.InstanceId, config.Cpu, config.MemoryMB, nil) -} -func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error { - return self.host.zone.region.ChangeVMConfig2(self.Placement.Zone, self.InstanceId, instanceType, nil) + var err error + for _, instanceType := range instanceTypes { + err = self.host.zone.region.ChangeVMConfig(self.InstanceId, instanceType) + if err != nil { + log.Errorf("ChangeConfig for %s with %s error: %v", self.InstanceId, instanceType, err) + continue + } + return nil + } + if err != nil { + return err + } + + return fmt.Errorf("Failed to change vm config, specification not supported") } func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { @@ -863,39 +882,12 @@ func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd return err } -func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, vmem int, disks []*SDisk) error { - // todo: support change disk config? - params := make(map[string]string) - instanceTypes, e := self.GetMatchInstanceTypes(ncpu, vmem, 0, zoneId) - if e != nil { - return e - } - - for _, instancetype := range instanceTypes { - params["InstanceType"] = instancetype.InstanceType - err := self.instanceOperation(instanceId, "ResetInstancesType", params, true) - if err != nil { - log.Errorf("Failed for %s: %s", instancetype.InstanceType, err) - } else { - return nil - } - } - - return fmt.Errorf("Failed to change vm config, specification not supported") -} - -func (self *SRegion) ChangeVMConfig2(zoneId string, instanceId string, instanceType string, disks []*SDisk) error { - // todo: support change disk config? +func (self *SRegion) ChangeVMConfig(instanceId string, instanceType string) error { params := make(map[string]string) params["InstanceType"] = instanceType err := self.instanceOperation(instanceId, "ResetInstancesType", params, true) - if err != nil { - log.Errorf("Failed for %s: %s", instanceType, err) - return fmt.Errorf("Failed to change vm config, specification not supported") - } - - return nil + return errors.Wrapf(err, "ResetInstancesType %s", instanceType) } func (self *SRegion) DetachDisk(instanceId string, diskId string) error { diff --git a/pkg/multicloud/qcloud/shell/instance.go b/pkg/multicloud/qcloud/shell/instance.go index 9b11ce854e..daa68a35b6 100644 --- a/pkg/multicloud/qcloud/shell/instance.go +++ b/pkg/multicloud/qcloud/shell/instance.go @@ -186,20 +186,12 @@ func init() { }) type InstanceChangeConfigOptions struct { - ID string `help:"instance ID"` - InstanceTypeId string `help:"instance type"` - Vmem int `help:"MiB of memory"` - Disk []int `help:"Data disk sizes int GB"` + ID string `help:"instance ID"` + InstanceType string `help:"instance type"` } shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Deploy keypair/password to a stopped virtual server", func(cli *qcloud.SRegion, args *InstanceChangeConfigOptions) error { - instance, e := cli.GetInstance(args.ID) - if e != nil { - return e - } - - // todo : add create disks - err := cli.ChangeVMConfig2(instance.Placement.Zone, args.ID, args.InstanceTypeId, nil) + err := cli.ChangeVMConfig(args.ID, args.InstanceType) if err != nil { return err }