mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
- feature: refact snapshot related code
- feature: rbd snapshot - bugfix: ceph cache iso image - delete disk create snapshot and guest create snapshot - fix auto snapshot - fix disk reset
This commit is contained in:
@@ -222,20 +222,6 @@ func init() {
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
type DiskCreateSnapshotOptions struct {
|
||||
DISK string `help:"ID or name of disk"`
|
||||
SNAPSHOT_NAME string `help:"Snapshot name"`
|
||||
}
|
||||
R(&DiskCreateSnapshotOptions{}, "disk-create-snapshot", "Disk create snapshot", func(s *mcclient.ClientSession, args *DiskCreateSnapshotOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
params.Add(jsonutils.NewString(args.SNAPSHOT_NAME), "name")
|
||||
disk, err := modules.Disks.PerformAction(s, args.DISK, "create-snapshot", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskSaveOptions struct {
|
||||
ID string `help:"ID or name of the disk" json:"-"`
|
||||
|
||||
@@ -46,14 +46,11 @@ func init() {
|
||||
})
|
||||
|
||||
type SnapshotDeleteOptions struct {
|
||||
ID string `help:"Delete snapshot id"`
|
||||
ID []string `help:"Delete snapshot id"`
|
||||
}
|
||||
R(&SnapshotDeleteOptions{}, "snapshot-delete", "Delete snapshots", func(s *mcclient.ClientSession, args *SnapshotDeleteOptions) error {
|
||||
result, err := modules.Snapshots.Delete(s, args.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
ret := modules.Snapshots.BatchDelete(s, args.ID, nil)
|
||||
printBatchResults(ret, modules.Snapshots.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
type DiskDeleteSnapshotsOptions struct {
|
||||
|
||||
@@ -30,6 +30,7 @@ type SSnapshotCreateInput struct {
|
||||
Size int `json:"size"`
|
||||
DiskType string `json:"disk_type"`
|
||||
CloudregionId string `json:"cloudregion_id"`
|
||||
OutOfChain bool `json:"out_of_chain"`
|
||||
}
|
||||
|
||||
type SSnapshotPolicyCreateInput struct {
|
||||
|
||||
@@ -105,6 +105,8 @@ const (
|
||||
|
||||
ACT_DISK_CLEAN_UP_SNAPSHOTS = "disk_clean_up_snapshots"
|
||||
ACT_DISK_CLEAN_UP_SNAPSHOTS_FAIL = "disk_clean_up_snapshots_fail"
|
||||
ACT_DISK_AUTO_SNAPSHOT = "disk_auto_snapshot"
|
||||
ACT_DISK_AUTO_SNAPSHOT_FAIL = "disk_auto_snapshot_fail"
|
||||
|
||||
ACT_ALLOCATING = "allocating"
|
||||
ACT_BACKUP_ALLOCATING = "backup_allocating"
|
||||
|
||||
@@ -207,10 +207,6 @@ func (self *SBaseGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest
|
||||
return fmt.Errorf("Not Implement")
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) StartGuestDiskSnapshotTask(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, params *jsonutils.JSONDict) error {
|
||||
return fmt.Errorf("Not Implement")
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) RequestDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, snapshotId, diskId string) error {
|
||||
return fmt.Errorf("Not Implement")
|
||||
}
|
||||
|
||||
@@ -747,27 +747,6 @@ func (self *SManagedVirtualizedGuestDriver) RequestChangeVmConfig(ctx context.Co
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizedGuestDriver) RequestDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, snapshotId, diskId string) error {
|
||||
iDisk, _ := models.DiskManager.FetchById(diskId)
|
||||
disk := iDisk.(*models.SDisk)
|
||||
providerDisk, err := disk.GetIDisk()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
|
||||
snapshot := iSnapshot.(*models.SSnapshot)
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
cloudSnapshot, err := providerDisk.CreateISnapshot(ctx, snapshot.Name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := jsonutils.NewDict()
|
||||
res.Set("snapshot_id", jsonutils.NewString(cloudSnapshot.GetId()))
|
||||
return res, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizedGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error {
|
||||
|
||||
uuid, _ := data.GetString("uuid")
|
||||
|
||||
@@ -296,12 +296,3 @@ func (self *SVirtualizedGuestDriver) StartGuestSaveImage(ctx context.Context, us
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVirtualizedGuestDriver) StartGuestDiskSnapshotTask(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, params *jsonutils.JSONDict) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "GuestDiskSnapshotTask", guest, userCred, params, "", "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -253,9 +253,13 @@ func (self *SKVMHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, ho
|
||||
snapshotHost.GetFetchUrl(true), snapshot.DiskId, snapshot.Id)))
|
||||
}
|
||||
content.Set("protocol", jsonutils.NewString(options.Options.SnapshotCreateDiskProtocol))
|
||||
} else if snapshotStorage.StorageType == api.STORAGE_RBD {
|
||||
pool, _ := snapshotStorage.StorageConf.GetString("pool")
|
||||
content.Set("snapshot_url", jsonutils.NewString(snapshot.Id))
|
||||
content.Set("src_disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
content.Set("src_pool", jsonutils.NewString(pool))
|
||||
} else {
|
||||
content.Set("snapshot_url", jsonutils.NewString(snapshot.Location))
|
||||
content.Set("protocol", jsonutils.NewString("location"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+70
-48
@@ -39,6 +39,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
@@ -560,8 +561,10 @@ func (self *SDisk) StartAllocate(ctx context.Context, host *SHost, storage *SSto
|
||||
content.Add(jsonutils.NewInt(int64(self.DiskSize)), "size")
|
||||
if len(snapshot) > 0 {
|
||||
content.Add(jsonutils.NewString(snapshot), "snapshot")
|
||||
SnapshotManager.AddRefCount(self.SnapshotId, 1)
|
||||
self.SetMetadata(ctx, "merge_snapshot", jsonutils.JSONTrue, userCred)
|
||||
if utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_LOCAL, api.STORAGE_GPFS, api.STORAGE_NFS}) {
|
||||
SnapshotManager.AddRefCount(self.SnapshotId, 1)
|
||||
self.SetMetadata(ctx, "merge_snapshot", jsonutils.JSONTrue, userCred)
|
||||
}
|
||||
} else if len(templateId) > 0 {
|
||||
content.Add(jsonutils.NewString(templateId), "image_id")
|
||||
}
|
||||
@@ -649,26 +652,6 @@ func (self *SDisk) CleanUpDiskSnapshots(ctx context.Context, userCred mcclient.T
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SDisk) AllowPerformCreateSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "create-snapshot")
|
||||
}
|
||||
|
||||
func (self *SDisk) PerformCreateSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
guests := self.GetGuests()
|
||||
if len(guests) != 1 {
|
||||
return nil, httperrors.NewBadRequestError("Disk dosen't attach guest??")
|
||||
}
|
||||
storage := self.GetStorage()
|
||||
if guests[0].Hypervisor == api.HYPERVISOR_KVM &&
|
||||
!utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_LOCAL, api.STORAGE_NFS, api.STORAGE_GPFS}) {
|
||||
return nil, httperrors.NewBadRequestError("storage %s not support snapshot", storage.StorageType)
|
||||
}
|
||||
|
||||
dataDict := data.(*jsonutils.JSONDict)
|
||||
dataDict.Set("disk_id", jsonutils.NewString(self.Id))
|
||||
return guests[0].PerformDiskSnapshot(ctx, userCred, query, dataDict)
|
||||
}
|
||||
|
||||
func (self *SDisk) AllowPerformDiskReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "disk-reset")
|
||||
}
|
||||
@@ -942,6 +925,16 @@ func (self *SDisk) validateDeleteCondition(ctx context.Context, isPurge bool) er
|
||||
if !isPurge && self.IsValidPrePaid() {
|
||||
return httperrors.NewForbiddenError("not allow to delete prepaid disk in valid status")
|
||||
}
|
||||
storage := self.GetStorage()
|
||||
if storage.StorageType == api.STORAGE_RBD {
|
||||
scnt, err := self.GetSnapshotCount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if scnt > 0 {
|
||||
return httperrors.NewBadRequestError("not allow to delete %s disk with snapshots", storage.StorageType)
|
||||
}
|
||||
}
|
||||
return self.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx)
|
||||
}
|
||||
|
||||
@@ -1879,40 +1872,69 @@ func (manager *SDiskManager) getAutoSnapshotDisks() []SDisk {
|
||||
|
||||
func (manager *SDiskManager) AutoDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
|
||||
disks := manager.getAutoSnapshotDisks()
|
||||
if disks == nil {
|
||||
if len(disks) == 0 {
|
||||
log.Infof("CronJob AutoDiskSnapshot: No disk need create snapshot")
|
||||
return
|
||||
}
|
||||
for _, disk := range disks {
|
||||
snapCount, err := disk.GetSnapshotCount()
|
||||
for i := 0; i < len(disks); i++ {
|
||||
disks[i].SetModelManager(DiskManager, &disks[i])
|
||||
var (
|
||||
err error
|
||||
snapCount int
|
||||
guests = disks[i].GetGuests()
|
||||
snapshotName = "Auto-" + disks[i].Name + time.Now().Format("2006-01-02#15:04:05")
|
||||
)
|
||||
|
||||
if len(guests) == 1 && !utils.IsInStringArray(guests[0].Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
err = fmt.Errorf("Guest(%s) in status(%s) cannot do snapshot action", guests[0].Id, guests[0].Status)
|
||||
goto onFail
|
||||
}
|
||||
if err := disks[i].CreateSnpashotAuto(ctx, userCred, snapshotName); err != nil {
|
||||
err = fmt.Errorf("Create snapshot auto failed %s", err)
|
||||
goto onFail
|
||||
}
|
||||
snapCount, err = SnapshotManager.Query().Equals("fake_deleted", false).
|
||||
Equals("disk_id", disks[i].Id).Equals("created_by", api.SNAPSHOT_AUTO).CountWithError()
|
||||
if err != nil {
|
||||
log.Errorf("GetSnapshotCount fail %s", err)
|
||||
continue
|
||||
err = fmt.Errorf("GetSnapshotCount fail %s", err)
|
||||
goto onFail
|
||||
}
|
||||
if snapCount >= options.Options.DefaultMaxSnapshotCount {
|
||||
continue
|
||||
|
||||
log.Infof("Auto snapshot count %v, max auto snapshot count %v",
|
||||
snapCount, options.Options.DefaultMaxSnapshotCount-options.Options.DefaultMaxManualSnapshotCount)
|
||||
if snapCount > (options.Options.DefaultMaxSnapshotCount - options.Options.DefaultMaxManualSnapshotCount) {
|
||||
disks[i].CleanOverdueSnapshots(ctx, userCred)
|
||||
}
|
||||
guests := disk.GetGuests()
|
||||
if len(guests) != 1 {
|
||||
log.Errorf("Disk %s(%s) is attached to %d guest(s)", disk.Name, disk.Id, len(guests))
|
||||
continue
|
||||
}
|
||||
if !utils.IsInStringArray(guests[0].Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
log.Errorf("Guest(%s) in status(%s) cannot do snapshot action", guests[0].Id, guests[0].Status)
|
||||
continue
|
||||
}
|
||||
// name
|
||||
name := "Auto-" + guests[0].Name + time.Now().Format("2006-01-02#15:04:05")
|
||||
snap, err := SnapshotManager.CreateSnapshot(ctx, userCred, api.SNAPSHOT_AUTO, disk.Id, guests[0].Id, "", name)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
continue
|
||||
}
|
||||
guests[0].StartDiskSnapshot(ctx, userCred, disk.Id, snap.Id)
|
||||
db.OpsLog.LogEvent(&disks[i], db.ACT_DISK_AUTO_SNAPSHOT, "disk auto snapshot "+snapshotName, userCred)
|
||||
continue
|
||||
onFail:
|
||||
db.OpsLog.LogEvent(&disks[i], db.ACT_DISK_AUTO_SNAPSHOT_FAIL, err.Error(), userCred)
|
||||
reason := fmt.Sprintf("Disk auto create snapshot failed: %s", err.Error())
|
||||
notifyclient.NotifySystemError(disks[i].Id, disks[i].Name, db.ACT_DISK_AUTO_SNAPSHOT_FAIL, reason)
|
||||
}
|
||||
}
|
||||
|
||||
func (disk *SDisk) StratCreateBackupTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
||||
if task, err := taskman.TaskManager.NewTask(ctx, "DiskCreateBackupTask", disk, userCred, nil, parentTaskId, "", nil); err != nil {
|
||||
func (self *SDisk) CreateSnpashotAuto(ctx context.Context, userCred mcclient.TokenCredential, snapshotName string) error {
|
||||
snap, err := SnapshotManager.CreateSnapshot(ctx, userCred, api.SNAPSHOT_AUTO, self.Id, "", "", snapshotName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
db.OpsLog.LogEvent(snap, db.ACT_CREATE, "disk create snapshot auto", userCred)
|
||||
return snap.StartSnapshotCreateTask(ctx, userCred, nil)
|
||||
}
|
||||
|
||||
func (self *SDisk) CleanOverdueSnapshots(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
if task, err := taskman.TaskManager.NewTask(ctx, "DiskCleanOverduedSnapshots", self, userCred, nil, "", "", nil); err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
} else {
|
||||
task.ScheduleRun(nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SDisk) StartCreateBackupTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
||||
if task, err := taskman.TaskManager.NewTask(ctx, "DiskCreateBackupTask", self, userCred, nil, parentTaskId, "", nil); err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
} else {
|
||||
|
||||
@@ -2226,70 +2226,6 @@ func (self *SGuest) PerformReset(ctx context.Context, userCred mcclient.TokenCre
|
||||
return nil, httperrors.NewInvalidStatusError("Cannot reset VM in status %s", self.Status)
|
||||
}
|
||||
|
||||
func (self *SGuest) AllowPerformDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "disk-snapshot")
|
||||
}
|
||||
|
||||
func (self *SGuest) PerformDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
if len(self.BackupHostId) > 0 {
|
||||
return nil, httperrors.NewBadRequestError("Guest has backup, can't create snapshot")
|
||||
}
|
||||
if !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
return nil, httperrors.NewInvalidStatusError("Cannot do snapshot when VM in status %s", self.Status)
|
||||
}
|
||||
if self.IsImport(userCred) {
|
||||
return nil, httperrors.NewBadRequestError("VM is import form libvirt, can't do snapshot")
|
||||
}
|
||||
|
||||
diskId, err := data.GetString("disk_id")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError(err.Error())
|
||||
}
|
||||
name, err := data.GetString("name")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError(err.Error())
|
||||
}
|
||||
err = ValidateSnapshotName(self.Hypervisor, name, userCred)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError(err.Error())
|
||||
}
|
||||
if self.GetGuestDisk(diskId) == nil {
|
||||
return nil, httperrors.NewNotFoundError("Guest disk %s not found", diskId)
|
||||
}
|
||||
pendingUsage := &SQuota{Snapshot: 1}
|
||||
quotaPlatform := self.GetQuotaPlatformID()
|
||||
_, err = QuotaManager.CheckQuota(ctx, userCred, rbacutils.ScopeProject, self.GetOwnerId(), quotaPlatform, pendingUsage)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewOutOfQuotaError("Out of snapshot quota %s", err)
|
||||
}
|
||||
if self.GetHypervisor() == api.HYPERVISOR_KVM {
|
||||
q := SnapshotManager.Query()
|
||||
cnt, err := q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
|
||||
sqlchemy.Equals(q.Field("created_by"), api.SNAPSHOT_MANUAL),
|
||||
sqlchemy.Equals(q.Field("fake_deleted"), false))).CountWithError()
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("check disk snapshot count fail %s", err)
|
||||
}
|
||||
if cnt >= options.Options.DefaultMaxManualSnapshotCount {
|
||||
return nil, httperrors.NewBadRequestError("Disk %s snapshot full, cannot take any more", diskId)
|
||||
}
|
||||
snapshot, err := SnapshotManager.CreateSnapshot(ctx, userCred, api.SNAPSHOT_MANUAL, diskId, self.Id, "", name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = self.StartDiskSnapshot(ctx, userCred, diskId, snapshot.Id)
|
||||
return nil, err
|
||||
} else {
|
||||
snapshot, err := SnapshotManager.CreateSnapshot(ctx, userCred, api.SNAPSHOT_MANUAL, diskId, self.Id, "", name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = self.StartDiskSnapshot(ctx, userCred, diskId, snapshot.Id)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (self *SGuest) AllowPerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "syncstatus")
|
||||
}
|
||||
@@ -2339,14 +2275,6 @@ func (self *SGuest) PerformStatus(ctx context.Context, userCred mcclient.TokenCr
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SGuest) StartDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, diskId, snapshotId string) error {
|
||||
self.SetStatus(userCred, api.VM_START_SNAPSHOT, "StartDiskSnapshot")
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("disk_id", jsonutils.NewString(diskId))
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshotId))
|
||||
return self.GetDriver().StartGuestDiskSnapshotTask(ctx, userCred, self, params)
|
||||
}
|
||||
|
||||
func (self *SGuest) AllowPerformStop(ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
|
||||
@@ -154,7 +154,6 @@ type IGuestDriver interface {
|
||||
RequestGuestHotAddIso(ctx context.Context, guest *SGuest, path string, task taskman.ITask) error
|
||||
RequestRebuildRootDisk(ctx context.Context, guest *SGuest, task taskman.ITask) error
|
||||
|
||||
StartGuestDiskSnapshotTask(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, params *jsonutils.JSONDict) error
|
||||
RequestDiskSnapshot(ctx context.Context, guest *SGuest, task taskman.ITask, snapshotId, diskId string) error
|
||||
RequestDeleteSnapshot(ctx context.Context, guest *SGuest, task taskman.ITask, params *jsonutils.JSONDict) error
|
||||
RequestReloadDiskSnapshot(ctx context.Context, guest *SGuest, task taskman.ITask, params *jsonutils.JSONDict) error
|
||||
|
||||
@@ -519,6 +519,21 @@ func (guest *SGuest) validateDeleteCondition(ctx context.Context, isPurge bool)
|
||||
if !isPurge && guest.IsValidPrePaid() {
|
||||
return httperrors.NewForbiddenError("not allow to delete prepaid server in valid status")
|
||||
}
|
||||
gd := guest.GetDisks()
|
||||
for i := 0; i < len(gd); i++ {
|
||||
d := gd[i].GetDisk()
|
||||
storage := d.GetStorage()
|
||||
if storage.StorageType == api.STORAGE_RBD {
|
||||
scnt, err := d.GetSnapshotCount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if scnt > 0 {
|
||||
return httperrors.NewBadRequestError(
|
||||
"not allow to delete guest with %s disk has snapshots", storage.StorageType)
|
||||
}
|
||||
}
|
||||
}
|
||||
return guest.SVirtualResourceBase.ValidateDeleteCondition(ctx)
|
||||
}
|
||||
|
||||
|
||||
@@ -78,11 +78,21 @@ type IRegionDriver interface {
|
||||
ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error)
|
||||
ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error)
|
||||
|
||||
// Region Driver Snapshot Policy Apis
|
||||
ValidateCreateSnapshotPolicyData(ctx context.Context, userCred mcclient.TokenCredential, data *compute.SSnapshotPolicyCreateInput) error
|
||||
RequestCreateSnapshotPolicy(ctx context.Context, userCred mcclient.TokenCredential, sp *SSnapshotPolicy, task taskman.ITask) error
|
||||
RequestDeleteSnapshotPolicy(ctx context.Context, userCred mcclient.TokenCredential, sp *SSnapshotPolicy, task taskman.ITask) error
|
||||
RequestApplySnapshotPolicy(ctx context.Context, userCred mcclient.TokenCredential, sp *SSnapshotPolicy, task taskman.ITask, diskIds []string) error
|
||||
RequestCancelSnapshotPolicy(ctx context.Context, userCred mcclient.TokenCredential, region cloudprovider.ICloudRegion, task taskman.ITask, diskIds []string) error
|
||||
|
||||
// Region Driver Snapshot Apis
|
||||
ValidateSnapshotDelete(ctx context.Context, snapshot *SSnapshot) error
|
||||
ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *SDisk, data *jsonutils.JSONDict) error
|
||||
RequestCreateSnapshot(ctx context.Context, snapshot *SSnapshot, task taskman.ITask) error
|
||||
RequestDeleteSnapshot(ctx context.Context, snapshot *SSnapshot, task taskman.ITask) error
|
||||
SnapshotIsOutOfChain(disk *SDisk) bool
|
||||
GetDiskResetParams(snapshot *SSnapshot) *jsonutils.JSONDict
|
||||
OnDiskReset(ctx context.Context, userCred mcclient.TokenCredential, disk *SDisk, snapshot *SSnapshot, data jsonutils.JSONObject) error
|
||||
}
|
||||
|
||||
var regionDrivers map[string]IRegionDriver
|
||||
|
||||
@@ -18,14 +18,13 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/compare"
|
||||
"yunion.io/x/pkg/util/timeutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
@@ -50,12 +49,14 @@ type SSnapshot struct {
|
||||
|
||||
SManagedResourceBase
|
||||
|
||||
DiskId string `width:"36" charset:"ascii" nullable:"true" create:"required" list:"user"`
|
||||
DiskId string `width:"36" charset:"ascii" nullable:"true" create:"required" list:"user"`
|
||||
|
||||
// Only onecloud has StorageId
|
||||
StorageId string `width:"36" charset:"ascii" nullable:"true" list:"admin" create:"optional"`
|
||||
CreatedBy string `width:"36" charset:"ascii" nullable:"false" default:"manual" list:"admin" create:"optional"`
|
||||
Location string `charset:"ascii" nullable:"true" list:"admin" create:"optional"`
|
||||
Size int `nullable:"false" list:"user" create:"required"` // MB
|
||||
OutOfChain bool `nullable:"false" default:"false" list:"admin"`
|
||||
OutOfChain bool `nullable:"false" default:"false" list:"admin" create:"optional"`
|
||||
FakeDeleted bool `nullable:"false" default:"false"`
|
||||
DiskType string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional"`
|
||||
|
||||
@@ -79,7 +80,7 @@ func init() {
|
||||
SnapshotManager.SetVirtualObject(SnapshotManager)
|
||||
}
|
||||
|
||||
func ValidateSnapshotName(hypervisor, name string, owner mcclient.IIdentityProvider) error {
|
||||
func ValidateSnapshotName(name string, owner mcclient.IIdentityProvider) error {
|
||||
q := SnapshotManager.Query()
|
||||
q = SnapshotManager.FilterByName(q, name)
|
||||
q = SnapshotManager.FilterByOwner(q, owner, SnapshotManager.NamespaceScope())
|
||||
@@ -97,12 +98,6 @@ func ValidateSnapshotName(hypervisor, name string, owner mcclient.IIdentityProvi
|
||||
if len(name) < 2 || len(name) > 128 {
|
||||
return httperrors.NewBadRequestError("Snapshot name length must within 2~128")
|
||||
}
|
||||
if hypervisor == api.HYPERVISOR_ALIYUN {
|
||||
if strings.HasPrefix(name, "auto") || strings.HasPrefix(name, "http://") || strings.HasPrefix(name, "https://") {
|
||||
return httperrors.NewBadRequestError(
|
||||
"Snapshot for %s name can't start with auto, http:// or https://", hypervisor)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -253,39 +248,17 @@ func (manager *SSnapshotManager) ValidateCreateData(ctx context.Context, userCre
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("name")
|
||||
}
|
||||
|
||||
guests := disk.GetGuests()
|
||||
if len(guests) != 1 {
|
||||
return nil, httperrors.NewBadRequestError("Disk %s dosen't attach guest ?", disk.Id)
|
||||
}
|
||||
|
||||
guest := guests[0]
|
||||
if len(guest.BackupHostId) > 0 {
|
||||
return nil, httperrors.NewBadRequestError(
|
||||
"Disk attached Guest has backup, Can't create snapshot")
|
||||
}
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
return nil, httperrors.NewInvalidStatusError("Cannot do snapshot when VM in status %s", guest.Status)
|
||||
}
|
||||
err = ValidateSnapshotName(guest.Hypervisor, snapshotName, ownerId)
|
||||
err = ValidateSnapshotName(snapshotName, ownerId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if guest.GetHypervisor() == api.HYPERVISOR_KVM {
|
||||
q := SnapshotManager.Query()
|
||||
cnt, err := q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), disk.Id),
|
||||
sqlchemy.Equals(q.Field("created_by"), api.SNAPSHOT_MANUAL),
|
||||
sqlchemy.IsFalse(q.Field("fake_deleted")))).CountWithError()
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("check disk snapshot count fail %s", err)
|
||||
}
|
||||
if cnt >= options.Options.DefaultMaxManualSnapshotCount {
|
||||
return nil, httperrors.NewBadRequestError("Disk %s snapshot full, cannot take any more", disk.Id)
|
||||
}
|
||||
|
||||
err = disk.GetStorage().GetRegion().GetDriver().ValidateSnapshotCreate(ctx, userCred, disk, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quotaPlatform := guest.GetQuotaPlatformID()
|
||||
|
||||
quotaPlatform := disk.GetQuotaPlatformID()
|
||||
pendingUsage := &SQuota{Snapshot: 1}
|
||||
_, err = QuotaManager.CheckQuota(ctx, userCred, rbacutils.ScopeProject, ownerId, quotaPlatform, pendingUsage)
|
||||
if err != nil {
|
||||
@@ -300,6 +273,7 @@ func (manager *SSnapshotManager) ValidateCreateData(ctx context.Context, userCre
|
||||
input.CreatedBy = api.SNAPSHOT_MANUAL
|
||||
input.Size = disk.DiskSize
|
||||
input.DiskType = disk.DiskType
|
||||
input.OutOfChain = disk.GetStorage().GetRegion().GetDriver().SnapshotIsOutOfChain(disk)
|
||||
storage := disk.GetStorage()
|
||||
if len(disk.ExternalId) == 0 {
|
||||
input.StorageId = disk.StorageId
|
||||
@@ -316,11 +290,16 @@ func (self *SSnapshot) CustomizeCreate(ctx context.Context, userCred mcclient.To
|
||||
|
||||
func (manager *SSnapshotManager) OnCreateComplete(ctx context.Context, items []db.IModel, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
snapshot := items[0].(*SSnapshot)
|
||||
guest, _ := snapshot.GetGuest()
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshot.Id))
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
guest.GetDriver().StartGuestDiskSnapshotTask(ctx, userCred, guest, params)
|
||||
snapshot.StartSnapshotCreateTask(ctx, userCred, nil)
|
||||
}
|
||||
|
||||
func (self *SSnapshot) StartSnapshotCreateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "SnapshotCreateTask", self, userCred, params, "", "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SSnapshot) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
@@ -487,31 +466,21 @@ func (self *SSnapshot) StartSnapshotDeleteTask(ctx context.Context, userCred mcc
|
||||
}
|
||||
|
||||
func (self *SSnapshot) ValidateDeleteCondition(ctx context.Context) error {
|
||||
if len(self.ExternalId) == 0 && self.RefCount > 0 {
|
||||
return httperrors.NewBadRequestError("Snapshot reference(by disk) count > 0, can not delete")
|
||||
if self.Status == api.SNAPSHOT_DELETING {
|
||||
return httperrors.NewBadRequestError("Cannot delete snapshot in status %s", self.Status)
|
||||
}
|
||||
return nil
|
||||
return self.GetRegionDriver().ValidateSnapshotDelete(ctx, self)
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetStorage() *SStorage {
|
||||
return StorageManager.FetchStorageById(self.StorageId)
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetRegionDriver() IRegionDriver {
|
||||
return self.GetRegion().GetDriver()
|
||||
}
|
||||
|
||||
func (self *SSnapshot) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
if self.Status == api.SNAPSHOT_DELETING {
|
||||
return fmt.Errorf("Cannot delete snapshot in status %s", self.Status)
|
||||
}
|
||||
if len(self.ExternalId) == 0 {
|
||||
if self.CreatedBy == api.SNAPSHOT_MANUAL {
|
||||
if !self.OutOfChain && !self.FakeDeleted {
|
||||
return self.FakeDelete(userCred)
|
||||
} else if self.OutOfChain {
|
||||
return self.StartSnapshotDeleteTask(ctx, userCred, false, "")
|
||||
}
|
||||
_, err := SnapshotManager.GetConvertSnapshot(self)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot delete snapshot: %s, disk need at least one of snapshot as backing file", err.Error())
|
||||
}
|
||||
return self.StartSnapshotDeleteTask(ctx, userCred, false, "")
|
||||
}
|
||||
return fmt.Errorf("Cannot delete snapshot created by %s", self.CreatedBy)
|
||||
}
|
||||
return self.StartSnapshotDeleteTask(ctx, userCred, false, "")
|
||||
}
|
||||
|
||||
@@ -609,6 +578,16 @@ func (self *SSnapshot) Delete(ctx context.Context, userCred mcclient.TokenCreden
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SSnapshotManager) DeleteDiskSnapshots(ctx context.Context, userCred mcclient.TokenCredential, diskId string) error {
|
||||
snapshots := self.GetDiskSnapshots(diskId)
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
if err := snapshots[i].RealDelete(ctx, userCred); err != nil {
|
||||
return errors.Wrap(err, "delete snapshot")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TotalSnapshotCount(scope rbacutils.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObj db.IStandaloneModel, providers []string, brands []string, cloudEnv string) (int, error) {
|
||||
q := SnapshotManager.Query()
|
||||
|
||||
@@ -796,7 +775,7 @@ func (self *SSnapshot) AllowPerformPurge(ctx context.Context, userCred mcclient.
|
||||
}
|
||||
|
||||
func (self *SSnapshot) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
err := self.ValidateDeleteCondition(ctx)
|
||||
err := self.GetRegionDriver().ValidateSnapshotDelete(ctx, self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -32,6 +32,13 @@ type IStorageDriver interface {
|
||||
DoStorageUpdateTask(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, task taskman.ITask) error
|
||||
|
||||
PostCreate(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, data jsonutils.JSONObject)
|
||||
|
||||
ValidateSnapshotDelete(ctx context.Context, snapshot *SSnapshot) error
|
||||
ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *SDisk, data *jsonutils.JSONDict) error
|
||||
RequestCreateSnapshot(ctx context.Context, snapshot *SSnapshot, task taskman.ITask) error
|
||||
RequestDeleteSnapshot(ctx context.Context, snapshot *SSnapshot, task taskman.ITask) error
|
||||
SnapshotIsOutOfChain(disk *SDisk) bool
|
||||
OnDiskReset(ctx context.Context, userCred mcclient.TokenCredential, disk *SDisk, snapshot *SSnapshot, data jsonutils.JSONObject) error
|
||||
}
|
||||
|
||||
var storageDrivers map[string]IStorageDriver
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/util/choices"
|
||||
@@ -845,3 +846,12 @@ func (self *SAliyunRegionDriver) ValidateCreateSnapshotPolicyData(ctx context.Co
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SAliyunRegionDriver) ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, data *jsonutils.JSONDict) error {
|
||||
name, _ := data.GetString("name")
|
||||
if strings.HasPrefix(name, "auto") || strings.HasPrefix(name, "http://") || strings.HasPrefix(name, "https://") {
|
||||
return httperrors.NewBadRequestError(
|
||||
"Snapshot for %s name can't start with auto, http:// or https://", self.GetProvider())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
@@ -143,3 +145,31 @@ func (self *SBaseRegionDriver) RequestApplySnapshotPolicy(ctx context.Context, u
|
||||
func (self *SBaseRegionDriver) RequestCancelSnapshotPolicy(ctx context.Context, userCred mcclient.TokenCredential, region cloudprovider.ICloudRegion, task taskman.ITask, diskIds []string) error {
|
||||
return fmt.Errorf("Not Implement RequestApplySnapshotPolicy")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) ValidateSnapshotDelete(ctx context.Context, snapshot *models.SSnapshot) error {
|
||||
return fmt.Errorf("Not Implement ValidateSnapshotDelete")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) RequestDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not Implement RequestDeleteSnapshot")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, data *jsonutils.JSONDict) error {
|
||||
return fmt.Errorf("Not Implement ValidateSnapshotCreate")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) RequestCreateSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not Implement RequestCreateSnapshot")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) SnapshotIsOutOfChain(disk *models.SDisk) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) GetDiskResetParams(snapshot *models.SSnapshot) *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) OnDiskReset(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, data *jsonutils.JSONObject) error {
|
||||
return fmt.Errorf("Not Implement OnDiskReset")
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ package regiondrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
@@ -48,3 +50,7 @@ func (self *SEsxiRegionDriver) ValidateCreateLoadbalancerAclData(ctx context.Con
|
||||
func (self *SEsxiRegionDriver) ValidateCreateLoadbalancerCertificateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
return nil, httperrors.NewNotImplementedError("%s does not support creating loadbalancer certificate", self.GetProvider())
|
||||
}
|
||||
|
||||
func (self *SEsxiRegionDriver) ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, data *jsonutils.JSONDict) error {
|
||||
return fmt.Errorf("%s does not support creating snapshot", self.GetProvider())
|
||||
}
|
||||
|
||||
@@ -717,3 +717,58 @@ func (self *SKVMRegionDriver) ValidateCreateVpcData(ctx context.Context, userCre
|
||||
func (self *SKVMRegionDriver) ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
return nil, httperrors.NewNotImplementedError("Not Implement EIP")
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) ValidateSnapshotDelete(ctx context.Context, snapshot *models.SSnapshot) error {
|
||||
storage := snapshot.GetStorage()
|
||||
if storage == nil {
|
||||
return httperrors.NewInternalServerError("Kvm snapshot missing storage ??")
|
||||
}
|
||||
return models.GetStorageDriver(storage.StorageType).ValidateSnapshotDelete(ctx, snapshot)
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) RequestDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
storage := snapshot.GetStorage()
|
||||
if storage == nil {
|
||||
return httperrors.NewInternalServerError("Kvm snapshot missing storage ??")
|
||||
}
|
||||
return models.GetStorageDriver(storage.StorageType).RequestDeleteSnapshot(ctx, snapshot, task)
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, data *jsonutils.JSONDict) error {
|
||||
storage := disk.GetStorage()
|
||||
return models.GetStorageDriver(storage.StorageType).ValidateSnapshotCreate(ctx, userCred, disk, data)
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) RequestCreateSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
storage := snapshot.GetStorage()
|
||||
if storage == nil {
|
||||
return httperrors.NewInternalServerError("Kvm snapshot missing storage ??")
|
||||
}
|
||||
return models.GetStorageDriver(storage.StorageType).RequestCreateSnapshot(ctx, snapshot, task)
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) SnapshotIsOutOfChain(disk *models.SDisk) bool {
|
||||
storage := disk.GetStorage()
|
||||
return models.GetStorageDriver(storage.StorageType).SnapshotIsOutOfChain(disk)
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) GetDiskResetParams(snapshot *models.SSnapshot) *jsonutils.JSONDict {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshot.Id))
|
||||
params.Set("out_of_chain", jsonutils.NewBool(snapshot.OutOfChain))
|
||||
return params
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) OnDiskReset(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, data jsonutils.JSONObject) error {
|
||||
if disk.DiskSize != snapshot.Size {
|
||||
_, err := db.Update(disk, func() error {
|
||||
disk.DiskSize = snapshot.Size
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
storage := disk.GetStorage()
|
||||
return models.GetStorageDriver(storage.StorageType).OnDiskReset(ctx, userCred, disk, snapshot, data)
|
||||
}
|
||||
|
||||
@@ -1112,3 +1112,74 @@ func (self *SManagedVirtualizationRegionDriver) RequestCancelSnapshotPolicy(ctx
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) ValidateSnapshotDelete(ctx context.Context, snapshot *models.SSnapshot) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
cloudRegion, err := snapshot.GetISnapshotRegion()
|
||||
if err != nil {
|
||||
log.Errorln(err, cloudRegion, snapshot.CloudregionId)
|
||||
return err
|
||||
}
|
||||
cloudSnapshot, err := cloudRegion.GetISnapshotById(snapshot.ExternalId)
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
return nil
|
||||
}
|
||||
log.Errorln(err, cloudSnapshot)
|
||||
return err
|
||||
}
|
||||
if err := cloudSnapshot.Delete(); err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitDeleted(cloudSnapshot, 10*time.Second, 300*time.Second)
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, data *jsonutils.JSONDict) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestCreateSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
iDisk, _ := models.DiskManager.FetchById(snapshot.DiskId)
|
||||
disk := iDisk.(*models.SDisk)
|
||||
providerDisk, err := disk.GetIDisk()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
cloudSnapshot, err := providerDisk.CreateISnapshot(ctx, snapshot.Name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := jsonutils.NewDict()
|
||||
res.Set("snapshot_id", jsonutils.NewString(cloudSnapshot.GetId()))
|
||||
return res, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) GetDiskResetParams(snapshot *models.SSnapshot) *jsonutils.JSONDict {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshot.ExternalId))
|
||||
return params
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) OnDiskReset(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, data jsonutils.JSONObject) error {
|
||||
externalId, _ := data.GetString("exteranl_disk_id")
|
||||
if len(externalId) > 0 {
|
||||
_, err := db.Update(disk, func() error {
|
||||
disk.ExternalId = externalId
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
iDisk, err := disk.GetIDisk()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return iDisk.Refresh()
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ func StartService() {
|
||||
|
||||
cron.AddJob1WithStartRun("AutoSyncCloudaccountTask", time.Duration(opts.CloudAutoSyncIntervalSeconds)*time.Second, models.CloudaccountManager.AutoSyncCloudaccountTask, true)
|
||||
|
||||
cron.AddJob2("AutoDiskSnapshot", opts.AutoSnapshotDay, opts.AutoSnapshotHour, 0, 0, models.DiskManager.AutoDiskSnapshot, false)
|
||||
cron.AddJob2("SyncSkus", opts.SyncSkusDay, opts.SyncSkusHour, 0, 0, models.SyncSkus, true)
|
||||
cron.AddJob2("AutoDiskSnapshot", opts.AutoSnapshotDay, opts.AutoSnapshotHour, 0, 0, models.DiskManager.AutoDiskSnapshot, true)
|
||||
cron.AddJob2("SyncSkus", opts.SyncSkusDay, opts.SyncSkusHour, 0, 0, models.SyncSkus, false)
|
||||
|
||||
cron.Start()
|
||||
defer cron.Stop()
|
||||
|
||||
@@ -16,12 +16,19 @@ package storagedrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
@@ -44,3 +51,108 @@ func (self *SBaseStorageDriver) DoStorageUpdateTask(ctx context.Context, userCre
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SBaseStorageDriver) ValidateSnapshotDelete(ctx context.Context, snapshot *models.SSnapshot) error {
|
||||
if snapshot.RefCount > 0 {
|
||||
return httperrors.NewBadRequestError("Snapshot reference(by disk) count > 0, can not delete")
|
||||
}
|
||||
|
||||
if !snapshot.OutOfChain && snapshot.FakeDeleted {
|
||||
_, err := models.SnapshotManager.GetConvertSnapshot(snapshot)
|
||||
if err != nil {
|
||||
return httperrors.NewBadRequestError("disk need at least one of snapshot as backing file")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SBaseStorageDriver) ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, data *jsonutils.JSONDict) error {
|
||||
guests := disk.GetGuests()
|
||||
if len(guests) != 1 {
|
||||
return httperrors.NewBadRequestError("Disk %s dosen't attach guest ?", disk.Id)
|
||||
}
|
||||
guest := guests[0]
|
||||
if len(guest.BackupHostId) > 0 {
|
||||
return httperrors.NewBadRequestError(
|
||||
"Disk attached Guest has backup, Can't create snapshot")
|
||||
}
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
return httperrors.NewInvalidStatusError("Cannot do snapshot when VM in status %s", guest.Status)
|
||||
}
|
||||
q := models.SnapshotManager.Query()
|
||||
cnt, err := q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), disk.Id),
|
||||
sqlchemy.Equals(q.Field("created_by"), api.SNAPSHOT_MANUAL),
|
||||
sqlchemy.IsFalse(q.Field("fake_deleted")))).CountWithError()
|
||||
if err != nil {
|
||||
return httperrors.NewInternalServerError("check disk snapshot count fail %s", err)
|
||||
}
|
||||
if cnt >= options.Options.DefaultMaxManualSnapshotCount {
|
||||
return httperrors.NewBadRequestError("Disk %s snapshot full, cannot take any more", disk.Id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SBaseStorageDriver) RequestCreateSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var params = jsonutils.NewDict()
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshot.Id))
|
||||
nt, err := taskman.TaskManager.NewTask(ctx, "GuestDiskSnapshotTask", guest, task.GetUserCred(), params, task.GetTaskId(), "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nt.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SBaseStorageDriver) RequestDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
return err
|
||||
} else if !snapshot.FakeDeleted {
|
||||
snapshot.SetStatus(task.GetUserCred(), compute.SNAPSHOT_READY, "snapshot fake_delete")
|
||||
task.SetStageComplete(ctx, nil)
|
||||
return snapshot.FakeDelete(task.GetUserCred())
|
||||
}
|
||||
}
|
||||
|
||||
if jsonutils.QueryBoolean(task.GetParams(), "reload_disk", false) && snapshot.OutOfChain {
|
||||
guest.SetStatus(task.GetUserCred(), api.VM_SNAPSHOT, "Start Reload Snapshot")
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
return guest.GetDriver().RequestReloadDiskSnapshot(ctx, guest, task, params)
|
||||
} else {
|
||||
convertSnapshot, _ := models.SnapshotManager.GetConvertSnapshot(snapshot)
|
||||
if convertSnapshot == nil {
|
||||
return fmt.Errorf("snapshot dose not have convert snapshot")
|
||||
}
|
||||
snapshot.SetStatus(task.GetUserCred(), api.SNAPSHOT_DELETING, "On SnapshotDeleteTask StartDeleteSnapshot")
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("delete_snapshot", jsonutils.NewString(snapshot.Id))
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
if !snapshot.OutOfChain {
|
||||
params.Set("convert_snapshot", jsonutils.NewString(convertSnapshot.Id))
|
||||
var FakeDelete = jsonutils.JSONFalse
|
||||
if snapshot.CreatedBy == api.SNAPSHOT_MANUAL && snapshot.FakeDeleted == false {
|
||||
FakeDelete = jsonutils.JSONTrue
|
||||
}
|
||||
params.Set("pending_delete", FakeDelete)
|
||||
} else {
|
||||
params.Set("auto_deleted", jsonutils.JSONTrue)
|
||||
}
|
||||
guest.SetStatus(task.GetUserCred(), api.VM_SNAPSHOT_DELETE, "Start Delete Snapshot")
|
||||
return guest.GetDriver().RequestDeleteSnapshot(ctx, guest, task, params)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SBaseStorageDriver) SnapshotIsOutOfChain(disk *models.SDisk) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SBaseStorageDriver) OnDiskReset(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, data jsonutils.JSONObject) error {
|
||||
return disk.CleanUpDiskSnapshots(ctx, userCred, snapshot)
|
||||
}
|
||||
|
||||
@@ -42,5 +42,4 @@ func (self *SLocalStorageDriver) ValidateCreateData(ctx context.Context, userCre
|
||||
}
|
||||
|
||||
func (self *SLocalStorageDriver) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, storage *models.SStorage, data jsonutils.JSONObject) {
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
type SRbdStorageDriver struct {
|
||||
@@ -171,3 +173,55 @@ func (self *SRbdStorageDriver) DoStorageUpdateTask(ctx context.Context, userCred
|
||||
subtask.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRbdStorageDriver) ValidateSnapshotDelete(ctx context.Context, snapshot *models.SSnapshot) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRbdStorageDriver) ValidateSnapshotCreate(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, data *jsonutils.JSONDict) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRbdStorageDriver) RequestCreateSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
disk, err := snapshot.GetDisk()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "snapshot get disk")
|
||||
}
|
||||
storage := snapshot.GetStorage()
|
||||
host := storage.GetMasterHost()
|
||||
url := fmt.Sprintf("%s/disks/%s/snapshot/%s", host.ManagerUri, storage.Id, disk.Id)
|
||||
header := task.GetTaskRequestHeader()
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshot.Id))
|
||||
_, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, params, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request create snapshot")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRbdStorageDriver) RequestDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
disk, err := snapshot.GetDisk()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "snapshot get disk")
|
||||
}
|
||||
storage := snapshot.GetStorage()
|
||||
host := storage.GetMasterHost()
|
||||
url := fmt.Sprintf("%s/disks/%s/delete-snapshot/%s", host.ManagerUri, storage.Id, disk.Id)
|
||||
header := task.GetTaskRequestHeader()
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshot.Id))
|
||||
_, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, params, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "request create snapshot")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRbdStorageDriver) SnapshotIsOutOfChain(disk *models.SDisk) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SRbdStorageDriver) OnDiskReset(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, data jsonutils.JSONObject) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
)
|
||||
|
||||
type DiskCleanOverduedSnapshots struct {
|
||||
SDiskBaseTask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(DiskCleanOverduedSnapshots{})
|
||||
}
|
||||
|
||||
func (self *DiskCleanOverduedSnapshots) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
disk := obj.(*models.SDisk)
|
||||
|
||||
count, err := models.SnapshotManager.Query().Equals("disk_id", disk.Id).
|
||||
Equals("created_by", compute.SNAPSHOT_AUTO).Equals("fake_deleted", false).CountWithError()
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if count <= (options.Options.DefaultMaxSnapshotCount - options.Options.DefaultMaxManualSnapshotCount) {
|
||||
self.SetStageComplete(ctx, nil)
|
||||
return
|
||||
}
|
||||
|
||||
snapshot := new(models.SSnapshot)
|
||||
err = models.SnapshotManager.Query().Equals("disk_id", disk.Id).
|
||||
Equals("created_by", compute.SNAPSHOT_AUTO).Equals("fake_deleted", false).Asc("created_at").First(snapshot)
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, err.Error())
|
||||
return
|
||||
}
|
||||
snapshot.SetModelManager(models.SnapshotManager, snapshot)
|
||||
err = snapshot.StartSnapshotDeleteTask(ctx, self.UserCred, false, self.Id)
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -133,6 +133,7 @@ func (self *DiskDeleteTask) OnGuestDiskDeleteComplete(ctx context.Context, obj d
|
||||
if len(disk.SnapshotId) > 0 && disk.GetMetadata("merge_snapshot", nil) == "true" {
|
||||
models.SnapshotManager.AddRefCount(disk.SnapshotId, -1)
|
||||
}
|
||||
|
||||
disk.RealDelete(ctx, self.UserCred)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -98,17 +98,8 @@ func (self *DiskResetTask) RequestResetDisk(ctx context.Context, disk *models.SD
|
||||
}
|
||||
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
|
||||
snapshot := iSnapshot.(*models.SSnapshot)
|
||||
params := jsonutils.NewDict()
|
||||
if len(snapshot.ExternalId) == 0 {
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshot.Id))
|
||||
if snapshot.OutOfChain {
|
||||
params.Set("out_of_chain", jsonutils.JSONTrue)
|
||||
} else {
|
||||
params.Set("out_of_chain", jsonutils.JSONFalse)
|
||||
}
|
||||
} else {
|
||||
params.Set("snapshot_id", jsonutils.NewString(snapshot.ExternalId))
|
||||
}
|
||||
params := snapshot.GetRegionDriver().GetDiskResetParams(snapshot)
|
||||
|
||||
self.SetStage("OnRequestResetDisk", nil)
|
||||
err = host.GetHostDriver().RequestResetDisk(ctx, host, disk, params, self)
|
||||
if err != nil {
|
||||
@@ -125,25 +116,13 @@ func (self *DiskResetTask) OnRequestResetDisk(ctx context.Context, disk *models.
|
||||
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
|
||||
snapshot := iSnapshot.(*models.SSnapshot)
|
||||
|
||||
externalId, _ := data.GetString("exteranl_disk_id")
|
||||
if disk.DiskSize != snapshot.Size || (len(externalId) > 0 && externalId != disk.GetExternalId()) {
|
||||
_, err := db.Update(disk, func() error {
|
||||
disk.DiskSize = snapshot.Size
|
||||
disk.ExternalId = externalId
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
if len(snapshot.ExternalId) == 0 {
|
||||
err := disk.CleanUpDiskSnapshots(ctx, self.UserCred, snapshot)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
self.TaskFailed(ctx, disk, fmt.Sprintf("OnRequestResetDisk %s", err.Error()))
|
||||
return
|
||||
}
|
||||
err := snapshot.GetRegionDriver().OnDiskReset(ctx, self.UserCred, disk, snapshot, data)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
self.TaskFailed(ctx, disk, fmt.Sprintf("OnRequestResetDisk %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
disk.SetStatus(self.UserCred, api.DISK_READY, "")
|
||||
self.TaskCompleted(ctx, disk, nil)
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ func (self *GuestCreateBackupDisksTask) CreateBackups(ctx context.Context, guest
|
||||
if int(diskIndex) == len(guestDisks) {
|
||||
self.SetStageComplete(ctx, nil)
|
||||
} else {
|
||||
err := guestDisks[diskIndex].GetDisk().StratCreateBackupTask(ctx, self.UserCred, self.GetTaskId())
|
||||
err := guestDisks[diskIndex].GetDisk().StartCreateBackupTask(ctx, self.UserCred, self.GetTaskId())
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, err.Error())
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -25,23 +23,87 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type SnapshotCreateTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
type GuestDiskSnapshotTask struct {
|
||||
SGuestBaseTask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(SnapshotCreateTask{})
|
||||
taskman.RegisterTask(GuestDiskSnapshotTask{})
|
||||
taskman.RegisterTask(SnapshotDeleteTask{})
|
||||
taskman.RegisterTask(BatchSnapshotsDeleteTask{})
|
||||
}
|
||||
|
||||
func (self *SnapshotCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
snapshot := obj.(*models.SSnapshot)
|
||||
self.DoDiskSnapshot(ctx, snapshot)
|
||||
}
|
||||
|
||||
func (self *SnapshotCreateTask) TaskFailed(ctx context.Context, snapshot *models.SSnapshot, reason string) {
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.Status = api.SNAPSHOT_FAILED
|
||||
return nil
|
||||
})
|
||||
db.OpsLog.LogEvent(snapshot, db.ACT_SNAPSHOT_FAIL, reason, self.UserCred)
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
func (self *SnapshotCreateTask) TaskComplete(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.Status = api.SNAPSHOT_READY
|
||||
return nil
|
||||
})
|
||||
db.OpsLog.LogEvent(snapshot, db.ACT_SNAPSHOT_DONE, "", self.UserCred)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *SnapshotCreateTask) DoDiskSnapshot(ctx context.Context, snapshot *models.SSnapshot) {
|
||||
self.SetStage("OnCreateSnapshot", nil)
|
||||
if err := snapshot.GetRegionDriver().RequestCreateSnapshot(ctx, snapshot, self); err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotCreateTask) OnCreateSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
extSnapshotId, err := data.GetString("snapshot_id")
|
||||
if err == nil { // Managed snapshot
|
||||
_, err := db.Update(snapshot, func() error {
|
||||
snapshot.ExternalId = extSnapshotId
|
||||
snapshot.Status = api.SNAPSHOT_READY
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, err = db.Update(snapshot, func() error {
|
||||
snapshot.Status = api.SNAPSHOT_READY
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
|
||||
func (self *SnapshotCreateTask) OnCreateSnapshotFailed(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
self.TaskFailed(ctx, snapshot, data.String())
|
||||
}
|
||||
|
||||
// =================================================================================================================
|
||||
|
||||
func (self *GuestDiskSnapshotTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
guest := obj.(*models.SGuest)
|
||||
guest.SetStatus(self.UserCred, api.VM_START_SNAPSHOT, "StartDiskSnapshot")
|
||||
self.DoDiskSnapshot(ctx, guest)
|
||||
}
|
||||
|
||||
@@ -70,25 +132,17 @@ func (self *GuestDiskSnapshotTask) OnDiskSnapshotComplete(ctx context.Context, g
|
||||
snapshotId, _ := self.Params.GetString("snapshot_id")
|
||||
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
|
||||
snapshot := iSnapshot.(*models.SSnapshot)
|
||||
if guest.Hypervisor == api.HYPERVISOR_KVM {
|
||||
location, err := res.GetString("location")
|
||||
if err != nil {
|
||||
log.Infof("OnDiskSnapshotComplete called with data no location")
|
||||
return
|
||||
}
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.Location = location
|
||||
snapshot.Status = api.SNAPSHOT_READY
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
extSnapshotId, _ := data.GetString("snapshot_id")
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.ExternalId = extSnapshotId
|
||||
snapshot.Status = api.SNAPSHOT_READY
|
||||
return nil
|
||||
})
|
||||
location, err := res.GetString("location")
|
||||
if err != nil {
|
||||
log.Infof("OnDiskSnapshotComplete called with data no location")
|
||||
return
|
||||
}
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.Location = location
|
||||
snapshot.Status = api.SNAPSHOT_READY
|
||||
return nil
|
||||
})
|
||||
|
||||
guest.SetStatus(self.UserCred, api.VM_SNAPSHOT_SUCC, "")
|
||||
self.TaskComplete(ctx, guest, nil)
|
||||
}
|
||||
@@ -97,268 +151,14 @@ func (self *GuestDiskSnapshotTask) OnDiskSnapshotCompleteFailed(ctx context.Cont
|
||||
self.TaskFailed(ctx, guest, err.String())
|
||||
}
|
||||
|
||||
func (self *GuestDiskSnapshotTask) OnAutoDeleteSnapshot(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
|
||||
self.TaskComplete(ctx, guest, data)
|
||||
}
|
||||
|
||||
func (self *GuestDiskSnapshotTask) OnAutoDeleteSnapshotFailed(ctx context.Context, guest *models.SGuest, err jsonutils.JSONObject) {
|
||||
log.Errorf("Auto Delete Snapshot Failed %s", err.String())
|
||||
self.TaskComplete(ctx, guest, err)
|
||||
}
|
||||
|
||||
func (self *GuestDiskSnapshotTask) TaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
|
||||
snapshotId, _ := self.Params.GetString("snapshot_id")
|
||||
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
|
||||
db.OpsLog.LogEvent(iSnapshot, db.ACT_SNAPSHOT_DONE, iSnapshot.GetShortDesc(ctx), self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_DISK_CREATE_SNAPSHOT, nil, self.UserCred, true)
|
||||
guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId())
|
||||
self.SetStage("OnSyncStatus", nil)
|
||||
}
|
||||
|
||||
func (self *GuestDiskSnapshotTask) OnSyncStatus(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
|
||||
self.SetStageComplete(ctx, nil)
|
||||
self.TaskComplete(ctx, guest, nil)
|
||||
guest.StartSyncstatus(ctx, self.UserCred, "")
|
||||
}
|
||||
|
||||
func (self *GuestDiskSnapshotTask) TaskFailed(ctx context.Context, guest *models.SGuest, reason string) {
|
||||
snapshotId, _ := self.Params.GetString("snapshot_id")
|
||||
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
|
||||
snapshot := iSnapshot.(*models.SSnapshot)
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.Status = api.SNAPSHOT_FAILED
|
||||
return nil
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
guest.SetStatus(self.UserCred, api.VM_SNAPSHOT_FAILED, reason)
|
||||
db.OpsLog.LogEvent(iSnapshot, db.ACT_SNAPSHOT_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_DISK_CREATE_SNAPSHOT, reason, self.UserCred, false)
|
||||
}
|
||||
|
||||
/***************************** Snapshot Delete Task *****************************/
|
||||
|
||||
type SnapshotDeleteTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
snapshot := obj.(*models.SSnapshot)
|
||||
if len(snapshot.ExternalId) > 0 {
|
||||
err := self.deleteExternalSnapshot(ctx, snapshot)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
} else {
|
||||
snapshot.RealDelete(ctx, self.GetUserCred())
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
return
|
||||
} else {
|
||||
// if snapshot is not used
|
||||
self.DeleteStaticSnapshot(ctx, snapshot)
|
||||
return
|
||||
}
|
||||
}
|
||||
if jsonutils.QueryBoolean(self.Params, "reload_disk", false) && snapshot.OutOfChain {
|
||||
self.StartReloadDisk(ctx, snapshot, guest)
|
||||
} else {
|
||||
self.StartDeleteSnapshot(ctx, snapshot, guest)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) deleteExternalSnapshot(ctx context.Context, snapshot *models.SSnapshot) error {
|
||||
cloudRegion, err := snapshot.GetISnapshotRegion()
|
||||
if err != nil {
|
||||
log.Errorln(err, cloudRegion, snapshot.CloudregionId)
|
||||
return err
|
||||
}
|
||||
cloudSnapshot, err := cloudRegion.GetISnapshotById(snapshot.ExternalId)
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
return nil
|
||||
}
|
||||
log.Errorln(err, cloudSnapshot)
|
||||
return err
|
||||
}
|
||||
if err := cloudSnapshot.Delete(); err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitDeleted(cloudSnapshot, 10*time.Second, 300*time.Second)
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) StartReloadDisk(ctx context.Context, snapshot *models.SSnapshot, guest *models.SGuest) {
|
||||
self.SetStage("OnReloadDiskSnapshot", nil)
|
||||
guest.SetStatus(self.UserCred, api.VM_SNAPSHOT, "Start Reload Snapshot")
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
err := guest.GetDriver().RequestReloadDiskSnapshot(ctx, guest, self, params)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) StartDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, guest *models.SGuest) {
|
||||
snapshot.SetStatus(self.UserCred, api.SNAPSHOT_DELETING, "On SnapshotDeleteTask StartDeleteSnapshot")
|
||||
params := jsonutils.NewDict()
|
||||
convertSnapshot, err := models.SnapshotManager.GetConvertSnapshot(snapshot)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
return
|
||||
}
|
||||
if convertSnapshot == nil {
|
||||
self.TaskFailed(ctx, snapshot, "snapshot dose not have convert snapshot")
|
||||
return
|
||||
}
|
||||
params.Set("delete_snapshot", jsonutils.NewString(snapshot.Id))
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
if !snapshot.OutOfChain {
|
||||
params.Set("convert_snapshot", jsonutils.NewString(convertSnapshot.Id))
|
||||
var FakeDelete = jsonutils.JSONFalse
|
||||
if snapshot.CreatedBy == api.SNAPSHOT_MANUAL && snapshot.FakeDeleted == false {
|
||||
FakeDelete = jsonutils.JSONTrue
|
||||
}
|
||||
params.Set("pending_delete", FakeDelete)
|
||||
} else {
|
||||
params.Set("auto_deleted", jsonutils.JSONTrue)
|
||||
}
|
||||
guest.SetStatus(self.UserCred, api.VM_SNAPSHOT_DELETE, "Start Delete Snapshot")
|
||||
|
||||
self.SetStage("OnDeleteSnapshot", nil)
|
||||
err = guest.GetDriver().RequestDeleteSnapshot(ctx, guest, self, params)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) DeleteStaticSnapshot(ctx context.Context, snapshot *models.SSnapshot) {
|
||||
err := snapshot.FakeDelete(self.UserCred)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
return
|
||||
}
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
if len(snapshot.ExternalId) == 0 {
|
||||
if !jsonutils.QueryBoolean(data, "deleted", false) {
|
||||
log.Infof("OnDeleteSnapshot with no deleted")
|
||||
return
|
||||
}
|
||||
snapshot.SetStatus(self.UserCred, api.SNAPSHOT_READY, "OnDeleteSnapshot")
|
||||
if snapshot.OutOfChain {
|
||||
snapshot.RealDelete(ctx, self.UserCred)
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
} else {
|
||||
var FakeDelete = false
|
||||
if snapshot.CreatedBy == api.SNAPSHOT_MANUAL && snapshot.FakeDeleted == false {
|
||||
FakeDelete = true
|
||||
}
|
||||
if FakeDelete {
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.OutOfChain = true
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
snapshot.RealDelete(ctx, self.UserCred)
|
||||
}
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
} else {
|
||||
snapshot.SetStatus(self.UserCred, api.SNAPSHOT_READY, "OnDeleteSnapshot")
|
||||
snapshot.RealDelete(ctx, self.UserCred)
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnDeleteSnapshotFailed(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
self.TaskFailed(ctx, snapshot, data.String())
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnReloadDiskSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
if !jsonutils.QueryBoolean(data, "reopen", false) {
|
||||
log.Infof("OnReloadDiskSnapshot with no reopen")
|
||||
return
|
||||
}
|
||||
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
return
|
||||
}
|
||||
if snapshot.FakeDeleted {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("delete_snapshot", jsonutils.NewString(snapshot.Id))
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
params.Set("auto_deleted", jsonutils.JSONTrue)
|
||||
self.SetStage("OnDeleteSnapshot", nil)
|
||||
err = guest.GetDriver().RequestDeleteSnapshot(ctx, guest, self, params)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
}
|
||||
} else {
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) TaskComplete(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
db.OpsLog.LogEvent(snapshot, db.ACT_SNAPSHOT_DELETE, snapshot.GetShortDesc(ctx), self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, snapshot, logclient.ACT_DELOCATE, nil, self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
log.Errorln(err.Error())
|
||||
return
|
||||
}
|
||||
guest.StartSyncstatus(ctx, self.UserCred, "")
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) TaskFailed(ctx context.Context, snapshot *models.SSnapshot, reason string) {
|
||||
if snapshot.Status == api.SNAPSHOT_DELETING {
|
||||
snapshot.SetStatus(self.UserCred, api.SNAPSHOT_READY, "On SnapshotDeleteTask TaskFailed")
|
||||
}
|
||||
db.OpsLog.LogEvent(snapshot, db.ACT_SNAPSHOT_DELETE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, snapshot, logclient.ACT_DELOCATE, reason, self.UserCred, false)
|
||||
self.SetStageFailed(ctx, reason)
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
log.Errorln(err.Error())
|
||||
return
|
||||
}
|
||||
guest.StartSyncstatus(ctx, self.UserCred, "")
|
||||
}
|
||||
|
||||
/***************************** Batch Snapshots Delete Task *****************************/
|
||||
|
||||
type BatchSnapshotsDeleteTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func (self *BatchSnapshotsDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
snapshot := obj.(*models.SSnapshot)
|
||||
self.StartStorageDeleteSnapshot(ctx, snapshot)
|
||||
}
|
||||
|
||||
func (self *BatchSnapshotsDeleteTask) StartStorageDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot) {
|
||||
host := snapshot.GetHost()
|
||||
if host == nil {
|
||||
self.SetStageFailed(ctx, "Cannot found snapshot host")
|
||||
return
|
||||
}
|
||||
self.SetStage("OnStorageDeleteSnapshot", nil)
|
||||
err := host.GetHostDriver().RequestDeleteSnapshotsWithStorage(ctx, host, snapshot, self)
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BatchSnapshotsDeleteTask) OnStorageDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
snapshots := models.SnapshotManager.GetDiskSnapshots(snapshot.DiskId)
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
snapshots[i].RealDelete(ctx, self.UserCred)
|
||||
}
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(SnapshotDeleteTask{})
|
||||
taskman.RegisterTask(BatchSnapshotsDeleteTask{})
|
||||
}
|
||||
|
||||
/***************************** Snapshot Delete Task *****************************/
|
||||
|
||||
type SnapshotDeleteTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnRequestSnapshotFailed(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
self.TaskFailed(ctx, snapshot, data.String())
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnRequestSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
if len(snapshot.ExternalId) > 0 {
|
||||
snapshot.RealDelete(ctx, self.GetUserCred())
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
} else {
|
||||
snapshot.SetStatus(self.UserCred, api.SNAPSHOT_READY, "")
|
||||
if jsonutils.QueryBoolean(self.Params, "reload_disk", false) && snapshot.OutOfChain {
|
||||
self.SetStage("OnReloadDiskSnapshot", nil)
|
||||
self.OnReloadDiskSnapshot(ctx, snapshot, data)
|
||||
} else {
|
||||
self.SetStage("OnDeleteSnapshot", nil)
|
||||
self.OnDeleteSnapshot(ctx, snapshot, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
snapshot := obj.(*models.SSnapshot)
|
||||
regionDriver := snapshot.GetRegionDriver()
|
||||
|
||||
self.SetStage("OnRequestSnapshot", nil)
|
||||
if err := regionDriver.RequestDeleteSnapshot(ctx, snapshot, self); err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
if !jsonutils.QueryBoolean(data, "deleted", false) {
|
||||
log.Infof("OnDeleteSnapshot with no deleted")
|
||||
return
|
||||
}
|
||||
snapshot.SetStatus(self.UserCred, api.SNAPSHOT_READY, "OnDeleteSnapshot")
|
||||
if snapshot.OutOfChain {
|
||||
snapshot.RealDelete(ctx, self.UserCred)
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
} else {
|
||||
var FakeDelete = false
|
||||
if snapshot.CreatedBy == api.SNAPSHOT_MANUAL && snapshot.FakeDeleted == false {
|
||||
FakeDelete = true
|
||||
}
|
||||
if FakeDelete {
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.OutOfChain = true
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
snapshot.RealDelete(ctx, self.UserCred)
|
||||
}
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnDeleteSnapshotFailed(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
self.TaskFailed(ctx, snapshot, data.String())
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnReloadDiskSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
if !jsonutils.QueryBoolean(data, "reopen", false) {
|
||||
log.Infof("OnReloadDiskSnapshot with no reopen")
|
||||
return
|
||||
}
|
||||
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
return
|
||||
}
|
||||
if snapshot.FakeDeleted {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("delete_snapshot", jsonutils.NewString(snapshot.Id))
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
params.Set("auto_deleted", jsonutils.JSONTrue)
|
||||
self.SetStage("OnDeleteSnapshot", nil)
|
||||
err = guest.GetDriver().RequestDeleteSnapshot(ctx, guest, self, params)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, snapshot, err.Error())
|
||||
}
|
||||
} else {
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) TaskComplete(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
db.OpsLog.LogEvent(snapshot, db.ACT_SNAPSHOT_DELETE, snapshot.GetShortDesc(ctx), self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, snapshot, logclient.ACT_DELOCATE, nil, self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
log.Errorln(err.Error())
|
||||
return
|
||||
}
|
||||
guest.StartSyncstatus(ctx, self.UserCred, "")
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) TaskFailed(ctx context.Context, snapshot *models.SSnapshot, reason string) {
|
||||
if snapshot.Status == api.SNAPSHOT_DELETING {
|
||||
snapshot.SetStatus(self.UserCred, api.SNAPSHOT_READY, "On SnapshotDeleteTask TaskFailed")
|
||||
}
|
||||
db.OpsLog.LogEvent(snapshot, db.ACT_SNAPSHOT_DELETE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, snapshot, logclient.ACT_DELOCATE, reason, self.UserCred, false)
|
||||
self.SetStageFailed(ctx, reason)
|
||||
guest, err := snapshot.GetGuest()
|
||||
if err != nil {
|
||||
log.Errorln(err.Error())
|
||||
return
|
||||
}
|
||||
guest.StartSyncstatus(ctx, self.UserCred, "")
|
||||
}
|
||||
|
||||
/***************************** Batch Snapshots Delete Task *****************************/
|
||||
|
||||
type BatchSnapshotsDeleteTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func (self *BatchSnapshotsDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
snapshot := obj.(*models.SSnapshot)
|
||||
self.StartStorageDeleteSnapshot(ctx, snapshot)
|
||||
}
|
||||
|
||||
func (self *BatchSnapshotsDeleteTask) StartStorageDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot) {
|
||||
host := snapshot.GetHost()
|
||||
if host == nil {
|
||||
self.SetStageFailed(ctx, "Cannot found snapshot host")
|
||||
return
|
||||
}
|
||||
self.SetStage("OnStorageDeleteSnapshot", nil)
|
||||
err := host.GetHostDriver().RequestDeleteSnapshotsWithStorage(ctx, host, snapshot, self)
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BatchSnapshotsDeleteTask) OnStorageDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
snapshots := models.SnapshotManager.GetDiskSnapshots(snapshot.DiskId)
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
snapshots[i].RealDelete(ctx, self.UserCred)
|
||||
}
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
@@ -38,6 +38,8 @@ type IDisk interface {
|
||||
GetSnapshotLocation() string
|
||||
|
||||
DeleteAllSnapshot() error
|
||||
DiskSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
|
||||
DiskDeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
|
||||
Delete(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
|
||||
Resize(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
|
||||
PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
|
||||
@@ -48,6 +50,7 @@ type IDisk interface {
|
||||
CreateFromUrl(ctx context.Context, url string, size int64) error
|
||||
CreateFromTemplate(context.Context, string, string, int64) (jsonutils.JSONObject, error)
|
||||
CreateFromSnapshotLocation(ctx context.Context, location string, size int64) error
|
||||
CreateFromRbdSnapshot(ctx context.Context, snapshotId, srcDiskId, srcPool string) error
|
||||
CreateFromImageFuse(ctx context.Context, url string, size int64) error
|
||||
CreateRaw(ctx context.Context, sizeMb int, diskFromat string, fsFormat string,
|
||||
encryption bool, diskId string, back string) (jsonutils.JSONObject, error)
|
||||
@@ -153,3 +156,15 @@ func (d *SBaseDisk) FormatFs(fsFormat, uuid, diskPath string) {
|
||||
log.Errorf("Format fs error : %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) DiskSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
return nil, fmt.Errorf("Not implement disk.DiskSnapshot")
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) DiskDeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
return nil, fmt.Errorf("Not implement disk.DiskDeleteSnapshot")
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) CreateFromRbdSnapshot(ctx context.Context, napshotUrl, srcDiskId, srcPool string) error {
|
||||
return fmt.Errorf("Not implement disk.CreateFromRbdSnapshot")
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/remotefile"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/fuseutils"
|
||||
@@ -421,6 +422,11 @@ func (d *SLocalDisk) ResetFromSnapshot(ctx context.Context, params interface{})
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
|
||||
outOfChain, err := resetParams.Input.Bool("out_of_chain")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("out_of_chain")
|
||||
}
|
||||
|
||||
snapshotDir := d.GetSnapshotDir()
|
||||
snapshotPath := path.Join(snapshotDir, resetParams.SnapshotId)
|
||||
diskTmpPath := d.GetPath() + "_reset.tmp"
|
||||
@@ -428,7 +434,7 @@ func (d *SLocalDisk) ResetFromSnapshot(ctx context.Context, params interface{})
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
if !resetParams.OutOfChain {
|
||||
if !outOfChain {
|
||||
img, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
@@ -447,7 +453,7 @@ func (d *SLocalDisk) ResetFromSnapshot(ctx context.Context, params interface{})
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err := procutils.NewCommand("rm", "-f", diskTmpPath).Run()
|
||||
_, err = procutils.NewCommand("rm", "-f", diskTmpPath).Run()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -133,10 +133,6 @@ func (d *SRBDDisk) PrepareSaveToGlance(ctx context.Context, params interface{})
|
||||
return jsonutils.Marshal(map[string]string{"backup": imageName}), nil
|
||||
}
|
||||
|
||||
func (d *SRBDDisk) ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
return nil, fmt.Errorf("Not impl")
|
||||
}
|
||||
|
||||
func (d *SRBDDisk) CleanupSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
storage := d.Storage.(*SRbdStorage)
|
||||
pool, _ := storage.StorageConf.GetString("pool")
|
||||
@@ -215,3 +211,42 @@ func (d *SRBDDisk) DeleteSnapshot(snapshotId, convertSnapshot string, pendingDel
|
||||
pool, _ := storage.StorageConf.GetString("pool")
|
||||
return storage.deleteSnapshot(pool, d.Id, snapshotId)
|
||||
}
|
||||
|
||||
func (d *SRBDDisk) DiskSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
snapshotId, ok := params.(string)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
return nil, d.CreateSnapshot(snapshotId)
|
||||
}
|
||||
|
||||
func (d *SRBDDisk) DiskDeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
snapshotId, ok := params.(string)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
err := d.DeleteSnapshot(snapshotId, "", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
res := jsonutils.NewDict()
|
||||
res.Set("deleted", jsonutils.JSONTrue)
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SRBDDisk) ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
resetParams, ok := params.(*SDiskReset)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
storage := d.Storage.(*SRbdStorage)
|
||||
pool, _ := storage.StorageConf.GetString("pool")
|
||||
return nil, storage.resetDisk(pool, d.GetId(), resetParams.SnapshotId)
|
||||
}
|
||||
|
||||
func (d *SRBDDisk) CreateFromRbdSnapshot(ctx context.Context, snapshot, srcDiskId, srcPool string) error {
|
||||
storage := d.Storage.(*SRbdStorage)
|
||||
pool, _ := storage.StorageConf.GetString("pool")
|
||||
return storage.cloneFromSnapshot(srcDiskId, srcPool, snapshot, d.GetId(), pool)
|
||||
}
|
||||
|
||||
@@ -34,12 +34,13 @@ import (
|
||||
var (
|
||||
keyWords = []string{"disks"}
|
||||
actionFuncs = map[string]actionFunc{
|
||||
"create": diskCreate,
|
||||
"delete": diskDelete,
|
||||
"resize": diskResize,
|
||||
"save-prepare": diskSavePrepare,
|
||||
"reset": diskReset,
|
||||
// "snapshot": diskSnapshot,
|
||||
"create": diskCreate,
|
||||
"delete": diskDelete,
|
||||
"resize": diskResize,
|
||||
"save-prepare": diskSavePrepare,
|
||||
"reset": diskReset,
|
||||
"snapshot": diskSnapshot,
|
||||
"delete-snapshot": diskDeleteSnapshot,
|
||||
"cleanup-snapshots": diskCleanupSnapshots,
|
||||
}
|
||||
)
|
||||
@@ -216,22 +217,27 @@ func diskReset(ctx context.Context, storage storageman.IStorage, diskId string,
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("snapshot_id")
|
||||
}
|
||||
outOfChain, err := body.Bool("out_of_chain")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("out_of_chain")
|
||||
}
|
||||
hostutils.DelayTask(ctx, disk.ResetFromSnapshot, &storageman.SDiskReset{snapshotId, outOfChain})
|
||||
hostutils.DelayTask(ctx, disk.ResetFromSnapshot, &storageman.SDiskReset{snapshotId, body})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// func diskSnapshot(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) {
|
||||
// snapshotId, err := body.GetString("snapshot_id")
|
||||
// if err != nil {
|
||||
// return nil, httperrors.NewMissingParameterError("snapshot_id")
|
||||
// }
|
||||
// hostutils.DelayTask(ctx, disk.CreateSnapshot(snapshotId), snapshotId)
|
||||
// return nil, nil
|
||||
// }
|
||||
func diskSnapshot(ctx context.Context, storage storageman.IStorage, diskId string, disk storageman.IDisk, body jsonutils.JSONObject) (interface{}, error) {
|
||||
snapshotId, err := body.GetString("snapshot_id")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("snapshot_id")
|
||||
}
|
||||
hostutils.DelayTask(ctx, disk.DiskSnapshot, snapshotId)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func diskDeleteSnapshot(ctx context.Context, storage storageman.IStorage, diskId string, disk storageman.IDisk, body jsonutils.JSONObject) (interface{}, error) {
|
||||
snapshotId, err := body.GetString("snapshot_id")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("snapshot_id")
|
||||
}
|
||||
hostutils.DelayTask(ctx, disk.DiskDeleteSnapshot, snapshotId)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func diskCleanupSnapshots(ctx context.Context, storage storageman.IStorage, diskId string, disk storageman.IDisk, body jsonutils.JSONObject) (interface{}, error) {
|
||||
convertSnapshots, err := body.GetArray("convert_snapshots")
|
||||
|
||||
@@ -106,7 +106,14 @@ func (r *SRbdImageCache) Remove(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (r *SRbdImageCache) GetDesc() *remotefile.SImageDesc {
|
||||
return nil
|
||||
imageCacheManger := r.Manager.(*SRbdImageCacheManager)
|
||||
storage := imageCacheManger.storage.(*SRbdStorage)
|
||||
|
||||
size := storage.getImageSizeMb(imageCacheManger.Pool, r.GetName())
|
||||
return &remotefile.SImageDesc{
|
||||
Size: int64(size),
|
||||
Name: r.GetName(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SRbdImageCache) GetImageId() string {
|
||||
|
||||
@@ -27,9 +27,7 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/cronman"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
@@ -104,6 +102,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
|
||||
|
||||
CreateSnapshotFormUrl(ctx context.Context, snapshotUrl, diskId, snapshotPath string) error
|
||||
|
||||
@@ -288,34 +287,14 @@ func (s *SBaseStorage) CreateDiskFromTemplate(ctx context.Context, disk IDisk, c
|
||||
}
|
||||
|
||||
func (s *SBaseStorage) CreateDiskFromSnpashot(ctx context.Context, disk IDisk, createParams *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
|
||||
var (
|
||||
// diskPath = path.Join(s.Path, createParams.DiskId)
|
||||
snapshotUrl, _ = createParams.DiskInfo.GetString("snapshot_url")
|
||||
transferProtocol, _ = createParams.DiskInfo.GetString("protocol")
|
||||
diskSize, _ = createParams.DiskInfo.Int("size")
|
||||
)
|
||||
|
||||
if len(snapshotUrl) == 0 || len(transferProtocol) == 0 {
|
||||
return nil, fmt.Errorf("Create disk from snapshot missing params snapshot url or protocol")
|
||||
var storage = createParams.Storage
|
||||
var snapshotUrl, _ = createParams.DiskInfo.GetString("snapshot_url")
|
||||
if len(snapshotUrl) == 0 {
|
||||
return nil, fmt.Errorf("Create disk from snapshot missing params snapshot url")
|
||||
}
|
||||
if createParams.Storage.StorageType() == compute.STORAGE_LOCAL {
|
||||
if transferProtocol == "url" {
|
||||
// not implement
|
||||
} else if transferProtocol == "fuse" {
|
||||
if err := disk.CreateFromImageFuse(ctx, snapshotUrl, diskSize); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unkown protocol %s", transferProtocol)
|
||||
}
|
||||
} else if utils.IsInStringArray(createParams.Storage.StorageType(), compute.SHARED_FILE_STORAGE) {
|
||||
if transferProtocol == "location" {
|
||||
if err := disk.CreateFromSnapshotLocation(ctx, snapshotUrl, diskSize); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unkown protocol %s", transferProtocol)
|
||||
}
|
||||
|
||||
if err := storage.CreateDiskFromSnapshot(ctx, disk, createParams); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return disk.GetDiskDesc(), nil
|
||||
|
||||
@@ -419,3 +419,21 @@ func doRebaseDisk(diskPath, newBasePath string) error {
|
||||
log.Infof("rebase disk %s backing file to %s ", diskPath, newBasePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SLocalStorage) CreateDiskFromSnapshot(
|
||||
ctx context.Context, disk IDisk, createParams *SDiskCreateByDiskinfo,
|
||||
) error {
|
||||
var (
|
||||
snapshotUrl, _ = createParams.DiskInfo.GetString("snapshot_url")
|
||||
transferProtocol, _ = createParams.DiskInfo.GetString("protocol")
|
||||
diskSize, _ = createParams.DiskInfo.Int("size")
|
||||
)
|
||||
if transferProtocol == "fuse" {
|
||||
if err := disk.CreateFromImageFuse(ctx, snapshotUrl, diskSize); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Unsupport protocol %s for Local storage", transferProtocol)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,3 +96,13 @@ func (s *SNasStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (s *SNasStorage) CreateDiskFromSnapshot(
|
||||
ctx context.Context, disk IDisk, createParams *SDiskCreateByDiskinfo,
|
||||
) error {
|
||||
var (
|
||||
snapshotUrl, _ = createParams.DiskInfo.GetString("snapshot_url")
|
||||
diskSize, _ = createParams.DiskInfo.Int("size")
|
||||
)
|
||||
return disk.CreateFromSnapshotLocation(ctx, snapshotUrl, diskSize)
|
||||
}
|
||||
|
||||
@@ -295,16 +295,36 @@ func (s *SRbdStorage) cloneImage(srcPool string, srcImage string, destPool strin
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) cloneFromSnapshot(srcImage, srcPool, srcSnapshot, newImage, pool string) error {
|
||||
_, err := s.withImage(srcPool, srcImage, func(src *rbd.Image) (interface{}, error) {
|
||||
snapshot := src.GetSnapshot(srcSnapshot)
|
||||
isProtect, err := snapshot.IsProtected()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "snapshot is protected")
|
||||
}
|
||||
if !isProtect {
|
||||
if err := snapshot.Protect(); err != nil {
|
||||
return nil, errors.Wrap(err, "snapshot protect")
|
||||
}
|
||||
defer snapshot.Unprotect()
|
||||
}
|
||||
return s.withIOContext(pool, func(ioctx *rados.IOContext) (interface{}, error) {
|
||||
_, err := src.Clone(srcSnapshot, ioctx, newImage, RBD_FEATURE, RBD_ORDER)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "clone from snapshot")
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "clone from snapshot")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) withImage(pool string, name string, doFunc func(*rbd.Image) (interface{}, error)) (interface{}, error) {
|
||||
return s.withIOContext(pool, func(ioctx *rados.IOContext) (interface{}, error) {
|
||||
names, err := rbd.GetImageNames(ioctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !utils.IsInStringArray(name, names) {
|
||||
return nil, ErrNoSuchImage
|
||||
}
|
||||
|
||||
image := rbd.GetImage(ioctx, name)
|
||||
if err := image.Open(); err != nil {
|
||||
log.Errorf("open image %s name error: %v", name, err)
|
||||
@@ -384,6 +404,14 @@ func (s *SRbdStorage) renameImage(pool string, src string, dest string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) resetDisk(pool string, diskId string, snapshotId string) error {
|
||||
_, err := s.withImage(pool, diskId, func(image *rbd.Image) (interface{}, error) {
|
||||
snap := image.GetSnapshot(snapshotId)
|
||||
return nil, snap.Rollback()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) createSnapshot(pool string, diskId string, snapshotId string) error {
|
||||
_, err := s.withImage(pool, diskId, func(image *rbd.Image) (interface{}, error) {
|
||||
return image.CreateSnapshot(snapshotId)
|
||||
@@ -393,16 +421,57 @@ func (s *SRbdStorage) createSnapshot(pool string, diskId string, snapshotId stri
|
||||
|
||||
func (s *SRbdStorage) deleteSnapshot(pool string, diskId string, snapshotId string) error {
|
||||
_, err := s.withImage(pool, diskId, func(image *rbd.Image) (interface{}, error) {
|
||||
snapshots, err := image.GetSnapshotNames()
|
||||
snapshot := image.GetSnapshot(snapshotId)
|
||||
isProtect, err := snapshot.IsProtected()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "snapshot is protected")
|
||||
}
|
||||
for _, snapshot := range snapshots {
|
||||
if len(snapshotId) == 0 || snapshot.Name == snapshotId {
|
||||
if err := image.GetSnapshot(snapshot.Name).Remove(); err != nil {
|
||||
return nil, err
|
||||
if isProtect {
|
||||
image.Close()
|
||||
if err := image.Open(snapshotId); err != nil {
|
||||
return nil, errors.Wrap(err, "image open snapshot")
|
||||
}
|
||||
pools, childImgs, err := image.ListChildren()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "image list children")
|
||||
}
|
||||
for i, iPool := range pools {
|
||||
_, err = s.withIOContext(iPool, func(ioctx *rados.IOContext) (interface{}, error) {
|
||||
_image := rbd.GetImage(ioctx, childImgs[i])
|
||||
err = _image.Open()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "open child image")
|
||||
}
|
||||
defer _image.Close()
|
||||
|
||||
log.Infof("start flatten %s/%s@%s => %s/%s", pool, diskId, snapshotId, iPool, childImgs[i])
|
||||
err := _image.Flatten()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "child image flatten")
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "flatten child %s/%s", iPool, childImgs[i])
|
||||
}
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
err = snapshot.Unprotect()
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
//Resource busy
|
||||
if strings.Contains(err.Error(), "16") {
|
||||
log.Warningf("snapshot is busy, try unprotect after %d seconds", (i+1)*5)
|
||||
time.Sleep(time.Second * time.Duration(i+1) * 5)
|
||||
continue
|
||||
}
|
||||
return nil, errors.Wrapf(err, "snapshot.Unprotect() %s", snapshotId)
|
||||
}
|
||||
}
|
||||
err = snapshot.Remove()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "snapshot remove")
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
@@ -602,10 +671,16 @@ func (s *SRbdStorage) CreateSnapshotFormUrl(ctx context.Context, snapshotUrl, di
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) DeleteSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
diskId, ok := params.(string)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
pool, _ := s.GetStorageConf().GetString("pool")
|
||||
return nil, s.deleteSnapshot(pool, diskId, "")
|
||||
return nil, fmt.Errorf("Not support delete snapshots")
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) CreateDiskFromSnapshot(
|
||||
ctx context.Context, disk IDisk, createParams *SDiskCreateByDiskinfo,
|
||||
) error {
|
||||
var (
|
||||
snapshotUrl, _ = createParams.DiskInfo.GetString("snapshot_url")
|
||||
srcDiskId, _ = createParams.DiskInfo.GetString("src_disk_id")
|
||||
srcPool, _ = createParams.DiskInfo.GetString("src_pool")
|
||||
)
|
||||
return disk.CreateFromRbdSnapshot(ctx, snapshotUrl, srcDiskId, srcPool)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func (i *SDiskCreateByDiskinfo) String() string {
|
||||
|
||||
type SDiskReset struct {
|
||||
SnapshotId string
|
||||
OutOfChain bool
|
||||
Input jsonutils.JSONObject
|
||||
}
|
||||
|
||||
type SDiskCleanupSnapshots struct {
|
||||
|
||||
@@ -115,11 +115,13 @@ func (img *SQemuImage) parse() error {
|
||||
cmd.Stdin = bytes.NewBuffer([]byte(img.Password))
|
||||
}
|
||||
var out bytes.Buffer
|
||||
var errOut bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &errOut
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Errorf("qemu-img info %s fail %s", img.Path, err)
|
||||
return fmt.Errorf("qemu-img info error %s", err)
|
||||
log.Errorf("qemu-img info %s fail %s: %s", img.Path, err, errOut.String())
|
||||
return fmt.Errorf("qemu-img info error %s", errOut.String())
|
||||
}
|
||||
for {
|
||||
line, err := out.ReadString('\n')
|
||||
|
||||
Reference in New Issue
Block a user