diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index c1346a9659..87b6e572d8 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -132,6 +132,14 @@ func (self *SBaseGuestDriver) GetChangeConfigStatus() ([]string, error) { return []string{}, fmt.Errorf("This Guest driver dose not implement GetChangeConfigStatus") } +func (self *SBaseGuestDriver) ValidateChangeConfig(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, cpuChanged bool, memChanged bool, newDisks []*api.DiskConfig) error { + return nil +} + +func (self *SBaseGuestDriver) ValidateDetachDisk(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, disk *models.SDisk) error { + return nil +} + func (self *SBaseGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { return httperrors.NewInputParameterError("Not Implement ValidateCreateEip") } diff --git a/pkg/compute/guestdrivers/qcloud.go b/pkg/compute/guestdrivers/qcloud.go index 19d7bfb1af..4259d90c86 100644 --- a/pkg/compute/guestdrivers/qcloud.go +++ b/pkg/compute/guestdrivers/qcloud.go @@ -105,6 +105,9 @@ func (self *SQcloudGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *m if utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_LOCAL_BASIC, api.STORAGE_LOCAL_SSD}) { return fmt.Errorf("Cannot resize %s disk", storage.StorageType) } + if disk.DiskSize/1024%10 > 0 { + return fmt.Errorf("Resize disk size must be an integer multiple of 10G") + } return nil } @@ -145,10 +148,67 @@ func (self *SQcloudGuestDriver) ValidateCreateData(ctx context.Context, userCred return nil, httperrors.NewInputParameterError("The %s disk size must be in the range of 100GB ~ 16000GB", disk.Backend) } } + if disk.SizeMb/1024%10 > 0 { + return nil, httperrors.NewInputParameterError("Data disk size must be an integer multiple of 10G") + } } return input, nil } +func (self *SQcloudGuestDriver) ValidateChangeConfig(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, cpuChanged bool, memChanged bool, newDisks []*api.DiskConfig) error { + if cpuChanged || memChanged { + disk, err := guest.GetSystemDisk() + if err != nil { + return httperrors.NewResourceNotFoundError("failed to found system disk error: %v", err) + } + storage := disk.GetStorage() + if storage == nil { + return httperrors.NewResourceNotFoundError("failed to found storage for disk %s(%s)", disk.Name, disk.Id) + } + // 腾讯云系统盘为本地存储,不支持调整配置 + if utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_LOCAL_BASIC, api.STORAGE_LOCAL_SSD}) { + return httperrors.NewUnsupportOperationError("The system disk is locally stored and does not support changing configuration") + } + } + + for _, newDisk := range newDisks { + switch newDisk.Backend { + case api.STORAGE_CLOUD_BASIC: + if newDisk.SizeMb < 10*1024 || newDisk.SizeMb > 16000*1024 { + return httperrors.NewInputParameterError("The %s disk size must be in the range of 10GB ~ 16000GB", newDisk.Backend) + } + case api.STORAGE_CLOUD_PREMIUM: + if newDisk.SizeMb < 50*1024 || newDisk.SizeMb > 16000*1024 { + return httperrors.NewInputParameterError("The %s disk size must be in the range of 50GB ~ 16000GB", newDisk.Backend) + } + case api.STORAGE_CLOUD_SSD: + if newDisk.SizeMb < 100*1024 || newDisk.SizeMb > 16000*1024 { + return httperrors.NewInputParameterError("The %s disk size must be in the range of 100GB ~ 16000GB", newDisk.Backend) + } + case api.STORAGE_LOCAL_BASIC, api.STORAGE_LOCAL_SSD: + return httperrors.NewUnsupportOperationError("Not support create local storage disks") + case "": //这里Backend为空有可能会导致创建出来还是local storage,依然会出错,需要用户显式指定 + return httperrors.NewInputParameterError("Please input new disk backend type") + } + if newDisk.SizeMb/1024%10 > 0 { + return httperrors.NewInputParameterError("Data disk size must be an integer multiple of 10G") + } + } + return nil +} + +func (self *SQcloudGuestDriver) ValidateDetachDisk(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, disk *models.SDisk) error { + storage := disk.GetStorage() + if storage == nil { + return httperrors.NewResourceNotFoundError("failed to found storage for disk %s(%s)", disk.Name, disk.Id) + } + // 腾讯云本地盘不支持卸载 + if utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_LOCAL_BASIC, api.STORAGE_LOCAL_SSD}) { + return httperrors.NewUnsupportOperationError("The disk is locally stored and does not support detach") + } + return nil +} + func (self *SQcloudGuestDriver) GetGuestInitialStateAfterCreate() string { return api.VM_RUNNING } diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 3acf524eb2..bb1ab5c089 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -1443,6 +1443,12 @@ func (self *SGuest) PerformDetachdisk(ctx context.Context, userCred mcclient.Tok if keepDisk && !self.GetDriver().CanKeepDetachDisk() { return nil, httperrors.NewInputParameterError("Cannot keep detached disk") } + + err = self.GetDriver().ValidateDetachDisk(ctx, userCred, self, disk) + if err != nil { + return nil, err + } + if utils.IsInStringArray(self.Status, detachDiskStatus) { self.SetStatus(userCred, api.VM_DETACH_DISK, "") err = self.StartGuestDetachdiskTask(ctx, userCred, disk, keepDisk, "", false) @@ -1984,6 +1990,7 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T } if diskConf.SizeMb > 0 { if diskIdx >= len(disks) { + // 这里backeend为空时,qcloud有可能会选择local_ssd作为后端存储,会导致报错(主要是climc) storage := host.GetLeastUsedStorage(diskConf.Backend) if storage == nil { return nil, httperrors.NewResourceNotReadyError("host not connect storage %s", diskConf.Backend) @@ -2011,6 +2018,10 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T if !ok { diskSizes[storage.Id] = 0 } + err = self.ValidateResizeDisk(disk, storage) + if err != nil { + return nil, httperrors.NewUnsupportOperationError(err.Error()) + } diskSizes[storage.Id] = diskSizes[storage.Id] + diskConf.SizeMb - oldSize } } @@ -2044,6 +2055,11 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T confs.Set("guest_online", jsonutils.JSONTrue) } + err = self.GetDriver().ValidateChangeConfig(ctx, userCred, self, cpuChanged, memChanged, newDisks) + if err != nil { + return nil, err + } + // schedulr forecast schedDesc := self.confToSchedDesc(addCpu, addMem, addDisk) s := auth.GetAdminSession(ctx, options.Options.Region, "") diff --git a/pkg/compute/models/guestdrivers.go b/pkg/compute/models/guestdrivers.go index 77e9f847fb..330166e904 100644 --- a/pkg/compute/models/guestdrivers.go +++ b/pkg/compute/models/guestdrivers.go @@ -168,6 +168,9 @@ type IGuestDriver interface { OnGuestChangeCpuMemFailed(ctx context.Context, guest *SGuest, data *jsonutils.JSONDict, task taskman.ITask) error IsSupportGuestClone() bool + ValidateChangeConfig(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, cpuChanged bool, memChanged bool, newDisks []*api.DiskConfig) error + ValidateDetachDisk(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, disk *SDisk) error + IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool GetUserDataType() string } diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 479dae275a..8a643ec4ab 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -521,6 +521,31 @@ func (guest *SGuest) DiskCount() (int, error) { return guest.GetDisksQuery().CountWithError() } +func (guest *SGuest) GetSystemDisk() (*SDisk, error) { + q := DiskManager.Query().Equals("disk_type", api.DISK_TYPE_SYS) + gs := GuestdiskManager.Query().SubQuery() + q = q.Join(gs, sqlchemy.Equals(gs.Field("disk_id"), q.Field("id"))). + Filter(sqlchemy.Equals(gs.Field("guest_id"), guest.Id)) + + count, err := q.CountWithError() + if err != nil { + return nil, err + } + if count > 1 { + return nil, sqlchemy.ErrDuplicateEntry + } + if count == 0 { + return nil, sql.ErrNoRows + } + disk := &SDisk{} + err = q.First(disk) + if err != nil { + return nil, errors.Wrap(err, "q.First(disk)") + } + disk.SetModelManager(DiskManager, disk) + return disk, nil +} + func (guest *SGuest) GetDisks() []SGuestdisk { disks := make([]SGuestdisk, 0) q := guest.GetDisksQuery().Asc("index")