From ddefc2ab34f7fa71be3c47c2f5ad35bb3c02c4ed Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Tue, 13 Oct 2020 21:21:24 +0800 Subject: [PATCH] feature: qcloud create VM with tags --- pkg/apis/input.go | 3 ++ pkg/cloudcommon/db/metadata.go | 5 +++- pkg/cloudcommon/db/standalone.go | 24 +++++++++++++-- pkg/cloudprovider/instance.go | 2 ++ pkg/compute/guestdrivers/managedvirtual.go | 2 ++ pkg/compute/models/networks.go | 17 +++++++++-- pkg/multicloud/qcloud/host.go | 11 ++++--- pkg/multicloud/qcloud/instance.go | 17 +++++++++-- pkg/multicloud/qcloud/region.go | 6 +++- pkg/multicloud/qcloud/shell/instance.go | 34 +++++++++++++++------- 10 files changed, 99 insertions(+), 22 deletions(-) diff --git a/pkg/apis/input.go b/pkg/apis/input.go index 3cf87d0c33..20607fca3a 100644 --- a/pkg/apis/input.go +++ b/pkg/apis/input.go @@ -332,6 +332,9 @@ type GetMetadataInput struct { // | 外部标签 | key以ext:为前缀,为从其他平台同步过来的标签 | // Field []string `json:"field"` + + // 按标签前缀过滤 + Prefix string `json:"prefix"` } // 获取资源标签(元数据)输出 diff --git a/pkg/cloudcommon/db/metadata.go b/pkg/cloudcommon/db/metadata.go index 3730840146..73269b4e99 100644 --- a/pkg/cloudcommon/db/metadata.go +++ b/pkg/cloudcommon/db/metadata.go @@ -585,13 +585,16 @@ func (manager *SMetadataManager) SetAll(ctx context.Context, obj IModel, store m return nil } -func (manager *SMetadataManager) GetAll(obj IModel, keys []string, userCred mcclient.TokenCredential) (map[string]string, error) { +func (manager *SMetadataManager) GetAll(obj IModel, keys []string, keyPrefix string, userCred mcclient.TokenCredential) (map[string]string, error) { idStr := GetObjectIdstr(obj) records := make([]SMetadata, 0) q := manager.Query().Equals("id", idStr) if keys != nil && len(keys) > 0 { q = q.In("key", keys) } + if len(keyPrefix) > 0 { + q = q.Startswith("key", keyPrefix) + } err := FetchModelObjects(manager, q, &records) if err != nil { return nil, err diff --git a/pkg/cloudcommon/db/standalone.go b/pkg/cloudcommon/db/standalone.go index 11e0f8c168..7c2703a809 100644 --- a/pkg/cloudcommon/db/standalone.go +++ b/pkg/cloudcommon/db/standalone.go @@ -295,7 +295,19 @@ func (model *SStandaloneResourceBase) RemoveAllMetadata(ctx context.Context, use } func (model *SStandaloneResourceBase) GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error) { - return Metadata.GetAll(model, nil, userCred) + return Metadata.GetAll(model, nil, "", userCred) +} + +func (model *SStandaloneResourceBase) GetAllUserMetadata() (map[string]string, error) { + meta, err := Metadata.GetAll(model, nil, USER_TAG_PREFIX, nil) + if err != nil { + return nil, errors.Wrap(err, "Metadata.GetAll") + } + ret := make(map[string]string) + for k, v := range meta { + ret[k[len(USER_TAG_PREFIX):]] = v + } + return ret, nil } func (model *SStandaloneResourceBase) AllowGetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { @@ -304,10 +316,18 @@ func (model *SStandaloneResourceBase) AllowGetDetailsMetadata(ctx context.Contex // 获取资源标签(元数据) func (model *SStandaloneResourceBase) GetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, input apis.GetMetadataInput) (apis.GetMetadataOutput, error) { - val, err := Metadata.GetAll(model, input.Field, userCred) + val, err := Metadata.GetAll(model, input.Field, input.Prefix, userCred) if err != nil { return nil, errors.Wrap(err, "Metadata.GetAll") } + if len(input.Prefix) > 0 { + // trim prefix from key + ret := make(map[string]string) + for k, v := range val { + ret[k[len(input.Prefix):]] = v + } + val = ret + } return val, nil } diff --git a/pkg/cloudprovider/instance.go b/pkg/cloudprovider/instance.go index aa852cb477..862ba6264f 100644 --- a/pkg/cloudprovider/instance.go +++ b/pkg/cloudprovider/instance.go @@ -71,6 +71,8 @@ type SManagedVMCreateConfig struct { SPublicIpInfo + Tags map[string]string + BillingCycle *billing.SBillingCycle } diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index 5294daaf23..17e4a4cfb5 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -306,6 +306,8 @@ func (self *SManagedVirtualizedGuestDriver) RequestDeployGuestOnHost(ctx context return errors.Wrapf(err, "desc.GetConfig") } + desc.Tags, _ = guest.GetAllUserMetadata() + //创建并同步安全组规则 { vpc, err := guest.GetVpc() diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index a47ba2b649..941f0e5f67 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -2194,8 +2194,21 @@ func (self *SNetwork) PerformMerge(ctx context.Context, userCred mcclient.TokenC logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false) return nil, err } - if self.WireId != net.WireId || self.GuestGateway != net.GuestGateway { - err = httperrors.NewInputParameterError("Invalid Target Network: %s", input.Target) + + failReason := make([]string, 0) + + if self.WireId != net.WireId { + failReason = append(failReason, "wire_id") + } + if self.GuestGateway != net.GuestGateway { + failReason = append(failReason, "guest_gateway") + } + if self.VlanId != net.VlanId { + failReason = append(failReason, "vlan_id") + } + + if len(failReason) > 0 { + err = httperrors.NewInputParameterError("Invalid Target Network %s: inconsist %s", input.Target, strings.Join(failReason, ",")) logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false) return nil, err } diff --git a/pkg/multicloud/qcloud/host.go b/pkg/multicloud/qcloud/host.go index 6abb867471..f1edc7a317 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.PublicIpBw, desc.PublicIpChargeType) + desc.UserData, desc.BillingCycle, desc.ProjectId, desc.PublicIpBw, desc.PublicIpChargeType, desc.Tags) if err != nil { return nil, err } @@ -77,7 +77,10 @@ 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, publicIpBw int, publicIpChargeType cloudprovider.TElasticipChargeType) (string, error) { + diskSizes []cloudprovider.SDiskInfo, publicKey string, secgroupId string, userData string, bc *billing.SBillingCycle, projectId string, + publicIpBw int, publicIpChargeType cloudprovider.TElasticipChargeType, + tags map[string]string, +) (string, error) { net := self.zone.getNetworkById(networkId) if net == nil { return "", fmt.Errorf("invalid network ID %s", networkId) @@ -127,7 +130,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, publicIpBw, publicIpChargeType) + vmId, err := self.zone.region.CreateInstance(name, imgId, instanceType, secgroupId, self.zone.Zone, desc, passwd, disks, networkId, ipAddr, keypair, userData, bc, projectId, publicIpBw, publicIpChargeType, tags) if err != nil { return "", errors.Wrapf(err, "Failed to create specification %s", instanceType) } @@ -146,7 +149,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, publicIpBw, publicIpChargeType) + vmId, err = self.zone.region.CreateInstance(name, imgId, instanceTypeId, secgroupId, self.zone.Zone, desc, passwd, disks, networkId, ipAddr, keypair, userData, bc, projectId, publicIpBw, publicIpChargeType, tags) 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 8fc3ae4b65..23983605b1 100644 --- a/pkg/multicloud/qcloud/instance.go +++ b/pkg/multicloud/qcloud/instance.go @@ -495,7 +495,9 @@ func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) { 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, - publicIpBw int, publicIpChargeType cloudprovider.TElasticipChargeType) (string, error) { + publicIpBw int, publicIpChargeType cloudprovider.TElasticipChargeType, + tags map[string]string, +) (string, error) { params := make(map[string]string) params["Region"] = self.Region params["ImageId"] = imageId @@ -525,7 +527,7 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st return "", errors.Wrapf(err, "GetBandwidthPackages") } if totalCount > 0 { - bandwidth = 1000 + bandwidth = 65535 // unlimited bandwidth internetChargeType = "BANDWIDTH_PACKAGE" } @@ -557,6 +559,17 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st params["InstanceChargeType"] = "POSTPAID_BY_HOUR" } + // tags + if len(tags) > 0 { + params["TagSpecification.0.ResourceType"] = "instance" + tagIdx := 0 + for k, v := range tags { + params[fmt.Sprintf("TagSpecification.0.Tags.%d.Key", tagIdx)] = k + params[fmt.Sprintf("TagSpecification.0.Tags.%d.Value", tagIdx)] = v + tagIdx += 1 + } + } + //params["IoOptimized"] = "optimized" for i, d := range disks { if i == 0 { diff --git a/pkg/multicloud/qcloud/region.go b/pkg/multicloud/qcloud/region.go index 2b0680a8e0..83009a0627 100644 --- a/pkg/multicloud/qcloud/region.go +++ b/pkg/multicloud/qcloud/region.go @@ -763,7 +763,7 @@ func (self *SRegion) GetMatchInstanceTypes(cpu int, memMB int, gpu int, zoneId s return ret, nil } -func (self *SRegion) CreateInstanceSimple(name string, imgId string, cpu int, memGB int, storageType string, dataDiskSizesGB []int, networkId string, passwd string, publicKey string) (*SInstance, error) { +func (self *SRegion) CreateInstanceSimple(name string, imgId string, cpu int, memGB int, storageType string, dataDiskSizesGB []int, networkId string, passwd string, publicKey string, secgroup string, tags map[string]string) (*SInstance, error) { izones, err := self.GetIZones() if err != nil { return nil, err @@ -783,6 +783,10 @@ func (self *SRegion) CreateInstanceSimple(name string, imgId string, cpu int, me Password: passwd, DataDisks: []cloudprovider.SDiskInfo{}, PublicKey: publicKey, + + Tags: tags, + + ExternalSecgroupId: secgroup, } for _, sizeGB := range dataDiskSizesGB { desc.DataDisks = append(desc.DataDisks, cloudprovider.SDiskInfo{SizeGB: sizeGB, StorageType: storageType}) diff --git a/pkg/multicloud/qcloud/shell/instance.go b/pkg/multicloud/qcloud/shell/instance.go index 30096e1a6c..a44fa437a8 100644 --- a/pkg/multicloud/qcloud/shell/instance.go +++ b/pkg/multicloud/qcloud/shell/instance.go @@ -16,6 +16,7 @@ package shell import ( "fmt" + "strings" "yunion.io/x/onecloud/pkg/multicloud/qcloud" "yunion.io/x/onecloud/pkg/util/shellutils" @@ -38,19 +39,32 @@ func init() { }) type InstanceCreateOptions struct { - NAME string `help:"name of instance"` - IMAGE string `help:"image ID"` - CPU int `help:"CPU count"` - MEMORYGB int `help:"MemoryGB"` - Disk []int `help:"Data disk sizes int GB"` - STORAGE string `help:"Storage type" choices:"LOCAL_BASIC|LOCAL_SSD|CLOUD_BASIC|CLOUD_PREMIUM|CLOUD_SSD"` - NETWORK string `help:"Network ID"` - PASSWD string `help:"password"` - PublicKey string `help:"PublicKey"` + NAME string `help:"name of instance"` + IMAGE string `help:"image ID"` + CPU int `help:"CPU count"` + MEMORYGB int `help:"MemoryGB"` + Disk []int `help:"Data disk sizes int GB"` + STORAGE string `help:"Storage type" choices:"LOCAL_BASIC|LOCAL_SSD|CLOUD_BASIC|CLOUD_PREMIUM|CLOUD_SSD"` + NETWORK string `help:"Network ID"` + PASSWD string `help:"password"` + SECGROUP string `help:"Security group"` + PublicKey string `help:"PublicKey"` + Tag []string `help:"tags"` } shellutils.R(&InstanceCreateOptions{}, "instance-create", "Create a instance", func(cli *qcloud.SRegion, args *InstanceCreateOptions) error { - instance, e := cli.CreateInstanceSimple(args.NAME, args.IMAGE, args.CPU, args.MEMORYGB, args.STORAGE, args.Disk, args.NETWORK, args.PASSWD, args.PublicKey) + tags := make(map[string]string) + if len(args.Tag) > 0 { + for _, t := range args.Tag { + ts := strings.Split(t, ":") + if len(ts) >= 2 { + tags[ts[0]] = ts[1] + } else { + tags[ts[0]] = "" + } + } + } + instance, e := cli.CreateInstanceSimple(args.NAME, args.IMAGE, args.CPU, args.MEMORYGB, args.STORAGE, args.Disk, args.NETWORK, args.PASSWD, args.PublicKey, args.SECGROUP, tags) if e != nil { return e }