From 02ec88ccc6f588364f1883d72f944d06206328ae Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Mon, 12 Oct 2020 20:03:03 +0800 Subject: [PATCH] fix: support public ip create --- pkg/apis/compute/api.go | 20 ++++++++++++++ pkg/cloudprovider/consts.go | 1 + pkg/cloudprovider/eip.go | 7 +++++ pkg/cloudprovider/instance.go | 7 +++++ pkg/compute/guestdrivers/baremetals.go | 4 +-- pkg/compute/guestdrivers/base.go | 4 +++ pkg/compute/guestdrivers/container.go | 4 +-- pkg/compute/guestdrivers/esxi.go | 21 ++++++-------- pkg/compute/guestdrivers/kvm.go | 23 +++++++++++----- pkg/compute/guestdrivers/managedvirtual.go | 29 +++++++++++--------- pkg/compute/guestdrivers/qcloud.go | 4 +++ pkg/compute/models/capabilities.go | 2 ++ pkg/compute/models/guest_actions.go | 3 +- pkg/compute/models/guestdrivers.go | 3 +- pkg/compute/models/guests.go | 20 +++++++++++++- pkg/compute/tasks/guest_batch_create_task.go | 4 +++ pkg/mcclient/options/servers.go | 3 ++ pkg/multicloud/qcloud/host.go | 8 +++--- pkg/multicloud/qcloud/instance.go | 20 ++++++++++---- pkg/multicloud/qcloud/qcloud.go | 1 + 20 files changed, 138 insertions(+), 50 deletions(-) diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index 24a7a3242f..cacac20d6c 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -447,6 +447,7 @@ type ServerCreateInput struct { // 弹性公网IP带宽 // 指定此参数后会创建新的弹性公网IP并绑定到新建的虚拟机 + // 此参数优先级低于public_ip // 私有云不支持此参数 EipBw int `json:"eip_bw,omitzero"` // 弹性公网IP计费类型 @@ -459,6 +460,25 @@ type ServerCreateInput struct { // required: false Eip string `json:"eip,omitempty"` + // 公网IP带宽(单位MB) + // 若指定此参数则忽略eip相关参数 + // 私有云不支持此参数 + // + // + // |平台 |支持范围 | + // |---- |------- | + // |腾讯云 |按量计费1-100, 包年包月1-200 | + PublicIpBw int `json:"public_ip_bw,omitzero"` + // 公网IP计费类型 + // 默认按流量计费 + // + // + // |类别 |说明 | + // |---- |------- | + // |traffic |按流量计费| + // |bandwidth |按带宽计费| + PublicIpChargeType string `json:"public_ip_charge_type,omitempty"` + // 使用主机快照创建虚拟机, 主机快照不会重置密码及秘钥信息 // 使用主机快照创建的虚拟机将沿用之前的密码秘钥及安全组信息 // required: false diff --git a/pkg/cloudprovider/consts.go b/pkg/cloudprovider/consts.go index 7780962a73..70675e4777 100644 --- a/pkg/cloudprovider/consts.go +++ b/pkg/cloudprovider/consts.go @@ -51,6 +51,7 @@ const ( CLOUD_CAPABILITY_EVENT = "event" CLOUD_CAPABILITY_CLOUDID = "cloudid" CLOUD_CAPABILITY_DNSZONE = "dnszone" + CLOUD_CAPABILITY_PUBLIC_IP = "public_ip" ) const ( diff --git a/pkg/cloudprovider/eip.go b/pkg/cloudprovider/eip.go index 4618067b1d..cda9f981fe 100644 --- a/pkg/cloudprovider/eip.go +++ b/pkg/cloudprovider/eip.go @@ -14,6 +14,13 @@ package cloudprovider +type TElasticipChargeType string + +var ( + ElasticipChargeTypeByTraffic = TElasticipChargeType("traffic") + ElasticipChargeTypeByBandwidth = TElasticipChargeType("bandwidth") +) + type SEip struct { Name string BandwidthMbps int diff --git a/pkg/cloudprovider/instance.go b/pkg/cloudprovider/instance.go index 3d4aa74a9f..aa852cb477 100644 --- a/pkg/cloudprovider/instance.go +++ b/pkg/cloudprovider/instance.go @@ -41,6 +41,11 @@ const ( CLOUD_EC2 = "ec2" ) +type SPublicIpInfo struct { + PublicIpBw int + PublicIpChargeType TElasticipChargeType +} + type SManagedVMCreateConfig struct { Name string ExternalImageId string @@ -64,6 +69,8 @@ type SManagedVMCreateConfig struct { UserData string ProjectId string + SPublicIpInfo + BillingCycle *billing.SBillingCycle } diff --git a/pkg/compute/guestdrivers/baremetals.go b/pkg/compute/guestdrivers/baremetals.go index 349a02b67c..bbd0186bd8 100644 --- a/pkg/compute/guestdrivers/baremetals.go +++ b/pkg/compute/guestdrivers/baremetals.go @@ -404,8 +404,8 @@ func (self *SBaremetalGuestDriver) ValidateCreateDataOnHost(ctx context.Context, return input, nil } -func (self *SBaremetalGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { - return guest.GetJsonDescAtBaremetal(ctx, host) +func (self *SBaremetalGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) { + return guest.GetJsonDescAtBaremetal(ctx, host), nil } func (self *SBaremetalGuestDriver) GetGuestVncInfo(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) (*jsonutils.JSONDict, error) { diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index 0277cfee2c..a34b3c4027 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -270,6 +270,10 @@ func (self *SBaseGuestDriver) IsSupportEip() bool { return false } +func (self *SBaseGuestDriver) IsSupportPublicIp() bool { + return false +} + func (self *SBaseGuestDriver) RequestAssociateEip(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, eip *models.SElasticip, task taskman.ITask) error { return fmt.Errorf("SBaseGuestDriver: Not Implement RequestAssociateEip") } diff --git a/pkg/compute/guestdrivers/container.go b/pkg/compute/guestdrivers/container.go index 37c8e0b831..11f82d4e86 100644 --- a/pkg/compute/guestdrivers/container.go +++ b/pkg/compute/guestdrivers/container.go @@ -148,8 +148,8 @@ func (self *SContainerDriver) RequestUndeployGuestOnHost(ctx context.Context, gu return err } -func (self *SContainerDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { - return guest.GetJsonDescAtHypervisor(ctx, host) +func (self *SContainerDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) { + return guest.GetJsonDescAtHypervisor(ctx, host), nil } func (self *SContainerDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { diff --git a/pkg/compute/guestdrivers/esxi.go b/pkg/compute/guestdrivers/esxi.go index 2ef110797f..dfc64c3225 100644 --- a/pkg/compute/guestdrivers/esxi.go +++ b/pkg/compute/guestdrivers/esxi.go @@ -153,40 +153,37 @@ type SEsxiImageInfo struct { StorageCacheHostIp string } -func (self *SESXiGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { +func (self *SESXiGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) { desc := guest.GetJsonDescAtHypervisor(ctx, host) // add image_info disks, _ := desc.GetArray("disks") if len(disks) == 0 { - return desc + return desc, nil } templateId, _ := disks[0].GetString("template_id") if len(templateId) == 0 { - return desc + return desc, nil } model, err := models.CachedimageManager.FetchById(templateId) if err != nil { - log.Errorf("fail to Fetch cachedimage by '%s' in SESXiGuestDriver.GetJsonDescAtHost: %s", templateId, err) - return desc + return desc, errors.Wrapf(err, "CachedimageManager.FetchById(%s)", templateId) } img := model.(*models.SCachedimage) if img.ImageType != cloudprovider.CachedImageTypeSystem { - return desc + return desc, nil } sciSubQ := models.StoragecachedimageManager.Query("storagecache_id").Equals("cachedimage_id", templateId).Equals("status", api.CACHED_IMAGE_STATUS_ACTIVE).SubQuery() scQ := models.StoragecacheManager.Query().In("id", sciSubQ) storageCaches := make([]models.SStoragecache, 0, 1) err = db.FetchModelObjects(models.StoragecacheManager, scQ, &storageCaches) if err != nil { - log.Errorf("fail to fetch storageCache associated with cacheimage '%s'", templateId) - return desc + return desc, errors.Wrapf(err, "fetch storageCache associated with cacheimage %s", templateId) } if len(storageCaches) == 0 { - log.Errorf("no such storage cache associated with cacheimage '%s'", templateId) - return desc + return desc, errors.Errorf("no such storage cache associated with cacheimage %s", templateId) } if len(storageCaches) > 1 { - log.Errorf("there are multiple storageCache associated with caheimage '%s' ??!!", templateId) + return desc, errors.Errorf("there are multiple storageCache associated with caheimage '%s' ??!!", templateId) } var hostIp string @@ -203,7 +200,7 @@ func (self *SESXiGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mc } dict := disks[0].(*jsonutils.JSONDict) dict.Add(jsonutils.Marshal(imageInfo), "image_info") - return desc + return desc, nil } func (self *SESXiGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { diff --git a/pkg/compute/guestdrivers/kvm.go b/pkg/compute/guestdrivers/kvm.go index c2fa738762..7e8eed548c 100644 --- a/pkg/compute/guestdrivers/kvm.go +++ b/pkg/compute/guestdrivers/kvm.go @@ -234,8 +234,8 @@ func (self *SKVMGuestDriver) RequestUndeployGuestOnHost(ctx context.Context, gue return nil } -func (self *SKVMGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { - return guest.GetJsonDescAtHypervisor(ctx, host) +func (self *SKVMGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) { + return guest.GetJsonDescAtHypervisor(ctx, host), nil } func (self *SKVMGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { @@ -267,7 +267,10 @@ func (self *SKVMGuestDriver) RequestStartOnHost(ctx context.Context, guest *mode header := self.getTaskRequestHeader(task) config := jsonutils.NewDict() - desc := guest.GetDriver().GetJsonDescAtHost(ctx, userCred, guest, host) + desc, err := guest.GetDriver().GetJsonDescAtHost(ctx, userCred, guest, host, nil) + if err != nil { + return nil, errors.Wrapf(err, "GetJsonDescAtHost") + } config.Add(desc, "desc") params := task.GetParams() if params.Length() > 0 { @@ -447,7 +450,10 @@ func (self *SKVMGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *mode } func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { - desc := guest.GetDriver().GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host) + desc, err := guest.GetDriver().GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host, nil) + if err != nil { + return errors.Wrapf(err, "GetJsonDescAtHost") + } body := jsonutils.NewDict() body.Add(desc, "desc") if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only { @@ -455,7 +461,7 @@ func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest } url := fmt.Sprintf("%s/servers/%s/sync", host.ManagerUri, guest.Id) header := self.getTaskRequestHeader(task) - _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false) + _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false) return err } @@ -496,13 +502,16 @@ func (self *SKVMGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest * func (self *SKVMGuestDriver) RequestSyncToBackup(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { host := guest.GetHost() - desc := guest.GetDriver().GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host) + desc, err := guest.GetDriver().GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host, nil) + if err != nil { + return errors.Wrapf(err, "GetJsonDescAtHost") + } body := jsonutils.NewDict() body.Add(desc, "desc") body.Set("backup_nbd_server_uri", jsonutils.NewString(guest.GetMetadata("backup_nbd_server_uri", task.GetUserCred()))) url := fmt.Sprintf("%s/servers/%s/drive-mirror", host.ManagerUri, guest.Id) header := self.getTaskRequestHeader(task) - _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false) + _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false) if err != nil { return err } diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index e75a122067..5294daaf23 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -54,12 +54,15 @@ func (d SManagedVirtualizedGuestDriver) DoScheduleMemoryFilter() bool { return f func (d SManagedVirtualizedGuestDriver) DoScheduleStorageFilter() bool { return false } -func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { +func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) { config := cloudprovider.SManagedVMCreateConfig{} config.Name = guest.Name config.Cpu = int(guest.VcpuCount) config.MemoryMB = guest.VmemSize config.Description = guest.Description + if params != nil { + params.Unmarshal(&config.SPublicIpInfo) + } config.InstanceType = guest.InstanceType @@ -117,7 +120,7 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex if guest.BillingType == billing_api.BILLING_TYPE_PREPAID { bc, err := billing.ParseBillingCycle(guest.BillingCycle) if err != nil { - log.Errorf("fail to parse billing cycle %s: %s", guest.BillingCycle, err) + return nil, errors.Wrapf(err, "ParseBillingCycle(%s)", guest.BillingCycle) } if bc.IsValid() { bc.AutoRenew = guest.AutoRenew @@ -125,7 +128,7 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex } } - return jsonutils.Marshal(&config) + return jsonutils.Marshal(&config), nil } func (self *SManagedVirtualizedGuestDriver) RequestGuestCreateAllDisks(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { @@ -293,14 +296,14 @@ func (self *SManagedVirtualizedGuestDriver) RequestStartOnHost(ctx context.Conte func (self *SManagedVirtualizedGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { config, err := guest.GetDeployConfigOnHost(ctx, task.GetUserCred(), host, task.GetParams()) if err != nil { - log.Errorf("GetDeployConfigOnHost error: %v", err) - return err + return errors.Wrapf(err, "GetDeployConfigOnHost") } log.Debugf("RequestDeployGuestOnHost: %s", config) desc := cloudprovider.SManagedVMCreateConfig{} - if err := desc.GetConfig(config); err != nil { - return err + err = desc.GetConfig(config) + if err != nil { + return errors.Wrapf(err, "desc.GetConfig") } //创建并同步安全组规则 @@ -447,10 +450,6 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte } db.SetExternalId(guest, userCred, iVM.GetGlobalId()) - err = iVM.SetSecurityGroups(desc.ExternalSecgroupIds) - if err != nil { - log.Errorf("failed to set multi secgroup for instance %s error: %v", guest.Name, err) - } if hostId := iVM.GetIHostId(); len(hostId) > 0 { host, err := db.FetchByExternalIdAndManagerId(models.HostManager, hostId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { @@ -482,8 +481,12 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte iVM, err = ihost.GetIVMById(iVM.GetGlobalId()) if err != nil { - log.Errorf("cannot find vm %s", err) - return nil, err + return nil, errors.Wrapf(err, "GetIVMById(%s)", iVM.GetGlobalId()) + } + + err = iVM.SetSecurityGroups(desc.ExternalSecgroupIds) + if err != nil { + return nil, errors.Wrapf(err, "SetSecurityGroups") } ret, expect := 0, len(desc.DataDisks)+1 diff --git a/pkg/compute/guestdrivers/qcloud.go b/pkg/compute/guestdrivers/qcloud.go index 5e4a102eba..082e67a302 100644 --- a/pkg/compute/guestdrivers/qcloud.go +++ b/pkg/compute/guestdrivers/qcloud.go @@ -259,6 +259,10 @@ func (self *SQcloudGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle return false } +func (self *SQcloudGuestDriver) IsSupportPublicIp() bool { + return true +} + func (self *SQcloudGuestDriver) IsSupportPublicipToEip() bool { return true } diff --git a/pkg/compute/models/capabilities.go b/pkg/compute/models/capabilities.go index 66d60ccc27..7156c7d226 100644 --- a/pkg/compute/models/capabilities.go +++ b/pkg/compute/models/capabilities.go @@ -44,6 +44,7 @@ type SCapabilities struct { DisabledComputeEngineBrands []string `json:",allowempty"` CloudIdBrands []string `json:",allowempty"` DisabledCloudIdBrands []string `json:",allowempty"` + PublicIpBrands []string `json:",allowempty"` NetworkManageBrands []string `json:",allowempty"` DisabledNetworkManageBrands []string `json:",allowempty"` ObjectStorageBrands []string `json:",allowempty"` @@ -262,6 +263,7 @@ func getBrands(region *SCloudregion, zone *SZone, domainId string, capa *SCapabi capa.NetworkManageBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_NETWORK) capa.ObjectStorageBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE) capa.CloudIdBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_CLOUDID) + capa.PublicIpBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_PUBLIC_IP) if utils.IsInStringArray(api.HYPERVISOR_KVM, capa.Hypervisors) || utils.IsInStringArray(api.HYPERVISOR_BAREMETAL, capa.Hypervisors) { capa.Brands = append(capa.Brands, api.ONECLOUD_BRAND_ONECLOUD) diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index c7377fa805..b931380168 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -169,8 +169,7 @@ func (self *SGuest) GetDetailsDesc(ctx context.Context, userCred mcclient.TokenC if host == nil { return nil, httperrors.NewInvalidStatusError("No host for server") } - desc := self.GetDriver().GetJsonDescAtHost(ctx, userCred, self, host) - return desc, nil + return self.GetDriver().GetJsonDescAtHost(ctx, userCred, self, host, nil) } func (self *SGuest) AllowPerformSaveImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { diff --git a/pkg/compute/models/guestdrivers.go b/pkg/compute/models/guestdrivers.go index 226e11aa16..9822acb665 100644 --- a/pkg/compute/models/guestdrivers.go +++ b/pkg/compute/models/guestdrivers.go @@ -56,7 +56,7 @@ type IGuestDriver interface { RequestRenewInstance(guest *SGuest, bc billing.SBillingCycle) (time.Time, error) - GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, host *SHost) jsonutils.JSONObject + GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, host *SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) ValidateImage(ctx context.Context, image *cloudprovider.SImage) error ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, data *api.ServerCreateInput) (*api.ServerCreateInput, error) @@ -169,6 +169,7 @@ type IGuestDriver interface { RequestSyncToBackup(ctx context.Context, guest *SGuest, task taskman.ITask) error IsSupportEip() bool + IsSupportPublicIp() bool ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error RequestAssociateEip(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, eip *SElasticip, task taskman.ITask) error diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 4952763112..f629482f53 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -1523,6 +1523,21 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m func (manager *SGuestManager) validateEip(userCred mcclient.TokenCredential, input *api.ServerCreateInput, preferRegionId string, preferManagerId string) error { + if input.PublicIpBw > 0 { + if !GetDriver(input.Hypervisor).IsSupportPublicIp() { + return httperrors.NewNotImplementedError("public ip not supported for %s", input.Hypervisor) + } + if len(input.PublicIpChargeType) == 0 { + input.PublicIpChargeType = string(cloudprovider.ElasticipChargeTypeByTraffic) + } + if !utils.IsInStringArray(input.PublicIpChargeType, []string{ + string(cloudprovider.ElasticipChargeTypeByTraffic), + string(cloudprovider.ElasticipChargeTypeByBandwidth), + }) { + return httperrors.NewInputParameterError("invalid public_ip_charge_type %s", input.PublicIpChargeType) + } + return nil + } eipStr := input.Eip eipBw := input.EipBw if len(eipStr) > 0 || eipBw > 0 { @@ -3676,7 +3691,10 @@ func (self *SGuest) isNeedDoResetPasswd() bool { func (self *SGuest) GetDeployConfigOnHost(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, params *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { config := jsonutils.NewDict() - desc := self.GetDriver().GetJsonDescAtHost(ctx, userCred, self, host) + desc, err := self.GetDriver().GetJsonDescAtHost(ctx, userCred, self, host, params) + if err != nil { + return nil, errors.Wrapf(err, "GetJsonDescAtHost") + } config.Add(desc, "desc") deploys, err := cmdline.FetchDeployConfigsByJSON(params) diff --git a/pkg/compute/tasks/guest_batch_create_task.go b/pkg/compute/tasks/guest_batch_create_task.go index 4dffe85c68..80604d2fbe 100644 --- a/pkg/compute/tasks/guest_batch_create_task.go +++ b/pkg/compute/tasks/guest_batch_create_task.go @@ -176,6 +176,10 @@ func (self *GuestBatchCreateTask) allocateGuestOnHost(ctx context.Context, guest return err } + if input.PublicIpBw > 0 { + input.Eip, input.EipBw = "", 0 + } + // allocate eips if input.EipBw > 0 { eip, err := models.ElasticipManager.NewEipForVMOnHost(ctx, self.UserCred, guest, host, input.EipBw, input.EipChargeType, input.EipAutoDellocate, &pendingRegionUsage) diff --git a/pkg/mcclient/options/servers.go b/pkg/mcclient/options/servers.go index d93a25ad11..bc0610bf79 100644 --- a/pkg/mcclient/options/servers.go +++ b/pkg/mcclient/options/servers.go @@ -387,6 +387,9 @@ type ServerCreateOptionalOptions struct { EipChargeType string `help:"newly allocated EIP charge type" choices:"traffic|bandwidth" json:"eip_charge_type,omitempty"` Eip string `help:"associate with an existing EIP when server is created" json:"eip,omitempty"` + PublicIpBw int `help:"associate public ip with bandwidth in MB where server is created" json:"public_ip_bw,omitzero"` + PublicIpChargeType string `help:"newly allocated public ip charge type" choices:"traffic|bandwidth" json:"public_ip_charge_type,omitempty"` + GuestImageID string `help:"create from guest image, need to specify the guest image id"` } diff --git a/pkg/multicloud/qcloud/host.go b/pkg/multicloud/qcloud/host.go index 866b02c51a..6abb867471 100644 --- a/pkg/multicloud/qcloud/host.go +++ b/pkg/multicloud/qcloud/host.go @@ -64,7 +64,7 @@ func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudpr desc.InstanceType, desc.ExternalNetworkId, desc.IpAddr, desc.Description, desc.Password, desc.DataDisks, desc.PublicKey, desc.ExternalSecgroupId, - desc.UserData, desc.BillingCycle, desc.ProjectId) + desc.UserData, desc.BillingCycle, desc.ProjectId, desc.PublicIpBw, desc.PublicIpChargeType) if err != nil { return nil, err } @@ -77,7 +77,7 @@ func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudpr func (self *SHost) _createVM(name string, imgId string, sysDisk cloudprovider.SDiskInfo, cpu int, memMB int, instanceType string, networkId string, ipAddr string, desc string, passwd string, - diskSizes []cloudprovider.SDiskInfo, publicKey string, secgroupId string, userData string, bc *billing.SBillingCycle, projectId string) (string, error) { + diskSizes []cloudprovider.SDiskInfo, publicKey string, secgroupId string, userData string, bc *billing.SBillingCycle, projectId string, publicIpBw int, publicIpChargeType cloudprovider.TElasticipChargeType) (string, error) { net := self.zone.getNetworkById(networkId) if net == nil { return "", fmt.Errorf("invalid network ID %s", networkId) @@ -127,7 +127,7 @@ func (self *SHost) _createVM(name string, imgId string, sysDisk cloudprovider.SD if len(instanceType) > 0 { log.Debugf("Try instancetype : %s", instanceType) - vmId, err := self.zone.region.CreateInstance(name, imgId, instanceType, secgroupId, self.zone.Zone, desc, passwd, disks, networkId, ipAddr, keypair, userData, bc, projectId) + vmId, err := self.zone.region.CreateInstance(name, imgId, instanceType, secgroupId, self.zone.Zone, desc, passwd, disks, networkId, ipAddr, keypair, userData, bc, projectId, publicIpBw, publicIpChargeType) if err != nil { return "", errors.Wrapf(err, "Failed to create specification %s", instanceType) } @@ -146,7 +146,7 @@ func (self *SHost) _createVM(name string, imgId string, sysDisk cloudprovider.SD for _, instType := range instanceTypes { instanceTypeId := instType.InstanceType log.Debugf("Try instancetype : %s", instanceTypeId) - vmId, err = self.zone.region.CreateInstance(name, imgId, instanceTypeId, secgroupId, self.zone.Zone, desc, passwd, disks, networkId, ipAddr, keypair, userData, bc, projectId) + vmId, err = self.zone.region.CreateInstance(name, imgId, instanceTypeId, secgroupId, self.zone.Zone, desc, passwd, disks, networkId, ipAddr, keypair, userData, bc, projectId, publicIpBw, publicIpChargeType) if err != nil { log.Errorf("Failed for %s: %s", instanceTypeId, err) } else { diff --git a/pkg/multicloud/qcloud/instance.go b/pkg/multicloud/qcloud/instance.go index d88e463e58..8fc3ae4b65 100644 --- a/pkg/multicloud/qcloud/instance.go +++ b/pkg/multicloud/qcloud/instance.go @@ -489,13 +489,13 @@ func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) { if instances[0].InstanceState == "LAUNCH_FAILED" { return nil, cloudprovider.ErrNotFound } - log.Debugf("%#v", instances) return &instances[0], nil } func (self *SRegion) CreateInstance(name string, imageId string, instanceType string, securityGroupId string, zoneId string, desc string, passwd string, disks []SDisk, networkId string, ipAddr string, - keypair string, userData string, bc *billing.SBillingCycle, projectId string) (string, error) { + keypair string, userData string, bc *billing.SBillingCycle, projectId string, + publicIpBw int, publicIpChargeType cloudprovider.TElasticipChargeType) (string, error) { params := make(map[string]string) params["Region"] = self.Region params["ImageId"] = imageId @@ -508,12 +508,18 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st params["InstanceName"] = name params["HostName"] = name - bandwidth := 100 - if bc != nil { - bandwidth = 200 + bandwidth := publicIpBw + if publicIpBw == 0 { + bandwidth = 100 + if bc != nil { + bandwidth = 200 + } } internetChargeType := "TRAFFIC_POSTPAID_BY_HOUR" + if publicIpChargeType == cloudprovider.ElasticipChargeTypeByBandwidth { + internetChargeType = "BANDWIDTH_PREPAID" + } _, totalCount, err := self.GetBandwidthPackages([]string{}, 0, 50) if err != nil { return "", errors.Wrapf(err, "GetBandwidthPackages") @@ -525,7 +531,9 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st params["InternetAccessible.InternetChargeType"] = internetChargeType params["InternetAccessible.InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bandwidth) - params["InternetAccessible.PublicIpAssigned"] = "FALSE" + if publicIpBw == 0 { + params["InternetAccessible.PublicIpAssigned"] = "FALSE" + } if len(keypair) > 0 { params["LoginSettings.KeyIds.0"] = keypair } else if len(passwd) > 0 { diff --git a/pkg/multicloud/qcloud/qcloud.go b/pkg/multicloud/qcloud/qcloud.go index cbe71db258..981e85cbbc 100644 --- a/pkg/multicloud/qcloud/qcloud.go +++ b/pkg/multicloud/qcloud/qcloud.go @@ -835,6 +835,7 @@ func (self *SQcloudClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_EVENT, cloudprovider.CLOUD_CAPABILITY_CLOUDID, cloudprovider.CLOUD_CAPABILITY_DNSZONE, + cloudprovider.CLOUD_CAPABILITY_PUBLIC_IP, } return caps }