fix(region): support query public cloud vm modification types (#24655)

This commit is contained in:
屈轩
2026-04-10 16:54:10 +08:00
committed by GitHub
parent cce183a1f9
commit 2564061744
13 changed files with 174 additions and 4 deletions
+1
View File
@@ -131,6 +131,7 @@ func init() {
cmd.BatchPerform("set-root-disk-matcher", &options.ServerSetRootDiskMatcher{})
cmd.Perform("disable-auto-merge-snapshot", &options.ServerDisableAutoMergeSnapshot{})
cmd.Get("vnc", new(options.ServerVncOptions))
cmd.Get("modification-types", new(options.ServerIdOptions))
cmd.Get("desc", new(options.ServerIdOptions))
cmd.Get("status", new(options.ServerIdOptions))
cmd.Get("iso", new(options.ServerIdOptions))
+1 -1
View File
@@ -94,7 +94,7 @@ require (
k8s.io/client-go v0.26.1
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.20260409072655-7f5827365741
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260409123851-7b55f5f3ad92
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005
yunion.io/x/jsonutils v1.0.1-0.20250507052344-1abcf4f443b1
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
+2 -2
View File
@@ -1090,8 +1090,8 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260409072655-7f5827365741 h1:F+I9SeZJpH477cIGTbjHiRT+hry+o3CnWbfwkiFG8LQ=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260409072655-7f5827365741/go.mod h1:GdAwZ78fiqj13i2r1zNuE/D6YOlhh2q4jZjuyHiZTq8=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260409123851-7b55f5f3ad92 h1:tklE5QK6iUhIeZiSmY8IVUjCcXNgQ1xfH8EaUgrZEDk=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260409123851-7b55f5f3ad92/go.mod h1:GdAwZ78fiqj13i2r1zNuE/D6YOlhh2q4jZjuyHiZTq8=
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005 h1:3sWwcjGXGjG9mLBWa7AyLq+QSi0udTAx21pfVQRFMBE=
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
+8
View File
@@ -1400,3 +1400,11 @@ type ServerChangeBillingTypeInput struct {
// required: true
BillingType string `json:"billing_type"`
}
type ServerModificationType struct {
Name string `json:"name"`
}
type ServerModificationTypesOutput struct {
ModificationTypes []ServerModificationType `json:"modification_types"`
}
+23
View File
@@ -93,6 +93,29 @@ func (self *SGuest) GetDetailsVnc(ctx context.Context, userCred mcclient.TokenCr
return ret, nil
}
// 获取公有云可变更配置
// 目前支持: 阿里云,腾讯云,华为云
func (guest *SGuest) GetDetailsModificationTypes(ctx context.Context, userCred mcclient.TokenCredential, input jsonutils.JSONObject) (*api.ServerModificationTypesOutput, error) {
if len(guest.ExternalId) == 0 {
return nil, httperrors.NewNotFoundError("guest external id is empty")
}
vm, err := guest.GetIVM(ctx)
if err != nil {
return nil, errors.Wrapf(err, "GetIVM")
}
ret := &api.ServerModificationTypesOutput{}
ret.ModificationTypes = make([]api.ServerModificationType, 0)
modificationTypes, err := vm.GetModificationTypes()
if err != nil {
return nil, errors.Wrapf(err, "GetModificationTypes")
}
for _, modificationType := range modificationTypes {
ret.ModificationTypes = append(ret.ModificationTypes, api.ServerModificationType{Name: modificationType.InstanceType})
}
return ret, nil
}
func (self *SGuest) PreCheckPerformAction(
ctx context.Context, userCred mcclient.TokenCredential,
action string, query jsonutils.JSONObject, data jsonutils.JSONObject,
+1 -1
View File
@@ -1830,7 +1830,7 @@ sigs.k8s.io/structured-merge-diff/v4/value
# sigs.k8s.io/yaml v1.3.0
## explicit; go 1.12
sigs.k8s.io/yaml
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260409072655-7f5827365741
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260409123851-7b55f5f3ad92
## explicit; go 1.21
yunion.io/x/cloudmux/pkg/apis
yunion.io/x/cloudmux/pkg/apis/billing
+4
View File
@@ -377,3 +377,7 @@ type SInstanceDeployOptions struct {
DeleteKeypair bool
UserData string
}
type SInstanceModificationType struct {
InstanceType string
}
+2
View File
@@ -379,6 +379,8 @@ type ICloudVM interface {
DeployVM(ctx context.Context, opts *SInstanceDeployOptions) error
ChangeConfig(ctx context.Context, config *SManagedVMChangeConfig) error
// 获取实例可变更类型
GetModificationTypes() ([]SInstanceModificationType, error)
GetVNCInfo(input *ServerVncInput) (*ServerVncOutput, error)
// 若有跟随主机删除的选项,需要设置为True
+55
View File
@@ -522,6 +522,61 @@ func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.S
return fmt.Errorf("Failed to change vm config, specification not supported")
}
func (self *SInstance) GetModificationTypes() ([]cloudprovider.SInstanceModificationType, error) {
return self.host.zone.region.GetInstanceModificationTypes(self.InstanceId)
}
func (self *SRegion) GetInstanceModificationTypes(instanceId string) ([]cloudprovider.SInstanceModificationType, error) {
params := map[string]string{
"RegionId": self.RegionId,
"ResourceId": instanceId,
"DestinationResource": "InstanceType",
}
body, err := self.ecsRequest("DescribeResourcesModification", params)
if err != nil {
return nil, errors.Wrapf(err, "DescribeResourcesModification")
}
parsed := struct {
AvailableZones struct {
AvailableZone []struct {
AvailableResources struct {
AvailableResource []struct {
SupportedResources struct {
SupportedResource []struct {
Value string
Status string
StatusCategory string
}
}
}
}
}
}
}{}
if err := body.Unmarshal(&parsed); err != nil {
return nil, errors.Wrapf(err, "body.Unmarshal")
}
ret := make([]cloudprovider.SInstanceModificationType, 0)
seen := map[string]struct{}{}
for _, zone := range parsed.AvailableZones.AvailableZone {
for _, resource := range zone.AvailableResources.AvailableResource {
for _, supportedResource := range resource.SupportedResources.SupportedResource {
if len(supportedResource.Value) == 0 || len(supportedResource.Status) == 0 || strings.ToLower(supportedResource.Status) != "available" {
continue
}
if _, ok := seen[supportedResource.Value]; ok {
continue
}
seen[supportedResource.Value] = struct{}{}
ret = append(ret, cloudprovider.SInstanceModificationType{InstanceType: supportedResource.Value})
}
}
}
return ret, nil
}
func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
return self.host.zone.region.AttachDisk(self.InstanceId, diskId)
}
+25
View File
@@ -682,6 +682,31 @@ func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.S
return fmt.Errorf("Failed to change vm config, specification not supported")
}
func (self *SInstance) GetModificationTypes() ([]cloudprovider.SInstanceModificationType, error) {
return self.host.zone.region.GetInstanceModificationTypes(self.GetId())
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=ListResizeFlavors
func (self *SRegion) GetInstanceModificationTypes(instanceId string) ([]cloudprovider.SInstanceModificationType, error) {
params := url.Values{}
params.Set("instance_uuid", instanceId)
resp, err := self.list(SERVICE_ECS, "cloudservers/resize_flavors", params)
if err != nil {
return nil, errors.Wrapf(err, "ListResizeFlavors(%s)", instanceId)
}
flavors := []SInstanceType{}
err = resp.Unmarshal(&flavors, "flavors")
if err != nil {
return nil, errors.Wrapf(err, "resp.Unmarshal(flavors)")
}
ret := make([]cloudprovider.SInstanceModificationType, 0, len(flavors))
for _, flavor := range flavors {
ret = append(ret, cloudprovider.SInstanceModificationType{InstanceType: flavor.ID})
}
return ret, nil
}
func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
return self.host.zone.region.GetInstanceVNCUrl(self.GetId())
}
+4
View File
@@ -98,3 +98,7 @@ func (ins *SInstanceBase) GetHealthStatus() string {
func (instance *SInstanceBase) GetError() error {
return nil
}
func (instance *SInstanceBase) GetModificationTypes() ([]cloudprovider.SInstanceModificationType, error) {
return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetModificationTypes")
}
+44
View File
@@ -496,6 +496,50 @@ func (self *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SMa
return self.host.zone.region.ChangeVMConfig(self.InstanceId, opts.InstanceType)
}
func (self *SInstance) GetModificationTypes() ([]cloudprovider.SInstanceModificationType, error) {
return self.host.zone.region.GetInstanceModificationTypes(self.InstanceId)
}
func (self *SRegion) GetInstanceModificationTypes(instanceId string) ([]cloudprovider.SInstanceModificationType, error) {
params := map[string]string{
"Region": self.Region,
"InstanceIds.0": instanceId,
}
body, err := self.cvmRequest("DescribeInstancesModification", params, true)
if err != nil {
return nil, errors.Wrapf(err, "DescribeInstancesModification")
}
resp := struct {
InstanceTypeConfigStatusSet []struct {
InstanceTypeConfig struct {
InstanceType string
Zone string
}
Status string
}
}{}
if err := body.Unmarshal(&resp); err != nil {
return nil, errors.Wrapf(err, "body.Unmarshal")
}
ret := make([]cloudprovider.SInstanceModificationType, 0)
seen := map[string]struct{}{}
for _, cfg := range resp.InstanceTypeConfigStatusSet {
if !strings.EqualFold(cfg.Status, "SELL") {
continue
}
if _, ok := seen[cfg.InstanceTypeConfig.InstanceType]; ok {
continue
}
seen[cfg.InstanceTypeConfig.InstanceType] = struct{}{}
ret = append(ret, cloudprovider.SInstanceModificationType{InstanceType: cfg.InstanceTypeConfig.InstanceType})
}
return ret, nil
}
func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
return self.host.zone.region.AttachDisk(self.InstanceId, diskId)
}
+4
View File
@@ -288,3 +288,7 @@ func (self *SInstance) GetPowerStates() string {
func (self *SInstance) GetHealthStatus() string {
return ""
}
func (self *SInstance) GetModificationTypes() ([]cloudprovider.SInstanceModificationType, error) {
return nil, cloudprovider.ErrNotSupported
}