Merge pull request #16069 from wanyaoqi/fix/guest-change-storage

fix(region): guest change storage
This commit is contained in:
Zexi Li
2023-02-28 23:43:18 +08:00
committed by GitHub
9 changed files with 200 additions and 16 deletions
+1
View File
@@ -100,6 +100,7 @@ func init() {
cmd.Perform("set-sshport", &options.ServerSetSshportOptions{})
cmd.Perform("have-agent", &options.ServerHaveAgentOptions{})
cmd.Perform("change-disk-storage", &options.ServerChangeDiskStorageOptions{})
cmd.Perform("change-storage", &options.ServerChangeStorageOptions{})
cmd.PerformClass("batch-user-metadata", &options.ServerBatchMetadataOptions{})
cmd.PerformClass("batch-set-user-metadata", &options.ServerBatchMetadataOptions{})
cmd.Perform("user-metadata", &baseoptions.ResourceMetadataOptions{})
+12 -1
View File
@@ -847,6 +847,17 @@ type ServerSetBootIndexInput struct {
Cdroms map[string]int8 `json:"cdroms"`
}
type ServerChangeStorageInput struct {
TargetStorageId string `json:"target_storage_id"`
KeepOriginDisk bool `json:"keep_origin_disk"`
}
type ServerChangeStorageInternalInput struct {
ServerChangeStorageInput
Disks []string `json:"disks"`
GuestRunning bool `json:"guest_running"`
}
type ServerChangeDiskStorageInput struct {
DiskId string `json:"disk_id"`
TargetStorageId string `json:"target_storage_id"`
@@ -858,7 +869,7 @@ type ServerChangeDiskStorageInternalInput struct {
StorageId string `json:"storage_id"`
TargetDiskId string `json:"target_disk_id"`
DiskFormat string `json:"disk_format"`
GuestRunning bool `josn:"guest_running"`
GuestRunning bool `json:"guest_running"`
TargetDiskDesc *GuestdiskJsonDesc `json:"target_disk_desc"`
}
+1 -1
View File
@@ -454,7 +454,7 @@ func (self *SBaseGuestDriver) ValidateDetachNetwork(ctx context.Context, userCre
return nil
}
func (self *SBaseGuestDriver) ValidateChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, input *api.ServerChangeDiskStorageInput) error {
func (self *SBaseGuestDriver) ValidateChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, targetStorageId string) error {
return cloudprovider.ErrNotImplemented
}
+3 -3
View File
@@ -847,7 +847,7 @@ func (self *SKVMGuestDriver) ValidateDetachNetwork(ctx context.Context, userCred
return nil
}
func (self *SKVMGuestDriver) ValidateChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, input *api.ServerChangeDiskStorageInput) error {
func (self *SKVMGuestDriver) ValidateChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, targetStorageId string) error {
if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_BLOCK_STREAM, api.VM_DISK_CHANGE_STORAGE}) {
return httperrors.NewBadRequestError("Cannot change disk storage in status %s", guest.Status)
}
@@ -865,12 +865,12 @@ func (self *SKVMGuestDriver) ValidateChangeDiskStorage(ctx context.Context, user
attachedStorages := host.GetAttachedEnabledHostStorages(nil)
foundStorage := false
for _, storage := range attachedStorages {
if storage.GetId() == input.TargetStorageId {
if storage.GetId() == targetStorageId {
foundStorage = true
}
}
if !foundStorage {
return httperrors.NewBadRequestError("Storage %s not attached or enabled on host %s", input.TargetStorageId, host.GetName())
return httperrors.NewBadRequestError("Storage %s not attached or enabled on host %s", targetStorageId, host.GetName())
}
return nil
}
+60 -8
View File
@@ -3476,11 +3476,11 @@ func (self *SGuest) PerformBlockMirrorReady(ctx context.Context, userCred mcclie
if disk == nil {
return nil, httperrors.NewNotFoundError("disk %s not found", diskId)
}
taskId := disk.GetMetadata(ctx, api.DISK_CLONE_TASK_ID, userCred)
log.Infof("task_id %s", taskId)
if err := self.startSwitchToClonedDisk(ctx, userCred, taskId); err != nil {
return nil, errors.Wrap(err, "startSwitchToClonedDisk")
if taskId := disk.GetMetadata(ctx, api.DISK_CLONE_TASK_ID, userCred); len(taskId) > 0 {
log.Infof("task_id %s", taskId)
if err := self.startSwitchToClonedDisk(ctx, userCred, taskId); err != nil {
return nil, errors.Wrap(err, "startSwitchToClonedDisk")
}
}
}
return nil, nil
@@ -5456,6 +5456,58 @@ func (self *SGuest) PerformListForward(ctx context.Context, userCred mcclient.To
return resp.JSON(), nil
}
func (self *SGuest) PerformChangeStorage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerChangeStorageInput) (*api.ServerChangeStorageInput, error) {
// validate input
if input.TargetStorageId == "" {
return nil, httperrors.NewNotEmptyError("Storage id is empty")
}
// validate storage
storageObj, err := StorageManager.FetchByIdOrName(userCred, input.TargetStorageId)
if err != nil {
return nil, errors.Wrapf(err, "Found storage by %s", input.TargetStorageId)
}
storage := storageObj.(*SStorage)
input.TargetStorageId = storage.GetId()
// validate disk
disks, err := self.GetDisks()
if err != nil {
return nil, errors.Wrapf(err, "Get server %s disks", self.GetName())
}
var changeDisks = []string{}
for _, disk := range disks {
if disk.StorageId != input.TargetStorageId {
changeDisks = append(changeDisks, disk.Id)
}
}
// driver validate
drv := self.GetDriver()
if err := drv.ValidateChangeDiskStorage(ctx, userCred, self, input.TargetStorageId); err != nil {
return nil, err
}
return nil, self.StartGuestChangeStorageTask(ctx, userCred, input, changeDisks)
}
func (self *SGuest) StartGuestChangeStorageTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerChangeStorageInput, disks []string) error {
params := api.ServerChangeStorageInternalInput{
ServerChangeStorageInput: *input,
Disks: disks,
GuestRunning: self.Status == api.VM_RUNNING,
}
reason := fmt.Sprintf("Change guest disks storage to %s", input.TargetStorageId)
self.SetStatus(userCred, api.VM_DISK_CHANGE_STORAGE, reason)
if task, err := taskman.TaskManager.NewTask(
ctx, "GuestChangeDisksStorageTask", self, userCred, jsonutils.Marshal(params).(*jsonutils.JSONDict),
"", "", nil); err != nil {
return err
} else {
task.ScheduleRun(nil)
}
return nil
}
func (self *SGuest) PerformChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerChangeDiskStorageInput) (*api.ServerChangeDiskStorageInput, error) {
// validate input
if input.DiskId == "" {
@@ -5492,7 +5544,7 @@ func (self *SGuest) PerformChangeDiskStorage(ctx context.Context, userCred mccli
// driver validate
drv := self.GetDriver()
if err := drv.ValidateChangeDiskStorage(ctx, userCred, self, input); err != nil {
if err := drv.ValidateChangeDiskStorage(ctx, userCred, self, input.TargetStorageId); err != nil {
return nil, err
}
@@ -5505,7 +5557,7 @@ func (self *SGuest) PerformChangeDiskStorage(ctx context.Context, userCred mccli
DiskType: srcDisk.DiskType,
}
targetDisk, err := self.createDiskOnStorage(ctx, userCred, storage, diskConf, nil, true, true)
targetDisk, err := self.CreateDiskOnStorage(ctx, userCred, storage, diskConf, nil, true, true)
if err != nil {
return nil, errors.Wrapf(err, "Create target disk on storage %s", storage.GetName())
}
@@ -5523,7 +5575,7 @@ func (self *SGuest) PerformChangeDiskStorage(ctx context.Context, userCred mccli
func (self *SGuest) StartChangeDiskStorageTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerChangeDiskStorageInternalInput, parentTaskId string) error {
reason := fmt.Sprintf("Change disk %s to storage %s", input.DiskId, input.TargetStorageId)
self.SetStatus(userCred, api.VM_DISK_CHANGE_STORAGE, reason)
return self.GetDriver().StartChangeDiskStorageTask(self, ctx, userCred, input, "")
return self.GetDriver().StartChangeDiskStorageTask(self, ctx, userCred, input, parentTaskId)
}
func (self *SGuest) startSwitchToClonedDisk(ctx context.Context, userCred mcclient.TokenCredential, taskId string) error {
+1 -1
View File
@@ -218,7 +218,7 @@ type IGuestDriver interface {
RequestListForward(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, req *guestdriver_types.ListForwardRequest) (*guestdriver_types.ListForwardResponse, error)
RequestCloseForward(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, req *guestdriver_types.CloseForwardRequest) (*guestdriver_types.CloseForwardResponse, error)
ValidateChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, input *api.ServerChangeDiskStorageInput) error
ValidateChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, targetStorageId string) error
StartChangeDiskStorageTask(guest *SGuest, ctx context.Context, userCred mcclient.TokenCredential, params *api.ServerChangeDiskStorageInternalInput, parentTaskId string) error
RequestChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, input *api.ServerChangeDiskStorageInternalInput, task taskman.ITask) error
RequestSwitchToTargetStorageDisk(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, input *api.ServerChangeDiskStorageInternalInput, task taskman.ITask) error
+2 -2
View File
@@ -4041,7 +4041,7 @@ func (self *SGuest) CreateDisksOnHost(
return nil
}
func (self *SGuest) createDiskOnStorage(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage,
func (self *SGuest) CreateDiskOnStorage(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage,
diskConfig *api.DiskConfig, pendingUsage quotas.IQuota, inheritBilling bool, isWithServerCreate bool) (*SDisk, error) {
lockman.LockObject(ctx, storage)
defer lockman.ReleaseObject(ctx, storage)
@@ -4128,7 +4128,7 @@ func (self *SGuest) createDiskOnHost(
return nil, fmt.Errorf("No storage on %s to create disk for %s", host.GetName(), diskConfig.Backend)
}
log.Debugf("Choose storage %s:%s for disk %#v", storage.Name, storage.Id, diskConfig)
disk, err := self.createDiskOnStorage(ctx, userCred, storage, diskConfig, pendingUsage, inheritBilling, isWithServerCreate)
disk, err := self.CreateDiskOnStorage(ctx, userCred, storage, diskConfig, pendingUsage, inheritBilling, isWithServerCreate)
if err != nil {
return nil, err
}
@@ -32,6 +32,7 @@ import (
func init() {
taskman.RegisterTask(GuestChangeDiskStorageTask{})
taskman.RegisterTask(GuestChangeDisksStorageTask{})
}
type GuestChangeDiskStorageTask struct {
@@ -100,6 +101,7 @@ func (t *GuestChangeDiskStorageTask) ChangeDiskStorage(ctx context.Context, gues
return
}
log.Infof("ChangeDiskStorage guest running is %v", input.GuestRunning)
if input.GuestRunning {
t.SetStage("OnDiskLiveChangeStorageReady", nil)
} else {
@@ -292,6 +294,10 @@ func (t *GuestChangeDiskStorageTask) attachTargetDisk(ctx context.Context, guest
}
func (t *GuestChangeDiskStorageTask) OnTargetDiskAttachComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
if t.HasParentTask() {
t.TaskComplete(ctx, guest, data)
return
}
t.SetStage("OnGuestSyncStatus", nil)
if err := guest.StartSyncstatus(ctx, t.UserCred, t.Id); err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(err.Error()))
@@ -320,3 +326,107 @@ func (t *GuestChangeDiskStorageTask) TaskFailed(ctx context.Context, guest *mode
logclient.AddActionLogWithStartable(t, guest, logclient.ACT_DISK_CHANGE_STORAGE, reason, t.GetUserCred(), false)
t.SetStageFailed(ctx, reason)
}
// --------------------- GuestChangeDisksStorageTask ----------------------------
type GuestChangeDisksStorageTask struct {
SGuestBaseTask
}
func (t *GuestChangeDisksStorageTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
guest := obj.(*models.SGuest)
t.ChangeDiskStorage(ctx, guest, nil)
}
func (t *GuestChangeDisksStorageTask) GetInputParams() (*api.ServerChangeStorageInternalInput, error) {
input := new(api.ServerChangeStorageInternalInput)
err := t.GetParams().Unmarshal(input)
return input, err
}
func (t *GuestChangeDisksStorageTask) TaskFailed(ctx context.Context, guest *models.SGuest, reason jsonutils.JSONObject) {
guest.SetStatus(t.GetUserCred(), api.VM_DISK_CHANGE_STORAGE_FAIL, reason.String())
logclient.AddActionLogWithStartable(t, guest, logclient.ACT_DISK_CHANGE_STORAGE, reason, t.GetUserCred(), false)
t.SetStageFailed(ctx, reason)
}
func (t *GuestChangeDisksStorageTask) TaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
logclient.AddActionLogWithStartable(t, guest, logclient.ACT_DISK_CHANGE_STORAGE, nil, t.GetUserCred(), true)
t.SetStageComplete(ctx, nil)
}
func (t *GuestChangeDisksStorageTask) OnDiskChangeStorageComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
t.SetStage("OnGuestSyncStatus", nil)
if err := guest.StartSyncstatus(ctx, t.UserCred, t.Id); err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(err.Error()))
}
}
func (t *GuestChangeDisksStorageTask) OnGuestSyncStatus(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
t.TaskComplete(ctx, guest, nil)
}
func (t *GuestChangeDisksStorageTask) OnGuestSyncStatusFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
t.TaskFailed(ctx, guest, data)
}
func (t *GuestChangeDisksStorageTask) ChangeDiskStorage(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
input, err := t.GetInputParams()
if err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(err.Error()))
return
}
if len(input.Disks) == 0 {
t.OnDiskChangeStorageComplete(ctx, guest, nil)
return
}
t.CreateTargetDisk(ctx, guest, input)
}
func (t *GuestChangeDisksStorageTask) ChangeDiskStorageFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
t.TaskFailed(ctx, guest, data)
}
func (t *GuestChangeDisksStorageTask) CreateTargetDisk(ctx context.Context, guest *models.SGuest, input *api.ServerChangeStorageInternalInput) {
storage := models.StorageManager.FetchStorageById(input.TargetStorageId)
srcDisk := models.DiskManager.FetchDiskById(input.Disks[0])
input.Disks = input.Disks[1:]
t.Params.Set("disks", jsonutils.Marshal(input.Disks))
if err := t.SetStage("ChangeDiskStorage", nil); err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(err.Error()))
return
}
// create a disk on target storage from source disk
diskConf := &api.DiskConfig{
Index: -1,
ImageId: srcDisk.TemplateId,
SizeMb: srcDisk.DiskSize,
Fs: srcDisk.FsFormat,
DiskType: srcDisk.DiskType,
}
targetDisk, err := guest.CreateDiskOnStorage(ctx, t.UserCred, storage, diskConf, nil, true, true)
if err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("Create target disk on storage %s: %s", storage.GetName(), err)))
return
}
internalInput := &api.ServerChangeDiskStorageInternalInput{
ServerChangeDiskStorageInput: api.ServerChangeDiskStorageInput{
DiskId: srcDisk.Id,
TargetStorageId: storage.Id,
KeepOriginDisk: input.KeepOriginDisk,
},
StorageId: srcDisk.StorageId,
TargetDiskId: targetDisk.GetId(),
GuestRunning: input.GuestRunning,
}
if err := guest.StartChangeDiskStorageTask(ctx, t.UserCred, internalInput, t.Id); err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(err.Error()))
return
}
}
+10
View File
@@ -1311,6 +1311,16 @@ func (o *ServerChangeDiskStorageOptions) Params() (jsonutils.JSONObject, error)
return jsonutils.Marshal(o), nil
}
type ServerChangeStorageOptions struct {
options.BaseIdOptions
TARGETSTORAGE string `json:"target_storage_id" help:"Target storage id or name"`
KeepOriginDisk bool `json:"keep_origin_disk" help:"Keep origin disk when changed"`
}
func (o *ServerChangeStorageOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(o), nil
}
type ServerCPUSetOptions struct {
options.BaseIdOptions
SETS string `help:"Cgroup cpusets CPUs spec string, e.g. '0-2,16'"`