- 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:
wanyaoqi
2019-08-08 18:30:46 +08:00
parent 345d2dd38f
commit e32c7bdb4e
41 changed files with 1039 additions and 632 deletions
-14
View File
@@ -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:"-"`
+3 -6
View File
@@ -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 {
+1
View File
@@ -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 {
+2
View File
@@ -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"
-4
View File
@@ -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
}
+5 -1
View File
@@ -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
View File
@@ -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 {
-72
View File
@@ -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,
-1
View File
@@ -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
+15
View File
@@ -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)
}
+10
View File
@@ -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
+45 -66
View File
@@ -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
}
+7
View File
@@ -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
+10
View File
@@ -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
}
+30
View File
@@ -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")
}
+6
View File
@@ -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())
}
+55
View File
@@ -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()
}
+2 -2
View File
@@ -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()
+112
View File
@@ -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)
}
-1
View File
@@ -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) {
}
+54
View File
@@ -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
}
}
+1
View File
@@ -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)
}
+8 -29
View File
@@ -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)
}
+1 -1
View File
@@ -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())
}
+79 -279
View File
@@ -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)
}
+170
View File
@@ -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)
}
+15
View File
@@ -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")
}
+8 -2
View File
@@ -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
}
+39 -4
View File
@@ -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")
+8 -1
View File
@@ -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 {
+8 -29
View File
@@ -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
+18
View File
@@ -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)
}
}
+10
View File
@@ -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)
}
+95 -20
View File
@@ -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)
}
+1 -1
View File
@@ -34,7 +34,7 @@ func (i *SDiskCreateByDiskinfo) String() string {
type SDiskReset struct {
SnapshotId string
OutOfChain bool
Input jsonutils.JSONObject
}
type SDiskCleanupSnapshots struct {
+4 -2
View File
@@ -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')