diff --git a/pkg/apis/compute/elasticcache.go b/pkg/apis/compute/elasticcache.go index bddf7ab1a8..32c9b78bbc 100644 --- a/pkg/apis/compute/elasticcache.go +++ b/pkg/apis/compute/elasticcache.go @@ -32,6 +32,9 @@ type ElasticcacheDetails struct { // 关联安全组列表 Secgroups []apis.StandaloneShortDesc `json:"secgroups"` + + // 备可用区列表 + SlaveZoneInfos []apis.StandaloneShortDesc `json:"slave_zone_infos"` } type ElasticcacheResourceInfo struct { diff --git a/pkg/apis/compute/elasticcachesku.go b/pkg/apis/compute/elasticcachesku.go index f0356f9e14..2735425a64 100644 --- a/pkg/apis/compute/elasticcachesku.go +++ b/pkg/apis/compute/elasticcachesku.go @@ -20,6 +20,7 @@ type ElasticcacheSkuDetails struct { apis.StatusStandaloneResourceDetails CloudregionResourceInfo ZoneResourceInfoBase + SlaveZoneResourceInfoBase SElasticcacheSku } diff --git a/pkg/apis/compute/storage_const.go b/pkg/apis/compute/storage_const.go index 5668d8a665..d252cca974 100644 --- a/pkg/apis/compute/storage_const.go +++ b/pkg/apis/compute/storage_const.go @@ -45,6 +45,7 @@ const ( // aws storage type STORAGE_GP2_SSD = "gp2" // aws general purpose ssd STORAGE_IO1_SSD = "io1" // aws Provisioned IOPS SSD + STORAGE_IO2_SSD = "io2" // aws Provisioned IOPS 2 SSD STORAGE_ST1_HDD = "st1" // aws Throughput Optimized HDD STORAGE_SC1_HDD = "sc1" // aws Cold HDD STORAGE_STANDARD_HDD = "standard" // aws Magnetic volumes diff --git a/pkg/apis/compute/zone.go b/pkg/apis/compute/zone.go index 47b727858b..35f65c121c 100644 --- a/pkg/apis/compute/zone.go +++ b/pkg/apis/compute/zone.go @@ -104,6 +104,15 @@ type Zone1ResourceInfoBase struct { Zone1ExtId string `json:"zone_1_ext_id"` } +type SlaveZoneResourceInfoBase struct { + // 可用区名称 + // example: 北京2区 + SlaveZone string `json:"slave_zone"` + + // 纳管云的zoneId + SlaveZoneExtId string `json:"slave_zone_ext_id"` +} + type ZoneResourceInfo struct { ZoneResourceInfoBase diff --git a/pkg/compute/guestdrivers/aws.go b/pkg/compute/guestdrivers/aws.go index 25775c6346..8feb7c1a85 100644 --- a/pkg/compute/guestdrivers/aws.go +++ b/pkg/compute/guestdrivers/aws.go @@ -125,6 +125,7 @@ func (self *SAwsGuestDriver) GetStorageTypes() []string { return []string{ api.STORAGE_GP2_SSD, api.STORAGE_IO1_SSD, + api.STORAGE_IO2_SSD, api.STORAGE_ST1_HDD, api.STORAGE_SC1_HDD, api.STORAGE_STANDARD_HDD, @@ -181,10 +182,10 @@ func (self *SAwsGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *mode if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) { return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status) } - if disk.DiskType == api.DISK_TYPE_SYS && !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_IO1_SSD, api.STORAGE_STANDARD_HDD, api.STORAGE_GP2_SSD}) { + if disk.DiskType == api.DISK_TYPE_SYS && !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_IO1_SSD, api.STORAGE_IO2_SSD, api.STORAGE_STANDARD_HDD, api.STORAGE_GP2_SSD}) { return fmt.Errorf("Cannot resize system disk with unsupported volumes type %s", storage.StorageType) } - if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_GP2_SSD, api.STORAGE_IO1_SSD, api.STORAGE_ST1_HDD, api.STORAGE_SC1_HDD, api.STORAGE_STANDARD_HDD}) { + if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_GP2_SSD, api.STORAGE_IO1_SSD, api.STORAGE_IO2_SSD, api.STORAGE_ST1_HDD, api.STORAGE_SC1_HDD, api.STORAGE_STANDARD_HDD}) { return fmt.Errorf("Cannot resize %s disk", storage.StorageType) } return nil diff --git a/pkg/compute/hostdrivers/aws.go b/pkg/compute/hostdrivers/aws.go index 7fd9657918..6fbe76d78c 100644 --- a/pkg/compute/hostdrivers/aws.go +++ b/pkg/compute/hostdrivers/aws.go @@ -49,7 +49,7 @@ func (self *SAwsHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb in if sizeGb < 1 || sizeGb > 16384 { return fmt.Errorf("The %s disk size must be in the range of 1G ~ 16384GB", storage.StorageType) } - } else if storage.StorageType == api.STORAGE_IO1_SSD { + } else if storage.StorageType == api.STORAGE_IO1_SSD || storage.StorageType == api.STORAGE_IO2_SSD { if sizeGb < 4 || sizeGb > 16384 { return fmt.Errorf("The %s disk size must be in the range of 4G ~ 16384GB", storage.StorageType) } diff --git a/pkg/compute/models/elasticcache_instances.go b/pkg/compute/models/elasticcache_instances.go index 55ef363590..ea3a1f3b73 100644 --- a/pkg/compute/models/elasticcache_instances.go +++ b/pkg/compute/models/elasticcache_instances.go @@ -218,6 +218,7 @@ func (manager *SElasticcacheManager) FetchCustomizeColumns( netIds := make([]string, len(objs)) cacheIds := make([]string, len(objs)) + zoneIds := []string{} for i := range rows { rows[i] = api.ElasticcacheDetails{ VirtualResourceDetails: virtRows[i], @@ -226,6 +227,13 @@ func (manager *SElasticcacheManager) FetchCustomizeColumns( } netIds[i] = objs[i].(*SElasticcache).NetworkId cacheIds[i] = objs[i].(*SElasticcache).Id + + sz := strings.Split(objs[i].(*SElasticcache).SlaveZones, ",") + for j := range sz { + if !utils.IsInStringArray(sz[j], zoneIds) { + zoneIds = append(zoneIds, sz[j]) + } + } } networks := make(map[string]SNetwork) @@ -254,9 +262,50 @@ func (manager *SElasticcacheManager) FetchCustomizeColumns( } } + // zone ids + if len(zoneIds) > 0 { + zss := fetchElasticcacheSlaveZones(zoneIds) + for i := range objs { + if len(objs[i].(*SElasticcache).SlaveZones) > 0 { + sz := strings.Split(objs[i].(*SElasticcache).SlaveZones, ",") + szi := []apis.StandaloneShortDesc{} + for j := range sz { + if info, ok := zss[sz[j]]; ok { + szi = append(szi, info) + } else { + szi = append(szi, apis.StandaloneShortDesc{Id: sz[j], Name: sz[j]}) + } + } + + rows[i].SlaveZoneInfos = szi + } + } + } + return rows } +func fetchElasticcacheSlaveZones(zoneIds []string) map[string]apis.StandaloneShortDesc { + zones := []SZone{} + err := ZoneManager.Query().In("id", zoneIds).All(&zones) + if err != nil { + log.Debugf("fetchElasticcacheSlaveZones.ZoneManager %s", err) + return nil + } + + ret := make(map[string]apis.StandaloneShortDesc) + for i := range zones { + zsd := apis.StandaloneShortDesc{ + Id: zones[i].Id, + Name: zones[i].Name, + } + + ret[zsd.Id] = zsd + } + + return ret +} + func (self *SElasticcache) GetElasticcacheParameters() ([]SElasticcacheParameter, error) { ret := []SElasticcacheParameter{} q := ElasticcacheParameterManager.Query().Equals("elasticcache_id", self.Id) diff --git a/pkg/compute/models/elasticcache_skus.go b/pkg/compute/models/elasticcache_skus.go index 730ae67101..63beb06af2 100644 --- a/pkg/compute/models/elasticcache_skus.go +++ b/pkg/compute/models/elasticcache_skus.go @@ -135,12 +135,44 @@ func (manager *SElasticcacheSkuManager) FetchCustomizeColumns( stdRows := manager.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) regRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + slavezoneRows := manager.FetchSlaveZoneResourceInfos(ctx, userCred, query, objs) for i := range rows { rows[i] = api.ElasticcacheSkuDetails{ StatusStandaloneResourceDetails: stdRows[i], CloudregionResourceInfo: regRows[i], ZoneResourceInfoBase: zoneRows[i].ZoneResourceInfoBase, + SlaveZoneResourceInfoBase: slavezoneRows[i], + } + } + + return rows +} + +func (self *SElasticcacheSkuManager) FetchSlaveZoneResourceInfos(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}) []api.SlaveZoneResourceInfoBase { + rows := make([]api.SlaveZoneResourceInfoBase, len(objs)) + zoneIds := []string{} + for i := range objs { + slavezone := objs[i].(*SElasticcacheSku).SlaveZoneId + if len(slavezone) > 0 { + zoneIds = append(zoneIds, slavezone) + } + } + + zones := make(map[string]SZone) + err := db.FetchStandaloneObjectsByIds(ZoneManager, zoneIds, &zones) + if err != nil { + log.Errorf("FetchStandaloneObjectsByIds fail %s", err) + return rows + } + + for i := range objs { + if zone, ok := zones[objs[i].(*SElasticcacheSku).SlaveZoneId]; ok { + rows[i].SlaveZone = zone.GetName() + rows[i].SlaveZoneExtId = fetchExternalId(zone.GetExternalId()) } } diff --git a/pkg/multicloud/aws/disk.go b/pkg/multicloud/aws/disk.go index ada1598716..01995ecf13 100644 --- a/pkg/multicloud/aws/disk.go +++ b/pkg/multicloud/aws/disk.go @@ -485,7 +485,7 @@ func (self *SRegion) CreateDisk(zoneId string, category string, name string, siz params.SetSnapshotId(snapshotId) } - if category == api.STORAGE_IO1_SSD { + if category == api.STORAGE_IO1_SSD || category == api.STORAGE_IO2_SSD { params.SetIops(200) } diff --git a/pkg/multicloud/aws/instance.go b/pkg/multicloud/aws/instance.go index 21002cb2ce..ed3a94cce0 100644 --- a/pkg/multicloud/aws/instance.go +++ b/pkg/multicloud/aws/instance.go @@ -763,6 +763,13 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st } else { ebs.SetIops(32000) } + } else if disk.Category == api.STORAGE_IO2_SSD { + iops := int64(disk.Size * 100) + if iops < 64000 { + ebs.SetIops(iops) + } else { + ebs.SetIops(64000) + } } blockDevice := &ec2.BlockDeviceMapping{ diff --git a/pkg/multicloud/aws/storage.go b/pkg/multicloud/aws/storage.go index 9550c9423e..b5bff5010f 100644 --- a/pkg/multicloud/aws/storage.go +++ b/pkg/multicloud/aws/storage.go @@ -91,7 +91,7 @@ func (self *SStorage) GetStorageType() string { } func (self *SStorage) GetMediumType() string { - if self.storageType == api.STORAGE_GP2_SSD || self.storageType == api.STORAGE_IO1_SSD { + if self.storageType == api.STORAGE_GP2_SSD || self.storageType == api.STORAGE_IO1_SSD || self.storageType == api.STORAGE_IO2_SSD { return api.DISK_TYPE_SSD } else { return api.DISK_TYPE_ROTATE diff --git a/pkg/multicloud/aws/zone.go b/pkg/multicloud/aws/zone.go index 6b12485bb2..91e5b01886 100644 --- a/pkg/multicloud/aws/zone.go +++ b/pkg/multicloud/aws/zone.go @@ -20,6 +20,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -65,6 +66,9 @@ func (self *SZone) getHost() *SHost { func (self *SZone) getStorageType() { if len(self.storageTypes) == 0 { self.storageTypes = StorageTypes + if !utils.IsInStringArray(self.GetId(), []string{"af-south-1", "eu-south-1", "eu-west-3", "sa-east-1", "cn-north-1", "cn-northwest-1"}) { + self.storageTypes = append(self.storageTypes, api.STORAGE_IO2_SSD) + } } } diff --git a/pkg/multicloud/huawei/client/modules/mod_disks.go b/pkg/multicloud/huawei/client/modules/mod_disks.go index 8488f315d8..be68003211 100644 --- a/pkg/multicloud/huawei/client/modules/mod_disks.go +++ b/pkg/multicloud/huawei/client/modules/mod_disks.go @@ -66,3 +66,11 @@ func (self *SDiskManager) AsyncCreate(params jsonutils.JSONObject) (string, erro // 包年包月机器 return ret.GetString("order_id") } + +// https://support.huaweicloud.com/api-evs/evs_04_2003.html +func (self *SDiskManager) GetDiskTypes() (*responses.ListResult, error) { + originKeyword := self.ResourceKeyword + self.ResourceKeyword = "" + defer func() { self.ResourceKeyword = originKeyword }() + return self.ListInContextWithSpec(self.ctx, "types", nil, "volume_types") +} diff --git a/pkg/multicloud/huawei/disktype.go b/pkg/multicloud/huawei/disktype.go new file mode 100644 index 0000000000..534d90987e --- /dev/null +++ b/pkg/multicloud/huawei/disktype.go @@ -0,0 +1,26 @@ +package huawei + +import "strings" + +type SDiskType struct { + ExtraSpecs ExtraSpecs `json:"extra_specs"` + Name string `json:"name"` + QosSpecsID string `json:"qos_specs_id"` + ID string `json:"id"` + IsPublic bool `json:"is_public"` +} + +type ExtraSpecs struct { + VolumeBackendName string `json:"volume_backend_name"` + AvailabilityZone string `json:"availability-zone"` + RESKEYAvailabilityZones string `json:"RESKEY:availability_zones"` + OSVendorExtendedSoldOutAvailabilityZones string `json:"os-vendor-extended:sold_out_availability_zones"` +} + +func (self *SDiskType) IsAvaliableInZone(zoneId string) bool { + if len(self.QosSpecsID) > 0 && strings.Contains(zoneId, self.ExtraSpecs.RESKEYAvailabilityZones) && !strings.Contains(zoneId, self.ExtraSpecs.OSVendorExtendedSoldOutAvailabilityZones) { + return true + } + + return false +} diff --git a/pkg/multicloud/huawei/region.go b/pkg/multicloud/huawei/region.go index 713bcce40f..a32f203991 100644 --- a/pkg/multicloud/huawei/region.go +++ b/pkg/multicloud/huawei/region.go @@ -1007,3 +1007,35 @@ func (self *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, er func (region *SRegion) GetCapabilities() []string { return region.client.GetCapabilities() } + +func (self *SRegion) GetDiskTypes() ([]SDiskType, error) { + ret, err := self.ecsClient.Disks.GetDiskTypes() + if err != nil { + return nil, errors.Wrap(err, "GetDiskTypes") + } + + dts := []SDiskType{} + _ret := jsonutils.NewArray(ret.Data...) + err = _ret.Unmarshal(&dts) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + + return dts, nil +} + +func (self *SRegion) GetZoneSupportedDiskTypes(zoneId string) ([]string, error) { + dts, err := self.GetDiskTypes() + if err != nil { + return nil, errors.Wrap(err, "GetDiskTypes") + } + + ret := []string{} + for i := range dts { + if dts[i].IsAvaliableInZone(zoneId) { + ret = append(ret, dts[i].Name) + } + } + + return ret, nil +} diff --git a/pkg/multicloud/huawei/shell/disk.go b/pkg/multicloud/huawei/shell/disk.go index fa10a563e8..f6ead5656d 100644 --- a/pkg/multicloud/huawei/shell/disk.go +++ b/pkg/multicloud/huawei/shell/disk.go @@ -42,4 +42,13 @@ func init() { } return nil }) + + shellutils.R(&DiskListOptions{}, "disk-types", "List disk types", func(cli *huawei.SRegion, args *DiskListOptions) error { + ret, e := cli.GetDiskTypes() + if e != nil { + return e + } + printList(ret, 0, 0, 0, nil) + return nil + }) } diff --git a/pkg/multicloud/huawei/zone.go b/pkg/multicloud/huawei/zone.go index bb4e739393..68337a1b18 100644 --- a/pkg/multicloud/huawei/zone.go +++ b/pkg/multicloud/huawei/zone.go @@ -58,7 +58,11 @@ func (self *SZone) addWire(wire *SWire) { func (self *SZone) getStorageType() { if len(self.storageTypes) == 0 { - self.storageTypes = StorageTypes + if sts, err := self.region.GetZoneSupportedDiskTypes(self.GetId()); err == nil { + self.storageTypes = sts + } else { + self.storageTypes = StorageTypes + } } }