diff --git a/pkg/compute/tasks/disk_reset_task.go b/pkg/compute/tasks/disk_reset_task.go index 1e5d187225..450147c492 100644 --- a/pkg/compute/tasks/disk_reset_task.go +++ b/pkg/compute/tasks/disk_reset_task.go @@ -102,16 +102,29 @@ func (self *DiskResetTask) OnStartGuest(ctx context.Context, disk *models.SDisk, func (self *DiskResetTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { disk := obj.(*models.SDisk) - storage, err := disk.GetStorage() - if err != nil { - self.TaskFailed(ctx, disk, errors.Wrapf(err, "disk.GetStorage")) - return - } - host, err := storage.GetMasterHost() - if err != nil { - self.TaskFailed(ctx, disk, errors.Wrapf(err, "storage.GetMasterHost")) - return + guest := disk.GetGuest() + + var host *models.SHost + if guest == nil { + storage, err := disk.GetStorage() + if err != nil { + self.TaskFailed(ctx, disk, errors.Wrapf(err, "disk.GetStorage")) + return + } + host, err = storage.GetMasterHost() + if err != nil { + self.TaskFailed(ctx, disk, errors.Wrapf(err, "storage.GetMasterHost")) + return + } + } else { + var err error + host, err = guest.GetHost() + if err != nil { + self.TaskFailed(ctx, disk, errors.Wrapf(err, "guest.GetHost")) + return + } } + self.RequestResetDisk(ctx, disk, host) } diff --git a/pkg/hostman/guestman/guesttasks.go b/pkg/hostman/guestman/guesttasks.go index 94c9820c28..d9da29e57d 100644 --- a/pkg/hostman/guestman/guesttasks.go +++ b/pkg/hostman/guestman/guesttasks.go @@ -2016,12 +2016,11 @@ func (s *SGuestDiskSnapshotTask) onReloadBlkdevSucc(res string) { } func (s *SGuestDiskSnapshotTask) onSnapshotBlkdevFail(reason string) { - snapshotDir := s.disk.GetSnapshotDir() - snapshotPath := path.Join(snapshotDir, s.snapshotId) - output, err := procutils.NewCommand("mv", "-f", snapshotPath, s.disk.GetPath()).Output() - if err != nil { - log.Errorf("mv %s to %s failed: %s, %s", snapshotPath, s.disk.GetPath(), err, output) + // rollback snapshot to disk file + if err := s.disk.RollbackDiskOnSnapshotFail(s.snapshotId); err != nil { + log.Errorf("failed do rollback %s", err) } + hostutils.TaskFailed(s.ctx, fmt.Sprintf("Reload blkdev error: %s", reason)) } diff --git a/pkg/hostman/storageman/disk_base.go b/pkg/hostman/storageman/disk_base.go index 2d03dc93f8..926868cbf4 100644 --- a/pkg/hostman/storageman/disk_base.go +++ b/pkg/hostman/storageman/disk_base.go @@ -46,6 +46,7 @@ type IDisk interface { DoDeleteSnapshot(snapshotId string) error GetSnapshotLocation() string GetSnapshotPath(snapshotId string) string + RollbackDiskOnSnapshotFail(snapshotId string) error GetStorage() IStorage @@ -246,6 +247,10 @@ func (d *SBaseDisk) DoDeleteSnapshot(snapshotId string) error { return fmt.Errorf("Not implement disk.DoDeleteSnapshot") } +func (d *SBaseDisk) RollbackDiskOnSnapshotFail(snapshotId string) error { + return errors.Errorf("Not implement disk.DoDeleteSnapshot") +} + func (d *SBaseDisk) GetBackupDir() string { return "" } diff --git a/pkg/hostman/storageman/disk_local.go b/pkg/hostman/storageman/disk_local.go index 54f539b164..663577d3f6 100644 --- a/pkg/hostman/storageman/disk_local.go +++ b/pkg/hostman/storageman/disk_local.go @@ -418,7 +418,6 @@ func (d *SLocalDisk) DiskBackup(ctx context.Context, params interface{}) (jsonut snapshotDir := d.GetSnapshotDir() snapshotPath := path.Join(snapshotDir, diskBackup.SnapshotId) - size, err := doBackupDisk(ctx, snapshotPath, diskBackup) if err != nil { return nil, errors.Wrap(err, "doBackupDisk") @@ -676,6 +675,16 @@ func (d *SLocalDisk) DoDeleteSnapshot(snapshotId string) error { return d.Storage.DeleteDiskfile(snapshotPath, false) } +func (d *SLocalDisk) RollbackDiskOnSnapshotFail(snapshotId string) error { + snapshotDir := d.GetSnapshotDir() + snapshotPath := path.Join(snapshotDir, snapshotId) + output, err := procutils.NewCommand("mv", "-f", snapshotPath, d.GetPath()).Output() + if err != nil { + return errors.Wrapf(err, "rollback disk on snapshot fail: %s", output) + } + return nil +} + func (d *SLocalDisk) IsFile() bool { return true } diff --git a/pkg/hostman/storageman/disk_lvm.go b/pkg/hostman/storageman/disk_lvm.go index 17b36b052f..0ae916e81f 100644 --- a/pkg/hostman/storageman/disk_lvm.go +++ b/pkg/hostman/storageman/disk_lvm.go @@ -31,12 +31,14 @@ import ( "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis" "yunion.io/x/onecloud/pkg/hostman/hostutils" "yunion.io/x/onecloud/pkg/hostman/options" "yunion.io/x/onecloud/pkg/hostman/storageman/lvmutils" "yunion.io/x/onecloud/pkg/hostman/storageman/storageutils" "yunion.io/x/onecloud/pkg/mcclient/auth" + identity_modules "yunion.io/x/onecloud/pkg/mcclient/modules/identity" "yunion.io/x/onecloud/pkg/util/fileutils2" "yunion.io/x/onecloud/pkg/util/fuseutils" "yunion.io/x/onecloud/pkg/util/procutils" @@ -407,8 +409,72 @@ func (d *SLVMDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) return res, nil } +func (d *SLVMDisk) GetBackupName(backupId string) string { + return "backup_" + backupId +} + +func (d *SLVMDisk) GetBackupPath(backupId string) string { + return path.Join("/dev", d.Storage.GetPath(), d.GetBackupName(backupId)) +} + func (d *SLVMDisk) DiskBackup(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { - return nil, errors.ErrNotImplemented + diskBackup := params.(*SDiskBackup) + + encKey := "" + if len(diskBackup.EncryptKeyId) > 0 { + session := auth.GetSession(ctx, diskBackup.UserCred, consts.GetRegion()) + secKey, err := identity_modules.Credentials.GetEncryptKey(session, diskBackup.EncryptKeyId) + if err != nil { + return nil, errors.Wrap(err, "GetEncryptKey") + } + encKey = secKey.Key + } + + snapshotPath := d.GetSnapshotPath(diskBackup.SnapshotId) + snapshotImg, err := qemuimg.NewQemuImage(snapshotPath) + if err != nil { + return nil, errors.Wrap(err, "lvm disk backup snapshotPath NewQemuImage") + } + + // create backup lv + lvSizeMb := lvmutils.GetQcow2LvSize(snapshotImg.SizeBytes / 1024 / 1024) + err = lvmutils.LvCreate(d.Storage.GetPath(), d.GetBackupName(diskBackup.BackupId), lvSizeMb*1024*1024) + if err != nil { + return nil, errors.Wrap(err, "lvcreate backup") + } + + backupPath := d.GetBackupPath(diskBackup.BackupId) + srcInfo := qemuimg.SImageInfo{ + Path: snapshotPath, + Format: snapshotImg.Format, + IoLevel: qemuimg.IONiceNone, + Password: encKey, + } + destInfo := qemuimg.SImageInfo{ + Path: backupPath, + Format: qemuimgfmt.QCOW2, + IoLevel: qemuimg.IONiceNone, + Password: encKey, + } + if err = qemuimg.Convert(srcInfo, destInfo, true, nil); err != nil { + if errRm := lvmutils.LvRemove(backupPath); errRm != nil { + log.Errorf("failed delete backup lv %s", errRm) + } + return nil, errors.Wrap(err, "failed convert snapshot to backup") + } + + _, err = d.Storage.StorageBackup(ctx, &SStorageBackup{ + BackupId: diskBackup.BackupId, + BackupLocalPath: backupPath, + BackupStorageId: diskBackup.BackupStorageId, + BackupStorageAccessInfo: diskBackup.BackupStorageAccessInfo, + }) + if err != nil { + return nil, errors.Wrap(err, "unable to SStorageBackup") + } + data := jsonutils.NewDict() + data.Set("size_mb", jsonutils.NewInt(snapshotImg.SizeBytes/1024/1024)) + return data, nil } func (d *SLVMDisk) CreateSnapshot(snapshotId string, encryptKey string, encFormat qemuimg.TEncryptFormat, encAlg seclib2.TSymEncAlg) error { @@ -441,7 +507,7 @@ func (d *SLVMDisk) CreateSnapshot(snapshotId string, encryptKey string, encForma } snapPath := d.GetSnapshotPath(snapshotId) - err = img.CreateQcow2(0, false, snapPath, "", "", "") + err = img.CreateQcow2(0, false, snapPath, encryptKey, encFormat, encAlg) if err != nil { if e := lvmutils.LvRemove(d.GetPath()); e != nil { log.Errorf("failed remove lv %s: %s", d.GetPath(), e) @@ -543,6 +609,20 @@ func (d *SLVMDisk) DoDeleteSnapshot(snapshotId string) error { return lvmutils.LvRemove(snapshotPath) } +func (d *SLVMDisk) RollbackDiskOnSnapshotFail(snapshotId string) error { + diskPath := d.GetPath() + if fileutils2.Exists(diskPath) { + if err := lvmutils.LvRemove(diskPath); err != nil { + return errors.Wrap(err, "rollback disk on snapshot fail delete disk") + } + } + snapshotName := d.GetSnapshotName(snapshotId) + if err := lvmutils.LvRename(d.Storage.GetPath(), snapshotName, d.Id); err != nil { + return errors.Wrapf(err, "RollbackDiskOnSnapshotFail rename %s to %s failed: %s", snapshotName, d.Id, err) + } + return nil +} + func (d *SLVMDisk) PrepareMigrate(liveMigrate bool) ([]string, string, bool, error) { disk, err := qemuimg.NewQemuImage(d.GetPath()) if err != nil { diff --git a/pkg/hostman/storageman/diskhandlers/diskhandler.go b/pkg/hostman/storageman/diskhandlers/diskhandler.go index efba2d7e4c..f73ed8d29e 100644 --- a/pkg/hostman/storageman/diskhandlers/diskhandler.go +++ b/pkg/hostman/storageman/diskhandlers/diskhandler.go @@ -414,27 +414,6 @@ func diskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, storag return nil, nil } -func diskStorageBackup(ctx context.Context, storage storageman.IStorage, diskId string, disk storageman.IDisk, body jsonutils.JSONObject) (interface{}, error) { - backupId, err := body.GetString("backup_id") - if err != nil { - return nil, httperrors.NewMissingParameterError("backup_id") - } - backupStorageId, err := body.GetString("backup_storage_id") - if err != nil { - return nil, httperrors.NewMissingParameterError("backup_storage_id") - } - backupStorageAccessInfo, err := body.Get("backup_storage_access_info") - if err != nil { - return nil, httperrors.NewMissingParameterError("backup_storage_access_info") - } - hostutils.DelayTask(ctx, storage.StorageBackup, &storageman.SStorageBackup{ - BackupId: backupId, - BackupStorageId: backupStorageId, - BackupStorageAccessInfo: backupStorageAccessInfo.(*jsonutils.JSONDict), - }) - return nil, nil -} - func diskStorageBackupRecovery(ctx context.Context, storage storageman.IStorage, diskId string, disk storageman.IDisk, body jsonutils.JSONObject) (interface{}, error) { backupId, err := body.GetString("backup_id") if err != nil { diff --git a/pkg/hostman/storageman/storage_base.go b/pkg/hostman/storageman/storage_base.go index 965e31c5cf..b9554ef65f 100644 --- a/pkg/hostman/storageman/storage_base.go +++ b/pkg/hostman/storageman/storage_base.go @@ -106,7 +106,7 @@ type IStorage interface { IsSnapshotExist(diskId, snapshotId string) (bool, error) GetBackupDir() string - StorageBackup(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) + StorageBackup(ctx context.Context, params *SStorageBackup) (jsonutils.JSONObject, error) StorageBackupRecovery(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) GetFreeSizeMb() int @@ -198,7 +198,7 @@ func (s *SBaseStorage) SetStoragecacheId(storagecacheId string) { s.StoragecacheId = storagecacheId } -func (s *SBaseStorage) StorageBackup(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { +func (s *SBaseStorage) StorageBackup(ctx context.Context, params *SStorageBackup) (jsonutils.JSONObject, error) { return nil, nil } diff --git a/pkg/hostman/storageman/storage_lvm.go b/pkg/hostman/storageman/storage_lvm.go index 5748bc475d..a40933f7e2 100644 --- a/pkg/hostman/storageman/storage_lvm.go +++ b/pkg/hostman/storageman/storage_lvm.go @@ -33,6 +33,7 @@ import ( "yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient" "yunion.io/x/onecloud/pkg/hostman/hostutils" "yunion.io/x/onecloud/pkg/hostman/options" + "yunion.io/x/onecloud/pkg/hostman/storageman/backupstorage" "yunion.io/x/onecloud/pkg/hostman/storageman/lvmutils" "yunion.io/x/onecloud/pkg/hostman/storageman/remotefile" "yunion.io/x/onecloud/pkg/httperrors" @@ -529,6 +530,23 @@ func (s *SLVMStorage) Detach() error { return nil } +func (s *SLVMStorage) StorageBackup(ctx context.Context, params *SStorageBackup) (jsonutils.JSONObject, error) { + backupStorage, err := backupstorage.GetBackupStorage(params.BackupStorageId, params.BackupStorageAccessInfo) + if err != nil { + return nil, err + } + backupPath := params.BackupLocalPath + err = backupStorage.SaveBackupFrom(ctx, backupPath, params.BackupId) + if err != nil { + return nil, err + } + // remove local backup + if err = lvmutils.LvRemove(backupPath); err != nil { + return nil, errors.Wrap(err, "On backuped lvremove") + } + return nil, nil +} + func (s *SLVMStorage) CloneDiskFromStorage( ctx context.Context, srcStorage IStorage, srcDisk IDisk, targetDiskId string, fullCopy bool, ) (*hostapi.ServerCloneDiskFromStorageResponse, error) { diff --git a/pkg/hostman/storageman/storagehelper.go b/pkg/hostman/storageman/storagehelper.go index 9ebc2f18fb..ee7a056489 100644 --- a/pkg/hostman/storageman/storagehelper.go +++ b/pkg/hostman/storageman/storagehelper.go @@ -86,6 +86,7 @@ type SDiskBackup struct { type SStorageBackup struct { BackupId string + BackupLocalPath string BackupStorageId string BackupStorageAccessInfo *jsonutils.JSONDict }