mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #16069 from wanyaoqi/fix/guest-change-storage
fix(region): guest change storage
This commit is contained in:
@@ -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{})
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'"`
|
||||
|
||||
Reference in New Issue
Block a user