diff --git a/pkg/apis/compute/storage_const.go b/pkg/apis/compute/storage_const.go index 752aa6f827..6f371b8f19 100644 --- a/pkg/apis/compute/storage_const.go +++ b/pkg/apis/compute/storage_const.go @@ -59,6 +59,7 @@ const ( // openstack STORAGE_OPENSTACK_ISCSI = "iscsi" + STORAGE_OPENSTACK_NOVA = "nova" // Ucloud storage type STORAGE_UCLOUD_CLOUD_NORMAL = "CLOUD_NORMAL" // 普通云盘 diff --git a/pkg/compute/guestdrivers/openstack.go b/pkg/compute/guestdrivers/openstack.go index 6ae4c893e7..12b5094f5d 100644 --- a/pkg/compute/guestdrivers/openstack.go +++ b/pkg/compute/guestdrivers/openstack.go @@ -17,12 +17,18 @@ package guestdrivers import ( "context" "fmt" + "time" "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/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/compute/options" @@ -71,7 +77,7 @@ func (self *SOpenStackGuestDriver) IsSupportEip() bool { } func (self *SOpenStackGuestDriver) GetDefaultSysDiskBackend() string { - return api.STORAGE_OPENSTACK_ISCSI + return api.STORAGE_OPENSTACK_NOVA } func (self *SOpenStackGuestDriver) GetMinimalSysDiskSizeGb() int { @@ -112,7 +118,7 @@ func (self *SOpenStackGuestDriver) IsNeedRestartForResetLoginInfo() bool { } func (self *SOpenStackGuestDriver) IsRebuildRootSupportChangeImage() bool { - return false + return true } func (self *SOpenStackGuestDriver) GetDeployStatus() ([]string, error) { @@ -142,6 +148,13 @@ func (self *SOpenStackGuestDriver) ValidateCreateData(ctx context.Context, userC if len(input.Eip) > 0 || input.EipBw > 0 { return nil, httperrors.NewUnsupportOperationError("%s not support create virtual machine with eip", self.GetHypervisor()) } + for i := 1; i < len(input.Disks); i++ { + disk := input.Disks[i] + if disk.Backend == api.STORAGE_OPENSTACK_NOVA { + return nil, httperrors.NewUnsupportOperationError("data disk not support storage type %s", disk.Backend) + } + } + return input, nil } @@ -149,6 +162,154 @@ func (self *SOpenStackGuestDriver) GetGuestInitialStateAfterCreate() string { return api.VM_RUNNING } +func (self *SOpenStackGuestDriver) attachDisks(ctx context.Context, ihost cloudprovider.ICloudHost, instanceId string, diskIds []string) { + if len(diskIds) == 0 { + return + } + iVM, err := ihost.GetIVMById(instanceId) + if err != nil || iVM == nil { + log.Errorf("cannot find vm %s", instanceId) + return + } + for _, diskId := range diskIds { + err = iVM.AttachDisk(ctx, diskId) + if err != nil { + log.Errorf("failed to attach disk %s", diskId) + } + } + return +} + +func (self *SOpenStackGuestDriver) RemoteDeployGuestForRebuildRoot(ctx context.Context, guest *models.SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error) { + iVM, err := ihost.GetIVMById(guest.GetExternalId()) + if err != nil || iVM == nil { + return nil, fmt.Errorf("cannot find vm %s(%s)", guest.Id, guest.Name) + } + + instanceId := iVM.GetGlobalId() + + diskId, err := func() (string, error) { + lockman.LockObject(ctx, guest) + defer lockman.ReleaseObject(ctx, guest) + + sysDisk, err := guest.GetSystemDisk() + if err != nil { + return "", errors.Wrap(err, "guest.GetSystemDisk(") + } + storage := sysDisk.GetStorage() + if storage.StorageType == api.STORAGE_OPENSTACK_NOVA { //不通过镜像创建磁盘的机器 + return iVM.RebuildRoot(ctx, desc.ExternalImageId, desc.Password, desc.PublicKey, desc.SysDisk.SizeGB) + } + + iDisks, err := iVM.GetIDisks() + if err != nil { + return "", errors.Wrap(err, "iVM.GetIDisks") + } + + detachDisks := []string{} + for i, iDisk := range iDisks { + if i != 0 { + err = iVM.DetachDisk(ctx, iDisk.GetGlobalId()) + if err != nil { + return "", errors.Wrap(err, "iVM.DetachDisk") + } + detachDisks = append(detachDisks, iDisk.GetGlobalId()) + } + } + defer self.attachDisks(ctx, ihost, instanceId, detachDisks) + + eip, err := guest.GetEip() + if err == nil && eip != nil { + ieip, err := eip.GetIEip() + if err != nil { + return "", errors.Wrap(err, "eip.GetIEip") + } + err = ieip.Dissociate() + if err != nil { + return "", errors.Wrap(err, "ieip.Dissociate") + } + defer ieip.Associate(instanceId) + } + err = iVM.DeleteVM(ctx) + if err != nil { + return "", errors.Wrap(err, "iVM.DeleteVM") + } + err = cloudprovider.WaitDeleted(iVM, time.Second*5, time.Minute*10) + if err != nil { + return "", errors.Wrap(err, "WaitDeleted") + } + desc.DataDisks = []cloudprovider.SDiskInfo{} + iVM, err = ihost.CreateVM(&desc) + if err != nil { + return "", errors.Wrap(err, "ihost.CreateVM") + } + + instanceId = iVM.GetGlobalId() + db.SetExternalId(guest, task.GetUserCred(), instanceId) + initialState := guest.GetDriver().GetGuestInitialStateAfterCreate() + log.Debugf("VMrebuildRoot %s new instance, wait status %s ...", iVM.GetGlobalId(), initialState) + cloudprovider.WaitStatus(iVM, initialState, time.Second*5, time.Second*1800) + + iVM.StopVM(ctx, true) + + iDisks, err = iVM.GetIDisks() + if err != nil { + return "", errors.Wrapf(err, "iVM.GetIDisks.AfterCreated") + } + + for _, iDisk := range iDisks { + return iDisk.GetGlobalId(), nil + } + return "", fmt.Errorf("failed to found new instance system disk") + }() + if err != nil { + return nil, err + } + + initialState := guest.GetDriver().GetGuestInitialStateAfterRebuild() + log.Debugf("VMrebuildRoot %s new diskID %s, wait status %s ...", iVM.GetGlobalId(), diskId, initialState) + err = cloudprovider.WaitStatus(iVM, initialState, time.Second*5, time.Second*1800) + if err != nil { + return nil, err + } + log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId()) + + maxWaitSecs := 300 + waited := 0 + + for { + // hack, wait disk number consistent + idisks, err := iVM.GetIDisks() + if err != nil { + log.Errorf("fail to find VM idisks %s", err) + return nil, err + } + if len(idisks) < len(desc.DataDisks)+1 { + if waited > maxWaitSecs { + log.Errorf("inconsistent disk number, wait timeout, must be something wrong on remote") + return nil, cloudprovider.ErrTimeout + } + log.Debugf("inconsistent disk number???? %d != %d", len(idisks), len(desc.DataDisks)+1) + time.Sleep(time.Second * 5) + waited += 5 + } else { + if idisks[0].GetGlobalId() == diskId { + break + } + if waited > maxWaitSecs { + return nil, fmt.Errorf("inconsistent sys disk id after rebuild root") + } + log.Debugf("current system disk id inconsistent %s != %s, try after 5 seconds", idisks[0].GetGlobalId(), diskId) + time.Sleep(time.Second * 5) + waited += 5 + } + } + + data := fetchIVMinfo(desc, iVM, guest.Id, desc.Account, desc.Password, desc.PublicKey, "rebuild") + + return data, nil +} + func (self *SOpenStackGuestDriver) GetGuestInitialStateAfterRebuild() string { return api.VM_READY } diff --git a/pkg/multicloud/openstack/disk.go b/pkg/multicloud/openstack/disk.go index 197bd5646a..0029554721 100644 --- a/pkg/multicloud/openstack/disk.go +++ b/pkg/multicloud/openstack/disk.go @@ -85,6 +85,7 @@ type VolumeImageMetadata struct { } type SDisk struct { + nova *SNovaStorage storage *SStorage multicloud.SDisk @@ -168,6 +169,9 @@ func (disk *SDisk) GetId() string { } func (disk *SDisk) Delete(ctx context.Context) error { + if disk.nova != nil { + return nil + } err := disk.storage.zone.region.DeleteDisk(disk.ID) if err != nil { return err @@ -195,6 +199,9 @@ func (disk *SDisk) attachInstances(attachments []Attachment) error { } func (disk *SDisk) Resize(ctx context.Context, sizeMb int64) error { + if disk.nova != nil { + return cloudprovider.ErrNotSupported + } instanceIds := []string{} for _, attachement := range disk.Attachments { @@ -227,6 +234,9 @@ func (disk *SDisk) IsEmulated() bool { } func (disk *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + if disk.nova != nil { + return disk.nova, nil + } return disk.storage, nil } @@ -252,14 +262,20 @@ func (disk *SDisk) GetStatus() string { } func (disk *SDisk) Refresh() error { - new, err := disk.storage.zone.region.GetDisk(disk.ID) + if disk.nova != nil { + return nil + } + _disk, err := disk.storage.zone.region.GetDisk(disk.ID) if err != nil { return err } - return jsonutils.Update(disk, new) + return jsonutils.Update(disk, _disk) } func (disk *SDisk) ResizeDisk(sizeMb int64) error { + if disk.nova != nil { + return cloudprovider.ErrNotSupported + } return disk.storage.zone.region.ResizeDisk(disk.ID, sizeMb) } @@ -394,6 +410,9 @@ func (region *SRegion) ResetDisk(diskId, snapshotId string) error { } func (disk *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { + if disk.nova != nil { + return nil, cloudprovider.ErrNotSupported + } snapshot, err := disk.storage.zone.region.CreateSnapshot(disk.ID, name, desc) if err != nil { return nil, err @@ -402,14 +421,23 @@ func (disk *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (clou } func (disk *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) { + if disk.nova != nil { + return nil, cloudprovider.ErrNotFound + } return disk.storage.zone.region.GetISnapshotById(snapshotId) } func (disk *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + if disk.nova != nil { + return []cloudprovider.ICloudSnapshot{}, nil + } return disk.storage.zone.region.GetSnapshots(disk.ID) } func (disk *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) { + if disk.nova != nil { + return "", cloudprovider.ErrNotSupported + } return disk.ID, disk.storage.zone.region.ResetDisk(disk.ID, snapshotId) } diff --git a/pkg/multicloud/openstack/flavor.go b/pkg/multicloud/openstack/flavor.go index 7a2fc1fd0a..0a5dc8181a 100644 --- a/pkg/multicloud/openstack/flavor.go +++ b/pkg/multicloud/openstack/flavor.go @@ -82,7 +82,22 @@ func (region *SRegion) GetFlavor(flavorId string) (*SFlavor, error) { } func (region *SRegion) SyncFlavor(name string, cpu, memoryMb, diskGB int) (string, error) { - return region.syncFlavor(name, cpu, memoryMb, diskGB) + id, err := region.syncFlavor(name, cpu, memoryMb, diskGB) + if err != nil { + return "", errors.Wrap(err, "syncFlavor") + } + flavor, err := region.GetFlavor(id) + if err != nil { + return "", errors.Wrapf(err, "region.GetFlavor(%s)", id) + } + if flavor.GetCpuCoreCount() != cpu || flavor.GetMemorySizeMB() != memoryMb || flavor.Disk != diskGB { + flavor, err = region.CreateFlavor(name, cpu, memoryMb, diskGB) + if err != nil { + return "", errors.Wrap(err, "CreateFlavor") + } + return flavor.ID, nil + } + return id, nil } func (region *SRegion) syncFlavor(name string, cpu, memoryMb, diskGB int) (string, error) { @@ -97,7 +112,7 @@ func (region *SRegion) syncFlavor(name string, cpu, memoryMb, diskGB int) (strin return flavor.ID, nil } } - flavor, err := region.CreateFlavor(name, cpu, memoryMb, 40) + flavor, err := region.CreateFlavor(name, cpu, memoryMb, diskGB) if err != nil { return "", errors.Wrap(err, "region.CreateClavor()") } diff --git a/pkg/multicloud/openstack/host.go b/pkg/multicloud/openstack/host.go index 670a7a4771..b2d57f48a7 100644 --- a/pkg/multicloud/openstack/host.go +++ b/pkg/multicloud/openstack/host.go @@ -129,6 +129,10 @@ func (host *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) { result := []cloudprovider.ICloudStorage{} for _, istorage := range istorages { + if istorage.GetStorageType() == api.STORAGE_OPENSTACK_NOVA { + result = append(result, istorage) + continue + } if storage := istorage.(*SStorage); len(storage.ExtraSpecs.VolumeBackendName) > 0 { for _, pool := range schedulerPools { if strings.HasPrefix(pool.Name, fmt.Sprintf("%s@%s", host.GetName(), storage.ExtraSpecs.VolumeBackendName)) { @@ -197,34 +201,40 @@ func (host *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudpr return nil, err } - storage, err := host.zone.getStorageByCategory(desc.SysDisk.StorageType) - if err != nil { - return nil, err - } - sysDiskSizeGB := image.Size / 1024 / 1024 / 1024 if desc.SysDisk.SizeGB < sysDiskSizeGB { desc.SysDisk.SizeGB = sysDiskSizeGB } - _sysDisk, err := host.zone.region.CreateDisk(desc.ExternalImageId, storage.Name, "", desc.SysDisk.SizeGB, desc.SysDisk.Name) - if err != nil { - return nil, err + if desc.SysDisk.SizeGB < image.GetMinOsDiskSizeGb() { + desc.SysDisk.SizeGB = image.GetMinOsDiskSizeGb() } - BlockDeviceMappingV2 := []map[string]interface{}{ - { + BlockDeviceMappingV2 := []map[string]interface{}{} + + if desc.SysDisk.StorageType != api.STORAGE_OPENSTACK_NOVA { //新建volume + storage, err := host.zone.getStorageByCategory(desc.SysDisk.StorageType) + if err != nil { + return nil, err + } + + _sysDisk, err := host.zone.region.CreateDisk(desc.ExternalImageId, storage.Name, "", desc.SysDisk.SizeGB, desc.SysDisk.Name) + if err != nil { + return nil, err + } + + BlockDeviceMappingV2 = append(BlockDeviceMappingV2, map[string]interface{}{ "boot_index": 0, "uuid": _sysDisk.GetGlobalId(), "source_type": "volume", "destination_type": "volume", "delete_on_termination": true, - }, + }) } var _disk *SDisk for _, disk := range desc.DataDisks { - storage, err = host.zone.getStorageByCategory(disk.StorageType) + storage, err := host.zone.getStorageByCategory(disk.StorageType) if err != nil { break } diff --git a/pkg/multicloud/openstack/instance.go b/pkg/multicloud/openstack/instance.go index 2c2a90e747..b38e887407 100644 --- a/pkg/multicloud/openstack/instance.go +++ b/pkg/multicloud/openstack/instance.go @@ -263,12 +263,16 @@ func (instance *SInstance) GetInstanceType() string { func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { disks := []SDisk{} + hasSysDisk := false for i := 0; i < len(instance.VolumesAttached); i++ { disk, err := instance.host.zone.region.GetDisk(instance.VolumesAttached[i].ID) if err != nil { return nil, err } disks = append(disks, *disk) + if disk.Bootable { + hasSysDisk = true + } } iDisks := []cloudprovider.ICloudDisk{} for i := 0; i < len(disks); i++ { @@ -279,6 +283,13 @@ func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { disks[i].storage = store iDisks = append(iDisks, &disks[i]) } + + if !hasSysDisk { + nova := SNovaStorage{zone: instance.host.zone} + sysDisk, _ := nova.GetIDiskById(instance.ID) + iDisks = append([]cloudprovider.ICloudDisk{sysDisk}, iDisks...) + } + return iDisks, nil } @@ -432,11 +443,7 @@ func (instance *SInstance) DeployVM(ctx context.Context, name string, username s } func (instance *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { - sysDiskId := "" - if len(instance.VolumesAttached) > 0 { - sysDiskId = instance.VolumesAttached[0].ID - } - return sysDiskId, instance.host.zone.region.ReplaceSystemDisk(instance.ID, imageId, passwd, publicKey, sysSizeGB) + return instance.ID, instance.host.zone.region.ReplaceSystemDisk(instance.ID, imageId, passwd, publicKey, sysSizeGB) } func (instance *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { diff --git a/pkg/multicloud/openstack/novastorage.go b/pkg/multicloud/openstack/novastorage.go new file mode 100644 index 0000000000..645083fc12 --- /dev/null +++ b/pkg/multicloud/openstack/novastorage.go @@ -0,0 +1,120 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openstack + +import ( + "fmt" + + "yunion.io/x/jsonutils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SNovaStorage struct { + zone *SZone +} + +func (storage *SNovaStorage) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (storage *SNovaStorage) GetId() string { + return fmt.Sprintf("%s-%s", storage.zone.GetId(), storage.GetName()) +} + +func (storage *SNovaStorage) GetName() string { + return api.STORAGE_OPENSTACK_NOVA +} + +func (storage *SNovaStorage) GetGlobalId() string { + return storage.GetId() +} + +func (storage *SNovaStorage) IsEmulated() bool { + return true +} + +func (storage *SNovaStorage) GetIZone() cloudprovider.ICloudZone { + return storage.zone +} + +func (storage *SNovaStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + return []cloudprovider.ICloudDisk{}, nil +} + +func (storage *SNovaStorage) GetStorageType() string { + return api.STORAGE_OPENSTACK_NOVA +} + +func (storage *SNovaStorage) GetMediumType() string { + return api.DISK_TYPE_ROTATE +} + +func (storage *SNovaStorage) GetCapacityMB() int64 { + return 1000000000 +} + +func (storage *SNovaStorage) GetStorageConf() jsonutils.JSONObject { + conf := jsonutils.NewDict() + return conf +} + +func (storage *SNovaStorage) GetStatus() string { + return api.STORAGE_ONLINE +} + +func (storage *SNovaStorage) Refresh() error { + // do nothing + return nil +} + +func (storage *SNovaStorage) GetEnabled() bool { + return true +} + +func (storage *SNovaStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return storage.zone.region.getStoragecache() +} + +func (storage *SNovaStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (storage *SNovaStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { + instance, err := storage.zone.region.GetInstance(idStr) + if err != nil { + return nil, err + } + disk := SDisk{ + ID: instance.ID, + Name: fmt.Sprintf("root disk for %s", instance.Name), + Size: instance.Flavor.Disk, + Status: DISK_STATUS_IN_USE, + Bootable: true, + CreatedAt: instance.Created, + VolumeType: api.STORAGE_OPENSTACK_NOVA, + } + disk.nova = storage + return &disk, nil +} + +func (storage *SNovaStorage) GetMountPoint() string { + return "" +} + +func (storage *SNovaStorage) IsSysDiskStore() bool { + return true +} diff --git a/pkg/multicloud/openstack/region.go b/pkg/multicloud/openstack/region.go index 363c454c1f..6e2db336be 100644 --- a/pkg/multicloud/openstack/region.go +++ b/pkg/multicloud/openstack/region.go @@ -21,6 +21,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -137,7 +138,7 @@ func (region *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, istore, err := izones[i].GetIStorageById(id) if err == nil { return istore, nil - } else if err != cloudprovider.ErrNotFound { + } else if errors.Cause(err) != cloudprovider.ErrNotFound { return nil, err } } diff --git a/pkg/multicloud/openstack/shell/zone.go b/pkg/multicloud/openstack/shell/zone.go index 3c069045a4..1eb9261072 100644 --- a/pkg/multicloud/openstack/shell/zone.go +++ b/pkg/multicloud/openstack/shell/zone.go @@ -30,4 +30,16 @@ func init() { printList(zones, 0, 0, 0, nil) return nil }) + + type SchedulerPoolListOptions struct { + } + shellutils.R(&SchedulerPoolListOptions{}, "scheduler-pool-list", "List scheduler pool", func(cli *openstack.SRegion, args *SchedulerPoolListOptions) error { + pools, err := cli.GetSchedulerStatsPool() + if err != nil { + return err + } + printList(pools, 0, 0, 0, nil) + return nil + }) + } diff --git a/pkg/multicloud/openstack/zone.go b/pkg/multicloud/openstack/zone.go index cc510ae066..fe9380c2ce 100644 --- a/pkg/multicloud/openstack/zone.go +++ b/pkg/multicloud/openstack/zone.go @@ -21,6 +21,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -101,18 +102,26 @@ func (zone *SZone) GetIWires() ([]cloudprovider.ICloudWire, error) { } func (zone *SZone) fetchSchedulerStatsPool() error { - zone.schedulerPools = []SPool{} - for _, service := range []string{"volumev3", "volumev2", "volume"} { - _, resp, err := zone.region.List(service, "/scheduler-stats/get_pools", "", nil) - if err == nil { - if err := resp.Unmarshal(&zone.schedulerPools, "pools"); err != nil { - return err - } - return nil - } - log.Warningf("failed to get scheduler-stats pool by service %s error: %v, try another", service, err) + var err error = nil + zone.schedulerPools, err = zone.region.GetSchedulerStatsPool() + if err != nil { + return errors.Wrap(err, "GetSchedulerStatsPool") } - return fmt.Errorf("failed to find scheduler-stats pool by cinder service") + return nil +} + +func (region *SRegion) GetSchedulerStatsPool() ([]SPool, error) { + pools := []SPool{} + for _, service := range []string{"volumev3", "volumev2", "volume"} { + _, resp, err := region.List(service, "/scheduler-stats/get_pools", "", nil) + if err == nil { + if err := resp.Unmarshal(&pools, "pools"); err != nil { + return nil, errors.Wrap(err, "resp.Unmarshal(&pools)") + } + return pools, nil + } + } + return nil, fmt.Errorf("failed to find scheduler-stats pool by cinder service") } func (zone *SZone) getSchedulerStatsPool() ([]SPool, error) { @@ -157,10 +166,12 @@ func (zone *SZone) fetchStorages() error { storages[i].zone = zone zone.istorages = append(zone.istorages, &storages[i]) } - return nil + break } log.Debugf("failed to get volume types by service %s error: %v, try another", service, err) } + nova := &SNovaStorage{zone: zone} + zone.istorages = append(zone.istorages, nova) return fmt.Errorf("failed to find storage types by cinder service") }