From c8c5a28675ff455d31534680a16eeacfc9955002 Mon Sep 17 00:00:00 2001 From: wanyaoqi <18528551+wanyaoqi@users.noreply.github.com> Date: Tue, 19 Jul 2022 20:02:10 +0800 Subject: [PATCH] feat(region,host): create disk from existing path (#14677) Server create disk from existing path. Usage: climc server-create --disk storage=:existing_path= ... climc disk-create storage=:existing_path= DISKNAME Signed-off-by: wanyaoqi --- pkg/apis/compute/api.go | 3 +++ pkg/apis/compute/disk.go | 1 + pkg/apis/compute/disk_const.go | 2 ++ pkg/cloudcommon/cmdline/parser.go | 2 ++ pkg/compute/models/disks.go | 28 +++++++++++++++++++++++++ pkg/compute/models/guests.go | 4 ++++ pkg/hostman/storageman/storage_base.go | 20 +++++++++++++++++- pkg/hostman/storageman/storage_local.go | 10 +++++++++ pkg/hostman/storageman/storage_rbd.go | 4 ++++ 9 files changed, 73 insertions(+), 1 deletion(-) diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index cfce12f734..3a2b3aa70d 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -212,6 +212,9 @@ type DiskConfig struct { //swagger:ignore DiskId string `json:"disk_id"` + + //swagger:ignore + ExistingPath string `json:"existing_path"` } type IsolatedDeviceConfig struct { diff --git a/pkg/apis/compute/disk.go b/pkg/apis/compute/disk.go index 93d5cc501e..445a0fd5c6 100644 --- a/pkg/apis/compute/disk.go +++ b/pkg/apis/compute/disk.go @@ -296,6 +296,7 @@ type DiskAllocateInput struct { Protocol string SrcDiskId string SrcPool string + ExistingPath string // vmware HostIp string diff --git a/pkg/apis/compute/disk_const.go b/pkg/apis/compute/disk_const.go index b4efbc63fe..506dd25217 100644 --- a/pkg/apis/compute/disk_const.go +++ b/pkg/apis/compute/disk_const.go @@ -63,3 +63,5 @@ const ( DISK_NOT_EXIST = "not_exist" DISK_EXIST = "exist" ) + +const DISK_META_EXISTING_PATH = "disk_existing_path" diff --git a/pkg/cloudcommon/cmdline/parser.go b/pkg/cloudcommon/cmdline/parser.go index 5b0927d328..0c105b7cb2 100644 --- a/pkg/cloudcommon/cmdline/parser.go +++ b/pkg/cloudcommon/cmdline/parser.go @@ -185,6 +185,8 @@ func ParseDiskConfig(diskStr string, idx int) (*compute.DiskConfig, error) { diskConfig.Storage = str case "image", "image_id": diskConfig.ImageId = str + case "existing_path": + diskConfig.ExistingPath = str default: return nil, errors.Errorf("invalid disk description %s", p) } diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index fdb82ae08b..ad17bea169 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -461,12 +461,17 @@ func (manager *SDiskManager) ValidateCreateData(ctx context.Context, userCred mc if err != nil { return input, err } + if input.ExistingPath != "" && input.Storage == "" { + return input, httperrors.NewInputParameterError("disk create from existing disk must give storage") + } + input.ProjectId = ownerId.GetProjectId() input.ProjectDomainId = ownerId.GetProjectDomainId() var quotaKey quotas.IQuotaKeys storageID := input.Storage + if storageID != "" { storageObj, err := StorageManager.FetchByIdOrName(nil, storageID) if err != nil { @@ -558,6 +563,13 @@ func (manager *SDiskManager) validateDiskOnStorage(diskConfig *api.DiskConfig, s if storage.StorageType != diskConfig.Backend { return httperrors.NewInputParameterError("Storage type[%s] not match backend %s", storage.StorageType, diskConfig.Backend) } + if diskConfig.ExistingPath != "" { + if !utils.IsInStringArray(storage.StorageType, api.FIEL_STORAGE) { + return httperrors.NewInputParameterError( + "Disk create from existing path, unsupport storage type %s", storage.StorageType) + } + } + if host, _ := storage.GetMasterHost(); host != nil { //公有云磁盘大小检查。 if err := host.GetHostDriver().ValidateDiskSize(storage, diskConfig.SizeMb>>10); err != nil { @@ -663,12 +675,24 @@ func getDiskResourceRequirements(ctx context.Context, userCred mcclient.TokenCre return newData }*/ +func (disk *SDisk) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { + input := api.DiskCreateInput{} + err := data.Unmarshal(&input) + if err != nil { + log.Errorf("!!!data.Unmarshal api.DiskCreateInput fail %s", err) + } + if input.ExistingPath != "" { + disk.SetMetadata(ctx, api.DISK_META_EXISTING_PATH, input.ExistingPath, userCred) + } +} + func (manager *SDiskManager) OnCreateComplete(ctx context.Context, items []db.IModel, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { input := api.DiskCreateInput{} err := data.Unmarshal(&input) if err != nil { log.Errorf("!!!data.Unmarshal api.DiskCreateInput fail %s", err) } + pendingUsage := getDiskResourceRequirements(ctx, userCred, ownerId, input, len(items)) parentTaskId, _ := data.GetString("parent_task_id") RunBatchCreateTask(ctx, items, userCred, data, pendingUsage, SRegionQuota{}, "DiskBatchCreateTask", parentTaskId) @@ -761,6 +785,10 @@ func (self *SDisk) StartAllocate(ctx context.Context, host *SHost, storage *SSto return errors.Wrap(err, "GetEncryptInfo") } } + if ePath := self.GetMetadata(ctx, api.DISK_META_EXISTING_PATH, userCred); ePath != "" { + input.ExistingPath = ePath + } + if rebuild { return host.GetHostDriver().RequestRebuildDiskOnStorage(ctx, host, storage, self, task, input) } else { diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index f8c522e571..1fc81e6a8d 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -3902,6 +3902,10 @@ func (self *SGuest) createDiskOnHost( if err != nil { return nil, err } + if diskConfig.ExistingPath != "" { + disk.SetMetadata(ctx, api.DISK_META_EXISTING_PATH, diskConfig.ExistingPath, userCred) + } + if len(self.BackupHostId) > 0 { backupHost := HostManager.FetchHostById(self.BackupHostId) backupStorage, err := self.ChooseHostStorage(backupHost, diskConfig, backupCandidate) diff --git a/pkg/hostman/storageman/storage_base.go b/pkg/hostman/storageman/storage_base.go index 9ffb1ff7bb..45cd33badc 100644 --- a/pkg/hostman/storageman/storage_base.go +++ b/pkg/hostman/storageman/storage_base.go @@ -123,7 +123,7 @@ type IStorage interface { CreateDiskByDiskinfo(context.Context, interface{}) (jsonutils.JSONObject, error) SaveToGlance(context.Context, interface{}) (jsonutils.JSONObject, error) CreateDiskFromSnapshot(context.Context, IDisk, *SDiskCreateByDiskinfo) error - + CreateDiskFromExistingPath(context.Context, IDisk, *SDiskCreateByDiskinfo) error CreateDiskFromBackup(context.Context, IDisk, *SDiskCreateByDiskinfo) error // GetCloneTargetDiskPath generate target disk path by target disk id @@ -354,6 +354,8 @@ func (s *SBaseStorage) CreateDiskByDiskinfo(ctx context.Context, params interfac case createParams.DiskInfo.Backup != nil: log.Infof("CreateDiskFromBackup %s", createParams) return s.createDiskFromBackup(ctx, disk, createParams) + case len(createParams.DiskInfo.ExistingPath) > 0: + return s.createDiskFromExistingPath(ctx, disk, createParams) case createParams.DiskInfo.DiskSizeMb > 0: log.Infof("CreateRawDisk %s", createParams) return s.CreateRawDisk(ctx, disk, createParams) @@ -397,6 +399,22 @@ func (s *SBaseStorage) CreateDiskFromSnpashot(ctx context.Context, disk IDisk, i return disk.GetDiskDesc(), nil } +func (s *SBaseStorage) createDiskFromExistingPath(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) { + var storage = input.Storage + if len(input.DiskInfo.ExistingPath) == 0 { + return nil, httperrors.NewMissingParameterError("existing_path") + } + if !strings.HasPrefix(input.DiskInfo.ExistingPath, storage.GetPath()) { + return nil, errors.Errorf("disk %s not in storage %s", input.DiskInfo.ExistingPath, storage.GetPath()) + } + err := storage.CreateDiskFromExistingPath(ctx, disk, input) + if err != nil { + return nil, errors.Wrapf(err, "CreateDiskFromExistingPath") + } + + return disk.GetDiskDesc(), nil +} + func (s *SBaseStorage) createDiskFromBackup(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) { var storage = input.Storage if input.DiskInfo.Backup == nil { diff --git a/pkg/hostman/storageman/storage_local.go b/pkg/hostman/storageman/storage_local.go index ef4576a7ea..370742ac9b 100644 --- a/pkg/hostman/storageman/storage_local.go +++ b/pkg/hostman/storageman/storage_local.go @@ -664,6 +664,16 @@ func (s *SLocalStorage) CreateDiskFromSnapshot( return httperrors.NewUnsupportOperationError("Unsupport protocol %s for Local storage", info.Protocol) } +func (s *SLocalStorage) CreateDiskFromExistingPath( + ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo, +) error { + err := os.Link(input.DiskInfo.ExistingPath, disk.GetPath()) + if err != nil { + return errors.Wrap(err, "os.link") + } + return nil +} + func (s *SLocalStorage) GetCloneTargetDiskPath(ctx context.Context, targetDiskId string) string { return path.Join(s.GetPath(), targetDiskId) } diff --git a/pkg/hostman/storageman/storage_rbd.go b/pkg/hostman/storageman/storage_rbd.go index d511be1b3f..15b3432c2d 100644 --- a/pkg/hostman/storageman/storage_rbd.go +++ b/pkg/hostman/storageman/storage_rbd.go @@ -577,6 +577,10 @@ func (s *SRbdStorage) GetBackupDir() string { return "" } +func (s *SRbdStorage) CreateDiskFromExistingPath(context.Context, IDisk, *SDiskCreateByDiskinfo) error { + return fmt.Errorf("Not support") +} + func (s *SRbdStorage) CreateDiskFromBackup(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) error { backup := input.DiskInfo.Backup pool, _ := s.StorageConf.GetString("pool")