diff --git a/pkg/apis/compute/disk_const.go b/pkg/apis/compute/disk_const.go index 0b92c53405..5ea1d8af6e 100644 --- a/pkg/apis/compute/disk_const.go +++ b/pkg/apis/compute/disk_const.go @@ -64,7 +64,10 @@ const ( DISK_EXIST = "exist" ) -const DISK_META_EXISTING_PATH = "disk_existing_path" +const ( + DISK_META_EXISTING_PATH = "disk_existing_path" + DISK_META_LAST_ATTACHED_HOST = "__disk_last_attached_host" +) const ( DISK_DRIVER_VIRTIO = "virtio" diff --git a/pkg/apis/compute/storage_const.go b/pkg/apis/compute/storage_const.go index 78387750aa..866327667a 100644 --- a/pkg/apis/compute/storage_const.go +++ b/pkg/apis/compute/storage_const.go @@ -163,7 +163,7 @@ var ( STORAGE_NFS, STORAGE_GPFS, STORAGE_VSAN, STORAGE_CIFS, STORAGE_CLVM, STORAGE_SLVM} SHARED_FILE_STORAGE = []string{STORAGE_NFS, STORAGE_GPFS} - FIEL_STORAGE = []string{STORAGE_LOCAL, STORAGE_NFS, STORAGE_GPFS} + FIEL_STORAGE = []string{STORAGE_LOCAL, STORAGE_NFS, STORAGE_GPFS, STORAGE_LVM, STORAGE_CLVM, STORAGE_SLVM} // 目前来说只支持这些 SHARED_STORAGE = []string{STORAGE_NFS, STORAGE_GPFS, STORAGE_RBD, STORAGE_CLVM, STORAGE_SLVM} diff --git a/pkg/compute/hostdrivers/base.go b/pkg/compute/hostdrivers/base.go index 90d82fa524..9d6b9693f3 100644 --- a/pkg/compute/hostdrivers/base.go +++ b/pkg/compute/hostdrivers/base.go @@ -59,7 +59,11 @@ func (self *SBaseHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb i return fmt.Errorf("Not Implement ValidateDiskSize") } -func (self *SBaseHostDriver) RequestDeleteSnapshotsWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask) error { +func (self *SBaseHostDriver) RequestDeleteSnapshotsWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask, snapshotIds []string) error { + return fmt.Errorf("Not Implement") +} + +func (self *SBaseHostDriver) RequestDeleteSnapshotWithoutGuest(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, params *jsonutils.JSONDict, task taskman.ITask) error { return fmt.Errorf("Not Implement") } diff --git a/pkg/compute/hostdrivers/kvm.go b/pkg/compute/hostdrivers/kvm.go index 1e74a7f037..819ff964f4 100644 --- a/pkg/compute/hostdrivers/kvm.go +++ b/pkg/compute/hostdrivers/kvm.go @@ -410,10 +410,11 @@ func (self *SKVMHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, ho return err } -func (self *SKVMHostDriver) RequestDeleteSnapshotsWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask) error { +func (self *SKVMHostDriver) RequestDeleteSnapshotsWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask, snapshotIds []string) error { url := fmt.Sprintf("/storages/%s/delete-snapshots", snapshot.StorageId) body := jsonutils.NewDict() body.Set("disk_id", jsonutils.NewString(snapshot.DiskId)) + body.Set("snapshot_ids", jsonutils.NewStringArray(snapshotIds)) header := task.GetTaskRequestHeader() @@ -421,6 +422,14 @@ func (self *SKVMHostDriver) RequestDeleteSnapshotsWithStorage(ctx context.Contex return err } +func (self *SKVMHostDriver) RequestDeleteSnapshotWithoutGuest(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, params *jsonutils.JSONDict, task taskman.ITask) error { + url := fmt.Sprintf("/storages/%s/delete-snapshot", snapshot.StorageId) + header := task.GetTaskRequestHeader() + + _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, params) + return err +} + func (self *SKVMHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { if len(guests) > 1 { return nil, httperrors.NewBadRequestError("Disk attach muti guests") diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index fc36e1b095..4911716317 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -2190,6 +2190,21 @@ func (self *SDisk) RealDelete(ctx context.Context, userCred mcclient.TokenCreden return self.SVirtualResourceBase.Delete(ctx, userCred) } +func (self *SDisk) RecordLastAttachedHost(ctx context.Context, userCred mcclient.TokenCredential, hostId string) error { + storage, err := self.GetStorage() + if err != nil { + return err + } + if storage.StorageType != api.STORAGE_SLVM { + return nil + } + return self.SetMetadata(ctx, api.DISK_META_LAST_ATTACHED_HOST, hostId, userCred) +} + +func (self *SDisk) GetLastAttachedHost(ctx context.Context, userCred mcclient.TokenCredential) string { + return self.GetMetadata(ctx, api.DISK_META_LAST_ATTACHED_HOST, userCred) +} + // 同步磁盘状态 func (self *SDisk) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskSyncstatusInput) (jsonutils.JSONObject, error) { var openTask = true diff --git a/pkg/compute/models/hostdrivers.go b/pkg/compute/models/hostdrivers.go index 04f7dce7f5..fa33ba07c2 100644 --- a/pkg/compute/models/hostdrivers.go +++ b/pkg/compute/models/hostdrivers.go @@ -49,7 +49,8 @@ type IHostDriver interface { // resize disk RequestResizeDiskOnHost(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error - RequestDeleteSnapshotsWithStorage(ctx context.Context, host *SHost, snapshot *SSnapshot, task taskman.ITask) error + RequestDeleteSnapshotsWithStorage(ctx context.Context, host *SHost, snapshot *SSnapshot, task taskman.ITask, snapshotIds []string) error + RequestDeleteSnapshotWithoutGuest(ctx context.Context, host *SHost, snapshot *SSnapshot, params *jsonutils.JSONDict, task taskman.ITask) error RequestResetDisk(ctx context.Context, host *SHost, disk *SDisk, params *jsonutils.JSONDict, task taskman.ITask) error RequestCleanUpDiskSnapshots(ctx context.Context, host *SHost, disk *SDisk, params *jsonutils.JSONDict, task taskman.ITask) error PrepareConvert(host *SHost, image, raid string, data jsonutils.JSONObject) (*api.ServerCreateInput, error) diff --git a/pkg/compute/models/snapshots.go b/pkg/compute/models/snapshots.go index b1355bcca7..c711c1dd19 100644 --- a/pkg/compute/models/snapshots.go +++ b/pkg/compute/models/snapshots.go @@ -730,16 +730,20 @@ func (self *SSnapshotManager) PerformDeleteDiskSnapshots(ctx context.Context, us if snapshots == nil || len(snapshots) == 0 { return nil, httperrors.NewNotFoundError("Disk %s dose not have snapshot", diskId) } + snapshotIds := []string{} for i := 0; i < len(snapshots); i++ { if snapshots[i].FakeDeleted == false { return nil, httperrors.NewBadRequestError("Can not delete disk snapshots, have manual snapshot") } + snapshotIds = append(snapshotIds, snapshots[i].Id) } - err = snapshots[0].StartSnapshotsDeleteTask(ctx, userCred, "") + err = snapshots[0].StartSnapshotsDeleteTask(ctx, userCred, "", snapshotIds) return nil, err } -func (self *SSnapshot) StartSnapshotsDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { +func (self *SSnapshot) StartSnapshotsDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, snapshotIds []string) error { + data := jsonutils.NewDict() + data.Set("snapshot_ids", jsonutils.NewStringArray(snapshotIds)) task, err := taskman.TaskManager.NewTask(ctx, "BatchSnapshotsDeleteTask", self, userCred, nil, parentTaskId, "", nil) if err != nil { log.Errorln(err) diff --git a/pkg/compute/storagedrivers/base.go b/pkg/compute/storagedrivers/base.go index 2591a43abf..e297e33903 100644 --- a/pkg/compute/storagedrivers/base.go +++ b/pkg/compute/storagedrivers/base.go @@ -96,6 +96,36 @@ func (self *SBaseStorageDriver) RequestDeleteSnapshot(ctx context.Context, snaps return err } } + if guest == nil { + storage := snapshot.GetStorage() + host, err := storage.GetMasterHost() + if err != nil { + return err + } + convertSnapshot, err := models.SnapshotManager.GetConvertSnapshot(snapshot) + if err != nil && err != sql.ErrNoRows { + return errors.Wrap(err, "get convert snapshot") + } + params := jsonutils.NewDict() + params.Set("delete_snapshot", jsonutils.NewString(snapshot.Id)) + params.Set("disk_id", jsonutils.NewString(snapshot.DiskId)) + disk, err := models.DiskManager.FetchById(snapshot.DiskId) + if err != nil && err != sql.ErrNoRows { + return errors.Wrap(err, "get disk by snapshot") + } + if !snapshot.OutOfChain { + if convertSnapshot != nil { + params.Set("convert_snapshot", jsonutils.NewString(convertSnapshot.Id)) + } else if disk != nil { + params.Set("block_stream", jsonutils.JSONTrue) + } else { + params.Set("auto_deleted", jsonutils.JSONTrue) + } + } else { + params.Set("auto_deleted", jsonutils.JSONTrue) + } + return host.GetHostDriver().RequestDeleteSnapshotWithoutGuest(ctx, host, snapshot, params, task) + } if jsonutils.QueryBoolean(task.GetParams(), "reload_disk", false) && snapshot.OutOfChain { guest.SetStatus(task.GetUserCred(), api.VM_SNAPSHOT, "Start Reload Snapshot") diff --git a/pkg/compute/tasks/disk_delete_task.go b/pkg/compute/tasks/disk_delete_task.go index bf85fab26f..3a72b81f80 100644 --- a/pkg/compute/tasks/disk_delete_task.go +++ b/pkg/compute/tasks/disk_delete_task.go @@ -118,10 +118,16 @@ func (self *DiskDeleteTask) startDeleteDisk(ctx context.Context, disk *models.SD purgeParams := jsonutils.QueryBoolean(self.Params, "purge", false) - host, err := storage.GetMasterHost() - if err != nil && errors.Cause(err) != sql.ErrNoRows && !purgeParams { - self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString("storage.GetMasterHost")) - return + var host *models.SHost + if hostId := disk.GetLastAttachedHost(ctx, self.UserCred); hostId != "" { + host = models.HostManager.FetchHostById(hostId) + } + if host == nil { + host, err = storage.GetMasterHost() + if err != nil && errors.Cause(err) != sql.ErrNoRows && !purgeParams { + self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString("storage.GetMasterHost")) + return + } } isPurge := false diff --git a/pkg/compute/tasks/guest_detach_disk_task.go b/pkg/compute/tasks/guest_detach_disk_task.go index d57f5d060d..b60357a3c6 100644 --- a/pkg/compute/tasks/guest_detach_disk_task.go +++ b/pkg/compute/tasks/guest_detach_disk_task.go @@ -60,6 +60,12 @@ func (self *GuestDetachDiskTask) OnInit(ctx context.Context, obj db.IStandaloneM return } + err = disk.RecordLastAttachedHost(ctx, self.UserCred, host.Id) + if err != nil { + self.OnTaskFail(ctx, guest, nil, jsonutils.NewString(err.Error())) + return + } + if !host.GetEnabled() { self.OnDetachDiskCompleteFailed(ctx, guest, jsonutils.Marshal(map[string]string{"error": fmt.Sprintf("host %s(%s) is disabled", host.Name, host.Id)})) return diff --git a/pkg/compute/tasks/snapshot_delete_task.go b/pkg/compute/tasks/snapshot_delete_task.go index b11c4406e2..ba217cc5d5 100644 --- a/pkg/compute/tasks/snapshot_delete_task.go +++ b/pkg/compute/tasks/snapshot_delete_task.go @@ -172,8 +172,16 @@ func (self *BatchSnapshotsDeleteTask) StartStorageDeleteSnapshot(ctx context.Con self.SetStageFailed(ctx, jsonutils.NewString(errors.Wrapf(err, "snapshot.GetHost").Error())) return } + + snapshotIds := []string{} + err = self.Params.Unmarshal(&snapshotIds, "snapshot_ids") + if err != nil { + self.SetStageFailed(ctx, jsonutils.NewString(errors.Wrapf(err, "unmarshal snapshot ids").Error())) + return + } + self.SetStage("OnStorageDeleteSnapshot", nil) - err = host.GetHostDriver().RequestDeleteSnapshotsWithStorage(ctx, host, snapshot, self) + err = host.GetHostDriver().RequestDeleteSnapshotsWithStorage(ctx, host, snapshot, self, snapshotIds) if err != nil { self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) } diff --git a/pkg/compute/tasks/storage_cache_image_task.go b/pkg/compute/tasks/storage_cache_image_task.go index 52070e7df2..a11be71f70 100644 --- a/pkg/compute/tasks/storage_cache_image_task.go +++ b/pkg/compute/tasks/storage_cache_image_task.go @@ -62,8 +62,6 @@ func (self *StorageCacheImageTask) OnRelinquishLeastUsedCachedImageComplete(ctx db.OpsLog.LogEvent(storageCache, db.ACT_CACHING_IMAGE, imageId, self.UserCred) - self.SetStage("OnImageCacheComplete", nil) - var host *models.SHost var err error serverId, _ := self.Params.GetString("server_id") @@ -75,7 +73,8 @@ func (self *StorageCacheImageTask) OnRelinquishLeastUsedCachedImageComplete(ctx return } server := guest.(*models.SGuest) - server.SetStatus(self.GetUserCred(), api.VM_IMAGE_CACHING, "") + self.Params.Set("guest_oldstatus", jsonutils.NewString(server.Status)) + server.SetStatus(self.GetUserCred(), api.VM_IMAGE_CACHING, "start cache image") host, _ = server.GetHost() } else { host, err = storageCache.GetMasterHost() @@ -86,6 +85,7 @@ func (self *StorageCacheImageTask) OnRelinquishLeastUsedCachedImageComplete(ctx } } + self.SetStage("OnImageCacheComplete", nil) err = host.GetHostDriver().CheckAndSetCacheImage(ctx, host, storageCache, self) if err != nil { errData := taskman.Error2TaskData(err) @@ -133,6 +133,20 @@ func (self *StorageCacheImageTask) OnCacheSucc(ctx context.Context, cache *model scimg.SetExternalId(extImgId) } models.CachedimageManager.ImageAddRefCount(imageId) + + serverId, _ := self.Params.GetString("server_id") + if len(serverId) > 0 { + guest, err := models.GuestManager.FetchById(serverId) + if err != nil { + errData := taskman.Error2TaskData(err) + self.OnImageCacheCompleteFailed(ctx, cache, errData) + return + } + server := guest.(*models.SGuest) + serverOldStatus, _ := self.Params.GetString("guest_oldstatus") + server.SetStatus(self.GetUserCred(), serverOldStatus, "on cache image success") + } + db.OpsLog.LogEvent(cache, db.ACT_CACHED_IMAGE, imageId, self.UserCred) self.SetStageComplete(ctx, data) } diff --git a/pkg/hostimage/host_image_service.go b/pkg/hostimage/host_image_service.go index 49c0cc138c..413e209bc3 100644 --- a/pkg/hostimage/host_image_service.go +++ b/pkg/hostimage/host_image_service.go @@ -115,7 +115,7 @@ func getSnapshotPath(diskId, snapshotId string) string { } } for _, vg := range HostImageOptions.LVMVolumeGroups { - diskPath := path.Join("/dev", vg, "snap_"+diskId+snapshotId) + diskPath := path.Join("/dev", vg, "snap_"+snapshotId) if _, err := os.Stat(diskPath); !os.IsNotExist(err) { return diskPath } diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index 8cda9fcda3..f62964d999 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -1142,7 +1142,7 @@ func (m *SGuestManager) DeleteSnapshot(ctx context.Context, params interface{}) } else { res := jsonutils.NewDict() res.Set("deleted", jsonutils.JSONTrue) - return res, delParams.Disk.DeleteSnapshot(delParams.DeleteSnapshot, "") + return res, delParams.Disk.DeleteSnapshot(delParams.DeleteSnapshot, "", false) } } diff --git a/pkg/hostman/guestman/guesttasks.go b/pkg/hostman/guestman/guesttasks.go index 55cdf8c9f9..ce908c264b 100644 --- a/pkg/hostman/guestman/guesttasks.go +++ b/pkg/hostman/guestman/guesttasks.go @@ -29,6 +29,7 @@ import ( "yunion.io/x/pkg/errors" "yunion.io/x/pkg/utils" + api "yunion.io/x/onecloud/pkg/apis/compute" hostapi "yunion.io/x/onecloud/pkg/apis/host" "yunion.io/x/onecloud/pkg/appctx" "yunion.io/x/onecloud/pkg/hostman/guestman/qemu" @@ -1240,6 +1241,7 @@ type SGuestStreamDisksTask struct { c chan struct{} streamDevs []string + lvmBacking []string } func NewGuestStreamDisksTask(ctx context.Context, guest *SKVMGuestInstance, callback func(), disksIdx []int) *SGuestStreamDisksTask { @@ -1274,6 +1276,7 @@ func (s *SGuestStreamDisksTask) checkBlockDrives() { func (s *SGuestStreamDisksTask) onBlockDrivesSucc(blocks []monitor.QemuBlock) { s.streamDevs = []string{} + s.lvmBacking = []string{} for _, block := range blocks { if len(block.Inserted.File) > 0 && len(block.Inserted.BackingFile) > 0 { var stream = false @@ -1286,10 +1289,17 @@ func (s *SGuestStreamDisksTask) onBlockDrivesSucc(blocks []monitor.QemuBlock) { if !stream { continue } + s.streamDevs = append(s.streamDevs, block.Device) + disk, err := storageman.GetManager().GetDiskByPath(block.Inserted.File) + if err == nil && disk.GetType() == api.STORAGE_SLVM { + s.lvmBacking = append(s.lvmBacking, block.Inserted.BackingFile) + } else { + log.Errorf("failed get disk by path %s: %s", block.Inserted.File, err) + } } } - log.Infof("Stream devices %s: %v", s.GetName(), s.streamDevs) + log.Infof("Stream devices %s: %v , backingfiles %v", s.GetName(), s.streamDevs, s.lvmBacking) if len(s.streamDevs) == 0 { s.taskComplete() } else { @@ -1334,7 +1344,14 @@ func (s *SGuestStreamDisksTask) checkStreamJobs(jobs int) { } } +func (s *SGuestStreamDisksTask) deactivateLvmBackingFile() { + for _, lvPath := range s.lvmBacking { + storageman.TryDeactivateBackingLvs(lvPath) + } +} + func (s *SGuestStreamDisksTask) taskComplete() { + s.deactivateLvmBackingFile() hostutils.UpdateServerProgress(context.Background(), s.Id, 100.0, 0.0) s.SyncStatus("Guest Disks Block Stream Complete") diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index 632ce92e5a..bec1cb64a4 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -2211,7 +2211,7 @@ func (s *SKVMGuestInstance) ExecDeleteSnapshotTask( func (s *SKVMGuestInstance) deleteStaticSnapshotFile( ctx context.Context, disk storageman.IDisk, deleteSnapshot, convertSnapshot string, blockStream bool, ) (jsonutils.JSONObject, error) { - if err := disk.DeleteSnapshot(deleteSnapshot, convertSnapshot); err != nil { + if err := disk.DeleteSnapshot(deleteSnapshot, convertSnapshot, blockStream); err != nil { log.Errorln(err) return nil, err } diff --git a/pkg/hostman/storageman/disk_base.go b/pkg/hostman/storageman/disk_base.go index fbc7a17a82..cd8ec0a64b 100644 --- a/pkg/hostman/storageman/disk_base.go +++ b/pkg/hostman/storageman/disk_base.go @@ -60,14 +60,14 @@ type IDisk interface { RebuildSlaveDisk(diskUri string) error CreateFromUrl(ctx context.Context, url string, size int64, callback func(progress, progressMbps float64, totalSizeMb int64)) error CreateFromTemplate(context.Context, string, string, int64, *apis.SEncryptInfo) (jsonutils.JSONObject, error) - CreateFromSnapshotLocation(ctx context.Context, location string, size int64, encryptInfo *apis.SEncryptInfo) error + CreateFromSnapshotLocation(ctx context.Context, location string, size int64, encryptInfo *apis.SEncryptInfo) (jsonutils.JSONObject, error) CreateFromRbdSnapshot(ctx context.Context, snapshotId, srcDiskId, srcPool string) error CreateFromImageFuse(ctx context.Context, url string, size int64, encryptInfo *apis.SEncryptInfo) error CreateRaw(ctx context.Context, sizeMb int, diskFromat string, fsFormat string, encryptInfo *apis.SEncryptInfo, diskId string, back string) (jsonutils.JSONObject, error) PostCreateFromImageFuse() CreateSnapshot(snapshotId string, encryptKey string, encFormat qemuimg.TEncryptFormat, encAlg seclib2.TSymEncAlg) error - DeleteSnapshot(snapshotId, convertSnapshot string) error + DeleteSnapshot(snapshotId, convertSnapshot string, blockStream bool) error DeployGuestFs(diskInfo *deployapi.DiskInfo, guestDesc *jsonutils.JSONDict, deployInfo *deployapi.DeployInfo) (jsonutils.JSONObject, error) ConvertSnapshot(convertSnapshotId string) error @@ -118,8 +118,8 @@ func (d *SBaseDisk) CreateFromTemplate(context.Context, string, string, int64, * return nil, errors.Errorf("Not implemented") } -func (d *SBaseDisk) CreateFromSnapshotLocation(ctx context.Context, location string, size int64, encryptInfo *apis.SEncryptInfo) error { - return errors.Errorf("Not implemented") +func (d *SBaseDisk) CreateFromSnapshotLocation(ctx context.Context, location string, size int64, encryptInfo *apis.SEncryptInfo) (jsonutils.JSONObject, error) { + return nil, errors.Errorf("Not implemented") } func (d *SBaseDisk) CreateFromImageFuse(ctx context.Context, url string, size int64, encryptInfo *apis.SEncryptInfo) error { diff --git a/pkg/hostman/storageman/disk_local.go b/pkg/hostman/storageman/disk_local.go index c5bfe0b0e1..e02e1e2369 100644 --- a/pkg/hostman/storageman/disk_local.go +++ b/pkg/hostman/storageman/disk_local.go @@ -515,46 +515,9 @@ func (d *SLocalDisk) ConvertSnapshot(convertSnapshotId string) error { return nil } -func (d *SLocalDisk) DeleteSnapshot(snapshotId, convertSnapshot string) error { +func (d *SLocalDisk) DeleteSnapshot(snapshotId, convertSnapshot string, blockStream bool) error { snapshotDir := d.GetSnapshotDir() - snapshotPath := path.Join(snapshotDir, snapshotId) - if len(convertSnapshot) > 0 { - if !fileutils2.Exists(snapshotDir) { - err := procutils.NewCommand("mkdir", "-p", snapshotDir).Run() - if err != nil { - log.Errorln(err) - return err - } - } - convertSnapshotPath := path.Join(snapshotDir, convertSnapshot) - output := convertSnapshotPath + ".tmp" - if fileutils2.Exists(output) { - procutils.NewCommand("rm", "-f", output).Run() - } - img, err := qemuimg.NewQemuImage(convertSnapshotPath) - if err != nil { - return errors.Wrap(err, "NewQemuImage") - } - if err = img.Convert2Qcow2To(output, false, "", "", ""); err != nil { - log.Errorf("convert image %s to %s: %s", img.Path, output, err) - procutils.NewCommand("rm", "-f", output).Run() - return err - } - if err = procutils.NewCommand("rm", "-f", convertSnapshotPath).Run(); err != nil { - log.Errorf("rm convert snapshot file %s: %s", convertSnapshotPath, err) - return err - } - if err = procutils.NewCommand("mv", "-f", output, convertSnapshotPath).Run(); err != nil { - log.Errorf("mv snapshot file %s to %s: %s", output, convertSnapshotPath, err) - return err - } - } - err := procutils.NewCommand("rm", "-f", snapshotPath).Run() - if err != nil { - log.Errorf("rm snapshot file: %s", err) - return errors.Wrap(err, "rm snapshot file") - } - return nil + return DeleteLocalSnapshot(snapshotDir, snapshotId, d.getPath(), convertSnapshot, blockStream) } func (d *SLocalDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { diff --git a/pkg/hostman/storageman/disk_lvm.go b/pkg/hostman/storageman/disk_lvm.go index 322487ed1f..9e5d890750 100644 --- a/pkg/hostman/storageman/disk_lvm.go +++ b/pkg/hostman/storageman/disk_lvm.go @@ -74,7 +74,7 @@ func (d *SLVMDisk) GetPath() string { // The LVM logical volume name is limited to 64 characters. func (d *SLVMDisk) GetSnapshotName(snapshotId string) string { - return "snap_" + d.Id + "_" + snapshotId + return "snap_" + snapshotId } func (d *SLVMDisk) GetSnapshotPath(snapshotId string) string { @@ -408,20 +408,31 @@ func (d *SLVMDisk) CreateSnapshot(snapshotId string, encryptKey string, encForma return err } if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, lvSize); err != nil { + if e := lvmutils.LvRename(d.Storage.GetPath(), snapName, d.Id); e != nil { + log.Errorf("failed rename lv %s to %s: %s", snapName, d.GetPath(), e) + } return errors.Wrap(err, "snapshot LvCreate") } img, err := qemuimg.NewQemuImage(d.GetPath()) if err != nil { - lvmutils.LvRemove(d.GetPath()) - lvmutils.LvRename(d.Storage.GetPath(), snapName, d.Id) + if e := lvmutils.LvRemove(d.GetPath()); e != nil { + log.Errorf("failed remove lv %s: %s", d.GetPath(), e) + } + if e := lvmutils.LvRename(d.Storage.GetPath(), snapName, d.Id); e != nil { + log.Errorf("failed rename lv %s to %s: %s", snapName, d.GetPath(), e) + } return errors.Wrapf(err, "failed qemuimg.NewQemuImage(%s))", d.GetPath()) } snapPath := d.GetSnapshotPath(snapshotId) err = img.CreateQcow2(0, false, snapPath, "", "", "") if err != nil { - lvmutils.LvRemove(d.GetPath()) - lvmutils.LvRename(d.Storage.GetPath(), snapName, d.Id) + if e := lvmutils.LvRemove(d.GetPath()); e != nil { + log.Errorf("failed remove lv %s: %s", d.GetPath(), e) + } + if e := lvmutils.LvRename(d.Storage.GetPath(), snapName, d.Id); e != nil { + log.Errorf("failed rename lv %s to %s: %s", snapName, d.GetPath(), e) + } return errors.Wrapf(err, "CreateQcow2(%s)", snapPath) } return nil @@ -475,8 +486,12 @@ func (d *SLVMDisk) ResetFromSnapshot(ctx context.Context, params interface{}) (j return nil, nil } -func (d *SLVMDisk) DeleteSnapshot(snapshotId, convertSnapshot string) error { - if len(convertSnapshot) > 0 { +func (d *SLVMDisk) DeleteSnapshot(snapshotId, convertSnapshot string, blockStream bool) error { + if blockStream { + if err := ConvertLVMDisk(d.Storage.GetPath(), d.Id); err != nil { + return err + } + } else if len(convertSnapshot) > 0 { if err := d.ConvertSnapshot(convertSnapshot); err != nil { return err } @@ -503,59 +518,8 @@ func (d *SLVMDisk) DeleteAllSnapshot(skipRecycle bool) error { } func (d *SLVMDisk) ConvertSnapshot(convertSnapshot string) error { - convertSnapshotPath := d.GetSnapshotPath(convertSnapshot) - qemuImg, err := qemuimg.NewQemuImage(convertSnapshotPath) - if err != nil { - log.Errorln(err) - return err - } - lvSize, err := lvmutils.GetLvSize(convertSnapshotPath) - if err != nil { - return err - } - - tmpVolume := d.Id + "-convert.tmp" - tmpVolumePath := path.Join("/dev", d.Storage.GetPath(), tmpVolume) - // create /dev/vg/snapshot-convert.tmp - if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, lvSize); err != nil { - return errors.Wrap(err, "delete snapshot LvCreate") - } - srcInfo := qemuimg.SImageInfo{ - Path: convertSnapshotPath, - Format: qemuImg.Format, - IoLevel: qemuimg.IONiceNone, - Password: "", - } - destInfo := qemuimg.SImageInfo{ - Path: tmpVolumePath, - Format: qemuimg.QCOW2, - IoLevel: qemuimg.IONiceNone, - Password: "", - } - // convert /dev/vg/snapshot to /dev/vg/snapshot-convert.tmp - if err = qemuimg.Convert(srcInfo, destInfo, false, nil); err != nil { - lvmutils.LvRemove(tmpVolumePath) - return errors.Wrap(err, "failed convert tmp disk") - } - - tmpVolume2 := d.Id + "-convert.tmp2" - tmpVolume2Path := path.Join("/dev", d.Storage.GetPath(), tmpVolume2) - // rename /dev/vg/snapshot to /dev/vg/snapshot-convert.tmp2 - err = lvmutils.LvRename(d.Storage.GetPath(), convertSnapshot, tmpVolume2) - if err != nil { - return errors.Wrap(err, "failed rename disk to tmp") - } - // rename /dev/vg/snapshot-convert.tmp to /dev/vg/snapshot - err = lvmutils.LvRename(d.Storage.GetPath(), tmpVolume, convertSnapshot) - if err != nil { - return errors.Wrap(err, "failed rename tmp to disk") - } - // delete /dev/vg/snapshot-convert.tmp2 - err = lvmutils.LvRemove(tmpVolume2Path) - if err != nil { - return errors.Wrap(err, "failed remove tmp disk") - } - return nil + convertSnapshotName := d.GetSnapshotName(convertSnapshot) + return ConvertLVMDisk(d.Storage.GetPath(), convertSnapshotName) } func (d *SLVMDisk) DoDeleteSnapshot(snapshotId string) error { diff --git a/pkg/hostman/storageman/disk_nas.go b/pkg/hostman/storageman/disk_nas.go index 3c4df1aad3..e7111d9a97 100644 --- a/pkg/hostman/storageman/disk_nas.go +++ b/pkg/hostman/storageman/disk_nas.go @@ -48,16 +48,16 @@ func (d *SNasDisk) CreateFromImageFuse(ctx context.Context, url string, size int return fmt.Errorf("Not implemented") } -func (d *SNasDisk) CreateFromSnapshotLocation(ctx context.Context, snapshotLocation string, size int64, encryptInfo *apis.SEncryptInfo) error { +func (d *SNasDisk) CreateFromSnapshotLocation(ctx context.Context, snapshotLocation string, size int64, encryptInfo *apis.SEncryptInfo) (jsonutils.JSONObject, error) { snapshotPath := path.Join(d.Storage.GetPath(), snapshotLocation) newImg, err := qemuimg.NewQemuImage(d.GetPath()) if err != nil { - return errors.Wrap(err, "new image from snapshot") + return nil, errors.Wrap(err, "new image from snapshot") } if newImg.IsValid() { if err := newImg.Delete(); err != nil { log.Errorln(err) - return err + return nil, err } } if encryptInfo != nil { @@ -66,7 +66,7 @@ func (d *SNasDisk) CreateFromSnapshotLocation(ctx context.Context, snapshotLocat err = newImg.CreateQcow2(0, false, snapshotPath, "", "", "") } if err != nil { - return errors.Wrap(err, "create image from snapshot") + return nil, errors.Wrap(err, "create image from snapshot") } retSize, _ := d.GetDiskDesc().Int("disk_size") log.Infof("REQSIZE: %d, RETSIZE: %d", size, retSize) @@ -77,9 +77,9 @@ func (d *SNasDisk) CreateFromSnapshotLocation(ctx context.Context, snapshotLocat params.Set("encrypt_info", jsonutils.Marshal(encryptInfo)) } _, err = d.Resize(ctx, params) - return err + return nil, err } - return nil + return d.GetDiskDesc(), nil } func (d *SNasDisk) ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { diff --git a/pkg/hostman/storageman/disk_rbd.go b/pkg/hostman/storageman/disk_rbd.go index 396dfe458d..7ae143b342 100644 --- a/pkg/hostman/storageman/disk_rbd.go +++ b/pkg/hostman/storageman/disk_rbd.go @@ -263,7 +263,7 @@ func (d *SRBDDisk) ConvertSnapshot(convertSnapshotId string) error { return nil } -func (d *SRBDDisk) DeleteSnapshot(snapshotId, convertSnapshot string) error { +func (d *SRBDDisk) DeleteSnapshot(snapshotId, convertSnapshot string, blockStream bool) error { storage := d.Storage.(*SRbdStorage) pool, _ := storage.StorageConf.GetString("pool") return storage.deleteSnapshot(pool, d.Id, snapshotId) @@ -282,7 +282,7 @@ func (d *SRBDDisk) DiskDeleteSnapshot(ctx context.Context, params interface{}) ( if !ok { return nil, hostutils.ParamsError } - err := d.DeleteSnapshot(snapshotId, "") + err := d.DeleteSnapshot(snapshotId, "", false) if err != nil { return nil, err } else { diff --git a/pkg/hostman/storageman/disk_slvm.go b/pkg/hostman/storageman/disk_slvm.go index fa59cd3c2f..18ac764298 100644 --- a/pkg/hostman/storageman/disk_slvm.go +++ b/pkg/hostman/storageman/disk_slvm.go @@ -56,29 +56,27 @@ func (d *SSLVMDisk) Probe() error { } var lvPath = d.GetPath() - activated, err := lvmutils.LvIsActivated(lvPath) - if err != nil { - return errors.Wrap(err, "check lv is activated") - } - if !activated { - if err := lvmutils.LVActive(lvPath, d.Storage.Lvmlockd(), false); err != nil { - return errors.Wrap(err, "lv active") - } + if err := lvmutils.LVActive(lvPath, d.Storage.Lvmlockd(), false); err != nil { + return errors.Wrap(err, "lv active") } - qemuImg, err := qemuimg.NewQemuImage(d.GetPath()) - if err != nil { - log.Errorln(err) - return err - } - if qemuImg.BackFilePath != "" { - originActivated, err := lvmutils.LvIsActivated(qemuImg.BackFilePath) + diskPath := d.GetPath() + for diskPath != "" { + qemuImg, err := qemuimg.NewQemuImage(diskPath) if err != nil { - return errors.Wrap(err, "check lv is activated") + log.Errorln(err) + return err } - if !originActivated { - if err = lvmutils.LVActive(qemuImg.BackFilePath, d.Storage.Lvmlockd(), false); err != nil { - return errors.Wrap(err, "lv active origin") + diskPath = qemuImg.BackFilePath + if qemuImg.BackFilePath != "" { + originActivated, err := lvmutils.LvIsActivated(qemuImg.BackFilePath) + if err != nil { + return errors.Wrap(err, "check lv is activated") + } + if !originActivated { + if err = lvmutils.LVActive(qemuImg.BackFilePath, d.Storage.Lvmlockd(), false); err != nil { + return errors.Wrap(err, "lv active origin") + } } } } @@ -93,13 +91,23 @@ func (d *SSLVMDisk) CreateRaw( ctx context.Context, sizeMb int, diskFormat string, fsFormat string, encryptInfo *apis.SEncryptInfo, diskId string, back string, ) (jsonutils.JSONObject, error) { + if fileutils2.Exists(d.GetPath()) { + err := lvmutils.LVDeactivate(d.GetPath()) + if err != nil { + return nil, errors.Wrap(err, "LVDeactivate") + } + if err := lvmutils.LvRemove(d.GetLvPath()); err != nil { + return nil, errors.Wrap(err, "CreateRaw lvremove") + } + } ret, err := d.SLVMDisk.CreateRaw(ctx, sizeMb, diskFormat, fsFormat, encryptInfo, diskId, back) if err != nil { return ret, err } - err = lvmutils.LVActive(d.GetPath(), d.Storage.Lvmlockd(), false) + + err = lvmutils.LVDeactivate(d.GetPath()) if err != nil { - return ret, errors.Wrap(err, "lvactive shared") + return ret, errors.Wrap(err, "LVDeactivate") } return ret, nil } @@ -107,13 +115,23 @@ func (d *SSLVMDisk) CreateRaw( func (d *SSLVMDisk) CreateFromTemplate( ctx context.Context, imageId, format string, sizeMb int64, encryptInfo *apis.SEncryptInfo, ) (jsonutils.JSONObject, error) { + if fileutils2.Exists(d.GetPath()) { + err := lvmutils.LVDeactivate(d.GetPath()) + if err != nil { + return nil, errors.Wrap(err, "LVDeactivate") + } + if err := lvmutils.LvRemove(d.GetLvPath()); err != nil { + return nil, errors.Wrap(err, "CreateRaw lvremove") + } + } + ret, err := d.SLVMDisk.CreateFromTemplate(ctx, imageId, format, sizeMb, encryptInfo) if err != nil { return ret, err } - err = lvmutils.LVActive(d.GetPath(), d.Storage.Lvmlockd(), false) + err = lvmutils.LVDeactivate(d.GetPath()) if err != nil { - return ret, errors.Wrap(err, "lvactive shared") + return ret, errors.Wrap(err, "LVDeactivate") } return ret, nil } @@ -122,7 +140,7 @@ func (d *SSLVMDisk) PreResize(ctx context.Context, sizeMb int64) error { if ok, err := lvmutils.LvIsActivated(d.GetPath()); err != nil { return err } else if ok && d.Storage.Lvmlockd() { - err = lvmutils.LVActive(d.GetPath(), false, true) + err = lvmutils.LVActive(d.GetPath(), false, d.Storage.Lvmlockd()) if err != nil { return errors.Wrap(err, "lvactive shared") } @@ -142,7 +160,7 @@ func (d *SSLVMDisk) Resize(ctx context.Context, params interface{}) (jsonutils.J if ok, err := lvmutils.LvIsActivated(d.GetPath()); err != nil { return nil, err } else if ok && d.Storage.Lvmlockd() { - err = lvmutils.LVActive(d.GetPath(), false, true) + err = lvmutils.LVActive(d.GetPath(), false, d.Storage.Lvmlockd()) if err != nil { return nil, errors.Wrap(err, "lvactive shared") } @@ -158,6 +176,23 @@ func (d *SSLVMDisk) Resize(ctx context.Context, params interface{}) (jsonutils.J return ret, nil } +func TryDeactivateBackingLvs(backingFile string) { + if backingFile == "" { + return + } + qemuImg, err := qemuimg.NewQemuImage(backingFile) + if err != nil { + log.Errorf("tryDeactivateBackingLvs NewQemuImage %s", err) + return + } + err = lvmutils.LVDeactivate(backingFile) + if err != nil { + log.Errorf("tryDeactivateBackingLvs LVDeactivate %s", err) + return + } + TryDeactivateBackingLvs(qemuImg.BackFilePath) +} + func (d *SSLVMDisk) Delete(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { var lvPath = d.GetPath() activated, err := lvmutils.LvIsActivated(lvPath) @@ -169,38 +204,56 @@ func (d *SSLVMDisk) Delete(ctx context.Context, params interface{}) (jsonutils.J return nil, errors.Wrap(err, "lv active") } } + qemuImg, err := qemuimg.NewQemuImage(lvPath) + if err != nil { + return nil, errors.Wrap(err, "NewQemuImage") + } + TryDeactivateBackingLvs(qemuImg.BackFilePath) + return d.SLVMDisk.Delete(ctx, params) } func (d *SSLVMDisk) CreateSnapshot(snapshotId string, encryptKey string, encFormat qemuimg.TEncryptFormat, encAlg seclib2.TSymEncAlg) error { - err := lvmutils.LVActive(d.GetPath(), false, true) + err := lvmutils.LVActive(d.GetPath(), false, d.Storage.Lvmlockd()) if err != nil { return errors.Wrap(err, "lvactive exclusive") } err = d.SLVMDisk.CreateSnapshot(snapshotId, encryptKey, encFormat, encAlg) if err != nil { - err := lvmutils.LVActive(d.GetPath(), true, false) - if err != nil { - log.Errorf("failed lvactive share %s", err) + e3 := lvmutils.LVActive(d.GetPath(), d.Storage.Lvmlockd(), false) + if e3 != nil { + log.Errorf("failed lvactive share %s", e3) } return err } + + // active disk share mode + err = lvmutils.LVActive(d.GetPath(), d.Storage.Lvmlockd(), false) + if err != nil { + return errors.Wrap(err, "lvactive snapshot share") + } + + // active snapshot active mode snapPath := d.GetSnapshotPath(snapshotId) - err = lvmutils.LVActive(snapPath, false, true) + err = lvmutils.LVActive(snapPath, d.Storage.Lvmlockd(), false) if err != nil { return errors.Wrap(err, "lvactive snapshot share") } return nil } +func (d *SSLVMDisk) PostCreateFromImageFuse() { + log.Infof("slvm post create from fuse do nothing") +} + func (d *SSLVMDisk) ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { - err := lvmutils.LVActive(d.GetPath(), false, true) + err := lvmutils.LVActive(d.GetPath(), false, d.Storage.Lvmlockd()) if err != nil { return nil, errors.Wrap(err, "lvactive exclusive") } ret, err := d.SLVMDisk.ResetFromSnapshot(ctx, params) if err != nil { - err := lvmutils.LVActive(d.GetPath(), true, false) + err := lvmutils.LVActive(d.GetPath(), d.Storage.Lvmlockd(), false) if err != nil { log.Errorf("failed lvactive share %s", err) } @@ -208,3 +261,22 @@ func (d *SSLVMDisk) ResetFromSnapshot(ctx context.Context, params interface{}) ( } return ret, nil } + +func (d *SSLVMDisk) CreateFromSnapshotLocation(ctx context.Context, snapshotLocation string, size int64, encryptInfo *apis.SEncryptInfo) (jsonutils.JSONObject, error) { + ret, err := d.SLVMDisk.CreateRaw(ctx, int(size), "", "", encryptInfo, d.Id, snapshotLocation) + if err != nil { + return nil, err + } + + qemuImg, err := qemuimg.NewQemuImage(d.GetPath()) + if err != nil { + return nil, errors.Wrap(err, "NewQemuImage") + } + TryDeactivateBackingLvs(qemuImg.BackFilePath) + + err = lvmutils.LVDeactivate(d.GetPath()) + if err != nil { + return nil, errors.Wrap(err, "LVDeactivate") + } + return ret, nil +} diff --git a/pkg/hostman/storageman/storage_base.go b/pkg/hostman/storageman/storage_base.go index 851dde036f..839de721bd 100644 --- a/pkg/hostman/storageman/storage_base.go +++ b/pkg/hostman/storageman/storage_base.go @@ -98,6 +98,7 @@ type IStorage interface { GetSnapshotDir() string GetSnapshotPathByIds(diskId, snapshotId string) string + DeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) DeleteSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) IsSnapshotExist(diskId, snapshotId string) (bool, error) @@ -118,7 +119,7 @@ type IStorage interface { // *SDiskCreateByDiskinfo CreateDiskByDiskinfo(context.Context, interface{}) (jsonutils.JSONObject, error) SaveToGlance(context.Context, interface{}) (jsonutils.JSONObject, error) - CreateDiskFromSnapshot(context.Context, IDisk, *SDiskCreateByDiskinfo) error + CreateDiskFromSnapshot(context.Context, IDisk, *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) CreateDiskFromExistingPath(context.Context, IDisk, *SDiskCreateByDiskinfo) error CreateDiskFromBackup(context.Context, IDisk, *SDiskCreateByDiskinfo) error @@ -398,12 +399,7 @@ func (s *SBaseStorage) CreateDiskFromSnpashot(ctx context.Context, disk IDisk, i return nil, httperrors.NewMissingParameterError("snapshot_url") } - err := storage.CreateDiskFromSnapshot(ctx, disk, input) - if err != nil { - return nil, errors.Wrapf(err, "CreateDiskFromSnapshot") - } - - return disk.GetDiskDesc(), nil + return storage.CreateDiskFromSnapshot(ctx, disk, input) } func (s *SBaseStorage) createDiskFromExistingPath(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) { diff --git a/pkg/hostman/storageman/storage_local.go b/pkg/hostman/storageman/storage_local.go index 2ef11ddd11..aa41fa6b3b 100644 --- a/pkg/hostman/storageman/storage_local.go +++ b/pkg/hostman/storageman/storage_local.go @@ -543,11 +543,11 @@ func (s *SLocalStorage) CreateSnapshotFormUrl( } func (s *SLocalStorage) DeleteSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { - diskId, ok := params.(string) + input, ok := params.(SStorageDeleteSnapshots) if !ok { return nil, hostutils.ParamsError } - snapshotDir := path.Join(s.GetSnapshotDir(), diskId+options.HostOptions.SnapshotDirSuffix) + snapshotDir := path.Join(s.GetSnapshotDir(), input.DiskId+options.HostOptions.SnapshotDirSuffix) output, err := procutils.NewCommand("rm", "-rf", snapshotDir).Output() if err != nil { return nil, fmt.Errorf("Delete snapshot dir failed: %s", output) @@ -555,6 +555,82 @@ func (s *SLocalStorage) DeleteSnapshots(ctx context.Context, params interface{}) return nil, nil } +func (s *SLocalStorage) DeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { + input, ok := params.(SStorageDeleteSnapshot) + if !ok { + return nil, hostutils.ParamsError + } + + snapshotDir := path.Join(s.GetSnapshotDir(), input.DiskId+options.HostOptions.SnapshotDirSuffix) + diskPath := path.Join(s.GetPath(), input.DiskId) + return nil, DeleteLocalSnapshot(snapshotDir, input.SnapshotId, diskPath, input.ConvertSnapshot, input.BlockStream) +} + +func DeleteLocalSnapshot(snapshotDir, snapshotId, diskPath, convertSnapshot string, blockStream bool) error { + //snapshotDir := d.GetSnapshotDir() + snapshotPath := path.Join(snapshotDir, snapshotId) + if blockStream { + //diskPath := d.getPath() + output := diskPath + ".tmp" + if fileutils2.Exists(output) { + procutils.NewCommand("rm", "-f", output).Run() + } + img, err := qemuimg.NewQemuImage(diskPath) + if err != nil { + return errors.Wrap(err, "NewQemuImage") + } + if err = img.Convert2Qcow2To(output, false, "", "", ""); err != nil { + log.Errorf("convert image %s to %s: %s", img.Path, output, err) + procutils.NewCommand("rm", "-f", output).Run() + return err + } + if err = procutils.NewCommand("rm", "-f", diskPath).Run(); err != nil { + log.Errorf("rm convert disk file %s: %s", diskPath, err) + return err + } + if err = procutils.NewCommand("mv", "-f", output, diskPath).Run(); err != nil { + log.Errorf("mv disk file %s to %s: %s", output, diskPath, err) + return err + } + } else if len(convertSnapshot) > 0 { + if !fileutils2.Exists(snapshotDir) { + err := procutils.NewCommand("mkdir", "-p", snapshotDir).Run() + if err != nil { + log.Errorln(err) + return err + } + } + convertSnapshotPath := path.Join(snapshotDir, convertSnapshot) + output := convertSnapshotPath + ".tmp" + if fileutils2.Exists(output) { + procutils.NewCommand("rm", "-f", output).Run() + } + img, err := qemuimg.NewQemuImage(convertSnapshotPath) + if err != nil { + return errors.Wrap(err, "NewQemuImage") + } + if err = img.Convert2Qcow2To(output, false, "", "", ""); err != nil { + log.Errorf("convert image %s to %s: %s", img.Path, output, err) + procutils.NewCommand("rm", "-f", output).Run() + return err + } + if err = procutils.NewCommand("rm", "-f", convertSnapshotPath).Run(); err != nil { + log.Errorf("rm convert snapshot file %s: %s", convertSnapshotPath, err) + return err + } + if err = procutils.NewCommand("mv", "-f", output, convertSnapshotPath).Run(); err != nil { + log.Errorf("mv snapshot file %s to %s: %s", output, convertSnapshotPath, err) + return err + } + } + err := procutils.NewCommand("rm", "-f", snapshotPath).Run() + if err != nil { + log.Errorf("rm snapshot file: %s", err) + return errors.Wrap(err, "rm snapshot file") + } + return nil +} + func (s *SLocalStorage) DestinationPrepareMigrate( ctx context.Context, liveMigrate bool, disksUri string, snapshotsUri string, disksBackingFile, diskSnapsChain, outChainSnaps jsonutils.JSONObject, @@ -696,9 +772,7 @@ func doRebaseDisk(diskPath, newBasePath string, encInfo *apis.SEncryptInfo) erro return nil } -func (s *SLocalStorage) CreateDiskFromSnapshot( - ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo, -) error { +func (s *SLocalStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) { info := input.DiskInfo if info.Protocol == "fuse" { var encryptInfo *apis.SEncryptInfo @@ -707,11 +781,11 @@ func (s *SLocalStorage) CreateDiskFromSnapshot( } err := disk.CreateFromImageFuse(ctx, info.SnapshotUrl, int64(info.DiskSizeMb), encryptInfo) if err != nil { - return errors.Wrapf(err, "CreateFromImageFuse") + return nil, errors.Wrapf(err, "CreateFromImageFuse") } - return nil + return disk.GetDiskDesc(), nil } - return httperrors.NewUnsupportOperationError("Unsupport protocol %s for Local storage", info.Protocol) + return nil, httperrors.NewUnsupportOperationError("Unsupport protocol %s for Local storage", info.Protocol) } func (s *SLocalStorage) CreateDiskFromExistingPath( diff --git a/pkg/hostman/storageman/storage_lvm.go b/pkg/hostman/storageman/storage_lvm.go index f974164ae1..23e929f7d1 100644 --- a/pkg/hostman/storageman/storage_lvm.go +++ b/pkg/hostman/storageman/storage_lvm.go @@ -186,7 +186,33 @@ func (s *SLVMStorage) GetSnapshotPathByIds(diskId, snapshotId string) string { } func (s *SLVMStorage) DeleteSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { - return nil, errors.Errorf("unsupported operation") + input := params.(SStorageDeleteSnapshots) + for i := range input.SnapshotIds { + lvPath := path.Join("/dev", s.GetPath(), "snap_"+input.SnapshotIds[i]) + if err := lvmutils.LvRemove(lvPath); err != nil { + return nil, err + } + } + return nil, nil +} + +func (s *SLVMStorage) DeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { + input, ok := params.(SStorageDeleteSnapshot) + if !ok { + return nil, hostutils.ParamsError + } + if input.BlockStream { + if err := ConvertLVMDisk(s.GetPath(), input.DiskId); err != nil { + return nil, err + } + } else if len(input.ConvertSnapshot) > 0 { + convertSnapshotName := "snap_" + input.ConvertSnapshot + if err := ConvertLVMDisk(s.GetPath(), convertSnapshotName); err != nil { + return nil, err + } + } + snapId := path.Join("/dev", s.GetPath(), input.SnapshotId) + return nil, lvmutils.LvRemove(snapId) } func (s *SLVMStorage) IsSnapshotExist(diskId, snapshotId string) (bool, error) { @@ -426,7 +452,7 @@ func (s *SLVMStorage) DestinationPrepareMigrate( return nil } -func (s *SLVMStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) error { +func (s *SLVMStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) { info := input.DiskInfo if info.Protocol == "fuse" { var encryptInfo *apis.SEncryptInfo @@ -435,11 +461,11 @@ func (s *SLVMStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, in } err := disk.CreateFromImageFuse(ctx, info.SnapshotUrl, int64(info.DiskSizeMb), encryptInfo) if err != nil { - return errors.Wrapf(err, "CreateFromImageFuse") + return nil, errors.Wrapf(err, "CreateFromImageFuse") } - return nil + return disk.GetDiskDesc(), nil } - return httperrors.NewUnsupportOperationError("Unsupport protocol %s for lvm storage", info.Protocol) + return nil, httperrors.NewUnsupportOperationError("Unsupport protocol %s for lvm storage", info.Protocol) } func (s *SLVMStorage) CreateDiskFromExistingPath(context.Context, IDisk, *SDiskCreateByDiskinfo) error { @@ -526,3 +552,58 @@ func (s *SLVMStorage) CloneDiskFromStorage( TargetFormat: qemuimg.QCOW2.String(), }, nil } + +func ConvertLVMDisk(vgName, lvName string) error { + diskPath := path.Join("/dev", vgName, lvName) + qemuImg, err := qemuimg.NewQemuImage(diskPath) + if err != nil { + log.Errorln(err) + return err + } + lvSize, err := lvmutils.GetLvSize(diskPath) + if err != nil { + return err + } + + tmpVolume := lvName + "-convert.tmp" + tmpVolumePath := path.Join("/dev", vgName, tmpVolume) + // create /dev/vg/disk-convert.tmp + if err := lvmutils.LvCreate(vgName, tmpVolume, lvSize); err != nil { + return errors.Wrap(err, "delete snapshot LvCreate") + } + srcInfo := qemuimg.SImageInfo{ + Path: diskPath, + Format: qemuImg.Format, + IoLevel: qemuimg.IONiceNone, + Password: "", + } + destInfo := qemuimg.SImageInfo{ + Path: tmpVolumePath, + Format: qemuimg.QCOW2, + IoLevel: qemuimg.IONiceNone, + Password: "", + } + // convert /dev/vg/disk to /dev/vg/disk-convert.tmp + if err = qemuimg.Convert(srcInfo, destInfo, false, nil); err != nil { + lvmutils.LvRemove(tmpVolumePath) + return errors.Wrap(err, "failed convert tmp disk") + } + tmpVolume2 := lvName + "-convert.tmp2" + tmpVolume2Path := path.Join("/dev", vgName, tmpVolume2) + // rename /dev/vg/disk to /dev/vg/disk-convert.tmp2 + err = lvmutils.LvRename(vgName, diskPath, tmpVolume2) + if err != nil { + return errors.Wrap(err, "failed rename disk to tmp") + } + // rename /dev/vg/disk-convert.tmp to /dev/vg/disk + err = lvmutils.LvRename(vgName, tmpVolume, diskPath) + if err != nil { + return errors.Wrap(err, "failed rename tmp to disk") + } + // delete /dev/vg/disk-convert.tmp2 + err = lvmutils.LvRemove(tmpVolume2Path) + if err != nil { + return errors.Wrap(err, "failed remove tmp disk") + } + return nil +} diff --git a/pkg/hostman/storageman/storage_nas.go b/pkg/hostman/storageman/storage_nas.go index c21928e80e..77b871d618 100644 --- a/pkg/hostman/storageman/storage_nas.go +++ b/pkg/hostman/storageman/storage_nas.go @@ -94,11 +94,12 @@ func (s *SNasStorage) SyncStorageInfo() (jsonutils.JSONObject, error) { return res, err } -func (s *SNasStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) error { +func (s *SNasStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) { info := input.DiskInfo var encryptInfo *apis.SEncryptInfo if info.Encryption { encryptInfo = &info.EncryptInfo } + return disk.CreateFromSnapshotLocation(ctx, input.DiskInfo.SnapshotUrl, int64(input.DiskInfo.DiskSizeMb), encryptInfo) } diff --git a/pkg/hostman/storageman/storage_rbd.go b/pkg/hostman/storageman/storage_rbd.go index 47f6e7c23a..9025cf7cbb 100644 --- a/pkg/hostman/storageman/storage_rbd.go +++ b/pkg/hostman/storageman/storage_rbd.go @@ -576,9 +576,17 @@ func (s *SRbdStorage) DeleteSnapshots(ctx context.Context, params interface{}) ( return nil, fmt.Errorf("Not support delete snapshots") } -func (s *SRbdStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) error { +func (s *SRbdStorage) DeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { + return nil, fmt.Errorf("Not support delete snapshot") +} + +func (s *SRbdStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) { info := input.DiskInfo - return disk.CreateFromRbdSnapshot(ctx, info.SnapshotUrl, info.SrcDiskId, info.SrcPool) + err := disk.CreateFromRbdSnapshot(ctx, info.SnapshotUrl, info.SrcDiskId, info.SrcPool) + if err != nil { + return nil, err + } + return disk.GetDiskDesc(), nil } func (s *SRbdStorage) GetBackupDir() string { diff --git a/pkg/hostman/storageman/storage_slvm.go b/pkg/hostman/storageman/storage_slvm.go index 4711077aed..c8787e1028 100644 --- a/pkg/hostman/storageman/storage_slvm.go +++ b/pkg/hostman/storageman/storage_slvm.go @@ -15,11 +15,15 @@ package storageman import ( + "context" + "path" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/hostman/hostutils" "yunion.io/x/onecloud/pkg/hostman/storageman/lvmutils" ) @@ -88,18 +92,58 @@ func (s *SSLVMStorage) GetDiskById(diskId string) (IDisk, error) { } var disk = NewSLVMDisk(s, diskId) - if disk.Probe() == nil { + err := disk.Probe() + if err == nil { s.Disks = append(s.Disks, disk) return disk, nil + } else { + log.Errorf("failed probe slvm disk %s: %s", diskId, err) } return nil, errors.ErrNotFound } -func (s *SSLVMStorage) Accessible() error { - if err := lvmutils.VgActive(s.Path, true); err != nil { - log.Warningf("vgactive got %s", err) +func (s *SSLVMStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) { + snapshotLocation := disk.GetSnapshotPath(input.DiskInfo.SnapshotId) + + return disk.CreateFromSnapshotLocation(ctx, snapshotLocation, int64(input.DiskInfo.DiskSizeMb), &input.DiskInfo.EncryptInfo) +} + +func (s *SSLVMStorage) DeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { + input, ok := params.(SStorageDeleteSnapshot) + if !ok { + return nil, hostutils.ParamsError } + if input.BlockStream { + diskLvPath := path.Join("/dev", s.GetPath(), input.DiskId) + err := lvmutils.LVActive(diskLvPath, false, s.Lvmlockd()) + if err != nil { + return nil, errors.Wrap(err, "lvactive exclusive") + } + + err = ConvertLVMDisk(s.GetPath(), input.DiskId) + if err != nil { + return nil, err + } + + } else if len(input.ConvertSnapshot) > 0 { + convertSnapshotName := "snap_" + input.ConvertSnapshot + convertSnapshotPath := path.Join("/dev", s.GetPath(), convertSnapshotName) + err := lvmutils.LVActive(convertSnapshotPath, false, s.Lvmlockd()) + if err != nil { + return nil, errors.Wrap(err, "lvactive exclusive") + } + + if err := ConvertLVMDisk(s.GetPath(), convertSnapshotName); err != nil { + return nil, err + } + } + + snapId := path.Join("/dev", s.GetPath(), input.SnapshotId) + return nil, lvmutils.LvRemove(snapId) +} + +func (s *SSLVMStorage) Accessible() error { if err := lvmutils.VgDisplay(s.Path); err != nil { return err } diff --git a/pkg/hostman/storageman/storagehandler/storagehandler.go b/pkg/hostman/storageman/storagehandler/storagehandler.go index 193ab572ab..03ce00c984 100644 --- a/pkg/hostman/storageman/storagehandler/storagehandler.go +++ b/pkg/hostman/storageman/storagehandler/storagehandler.go @@ -56,6 +56,9 @@ func AddStorageHandler(prefix string, app *appsrv.Application) { app.AddHandler("POST", fmt.Sprintf("%s/%s//delete-snapshots", prefix, keyWords), auth.Authenticate(storageDeleteSnapshots)) + app.AddHandler("POST", + fmt.Sprintf("%s/%s//delete-snapshot", prefix, keyWords), + auth.Authenticate(storageDeleteSnapshot)) app.AddHandler("GET", fmt.Sprintf("%s/%s/is-mount-point", prefix, keyWords), auth.Authenticate(storageVerifyMountPoint)) @@ -441,6 +444,41 @@ func deleteBackup(ctx context.Context, params interface{}) (jsonutils.JSONObject return nil, nil } +func storageDeleteSnapshot(ctx context.Context, w http.ResponseWriter, r *http.Request) { + params, _, body := appsrv.FetchEnv(ctx, w, r) + var storageId = params[""] + storage := storageman.GetManager().GetStorage(storageId) + if storage == nil { + hostutils.Response(ctx, w, httperrors.NewNotFoundError("Stroage Not found")) + return + } + diskId, err := body.GetString("disk_id") + if err != nil { + hostutils.Response(ctx, w, httperrors.NewImageNotFoundError("disk_id")) + return + } + // blockStream indicate snapshot<-disk + blockStream := jsonutils.QueryBoolean(body, "block_stream", false) + autoDeleted := jsonutils.QueryBoolean(body, "auto_deleted", false) + + input := &storageman.SStorageDeleteSnapshot{ + DiskId: diskId, + BlockStream: blockStream, + } + + if !blockStream && !autoDeleted { + convertSnapshot, err := body.GetString("convert_snapshot") + if err != nil { + hostutils.Response(ctx, w, httperrors.NewMissingParameterError("convert_snapshot")) + return + } + input.ConvertSnapshot = convertSnapshot + } + + hostutils.DelayTask(ctx, storage.DeleteSnapshot, input) + hostutils.ResponseOk(ctx, w) +} + func storageDeleteSnapshots(ctx context.Context, w http.ResponseWriter, r *http.Request) { params, _, body := appsrv.FetchEnv(ctx, w, r) var storageId = params[""] @@ -454,6 +492,18 @@ func storageDeleteSnapshots(ctx context.Context, w http.ResponseWriter, r *http. hostutils.Response(ctx, w, httperrors.NewImageNotFoundError("disk_id")) return } - hostutils.DelayTask(ctx, storage.DeleteSnapshots, diskId) + snapshotIds := []string{} + err = body.Unmarshal(&snapshotIds, "snapshot_ids") + if err != nil { + hostutils.Response(ctx, w, httperrors.NewMissingParameterError("snapshot_ids")) + return + } + + input := &storageman.SStorageDeleteSnapshots{ + DiskId: diskId, + SnapshotIds: snapshotIds, + } + + hostutils.DelayTask(ctx, storage.DeleteSnapshots, input) hostutils.ResponseOk(ctx, w) } diff --git a/pkg/hostman/storageman/storagehelper.go b/pkg/hostman/storageman/storagehelper.go index 77836d7e87..12bf228fa6 100644 --- a/pkg/hostman/storageman/storagehelper.go +++ b/pkg/hostman/storageman/storagehelper.go @@ -46,6 +46,18 @@ type SDiskCleanupSnapshots struct { DeleteSnapshots []jsonutils.JSONObject } +type SStorageDeleteSnapshots struct { + DiskId string + SnapshotIds []string +} + +type SStorageDeleteSnapshot struct { + DiskId string + SnapshotId string + ConvertSnapshot string + BlockStream bool +} + type SDiskBakcup struct { SnapshotId string `json:"snapshot_id"` BackupId string `json:"backup_id"`