// Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package snapshot import ( "context" "database/sql" "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/logclient" ) func init() { taskman.RegisterTask(SnapshotDeleteTask{}) taskman.RegisterTask(BatchSnapshotsDeleteTask{}) taskman.RegisterTask(GuestDeleteSnapshotsTask{}) taskman.RegisterTask(DiskDeleteSnapshotsTask{}) } /***************************** 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) } func (self *SnapshotDeleteTask) OnRequestSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) { err := snapshot.GetRegionDriver().OnSnapshotDelete(ctx, snapshot, self, data) if err != nil { self.TaskFailed(ctx, snapshot, jsonutils.NewString(err.Error())) } } func (self *SnapshotDeleteTask) OnManagedSnapshotDelete(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) { snapshot.RealDelete(ctx, self.GetUserCred()) self.TaskComplete(ctx, snapshot, nil) } func (self *SnapshotDeleteTask) OnKvmSnapshotDelete(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) { snapshot.SetStatus(ctx, 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) err := regionDriver.RequestDeleteSnapshot(ctx, snapshot, self) if err != nil { if errors.Cause(err) == cloudprovider.ErrNotFound { self.ScheduleRun(jsonutils.Marshal(map[string]bool{"deleted": true})) return } self.TaskFailed(ctx, snapshot, jsonutils.NewString(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(ctx, 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) } 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, jsonutils.NewString(err.Error())) return } if snapshot.FakeDeleted { params := jsonutils.NewDict() disk, err := models.DiskManager.FetchById(snapshot.DiskId) if err != nil && err != sql.ErrNoRows { self.TaskFailed(ctx, snapshot, jsonutils.NewString(err.Error())) return } sDisk, _ := disk.(*models.SDisk) if sDisk.IsEncrypted() { if encryptInfo, err := sDisk.GetEncryptInfo(ctx, self.GetUserCred()); err != nil { self.TaskFailed(ctx, snapshot, jsonutils.NewString(err.Error())) return } else { params.Set("encrypt_info", jsonutils.Marshal(encryptInfo)) } } 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) drv, err := guest.GetDriver() if err != nil { self.TaskFailed(ctx, snapshot, jsonutils.NewString(err.Error())) return } err = drv.RequestDeleteSnapshot(ctx, guest, self, params) if err != nil { self.TaskFailed(ctx, snapshot, jsonutils.NewString(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) notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ Obj: snapshot, Action: notifyclient.ActionDelete, }) 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 jsonutils.JSONObject) { snapshot.SetStatus(ctx, self.UserCred, api.SNAPSHOT_DELETE_FAILED, reason.String()) 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, err := snapshot.GetHost() if err != nil { self.SetStageFailed(ctx, jsonutils.NewString(errors.Wrapf(err, "snapshot.GetHost").Error())) return } snapshotIds := []string{} err = self.Params.Unmarshal(&snapshotIds, "snapshot_ids") if err != nil { self.SetStageFailed(ctx, jsonutils.NewString(errors.Wrapf(err, "unmarshal snapshot ids").Error())) return } driver, err := host.GetHostDriver() if err != nil { self.SetStageFailed(ctx, jsonutils.NewString(errors.Wrapf(err, "GetHostDriver").Error())) return } self.SetStage("OnStorageDeleteSnapshot", nil) err = driver.RequestDeleteSnapshotsWithStorage(ctx, host, snapshot, self, snapshotIds) if err != nil { self.SetStageFailed(ctx, jsonutils.NewString(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) } type GuestDeleteSnapshotsTask struct { taskman.STask } func (self *GuestDeleteSnapshotsTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { guest := obj.(*models.SGuest) instanceSnapshots, _ := guest.GetInstanceSnapshots() self.StartDeleteInstanceSnapshots(ctx, guest, instanceSnapshots) } func (self *GuestDeleteSnapshotsTask) StartDeleteInstanceSnapshots( ctx context.Context, guest *models.SGuest, instanceSnapshots []models.SInstanceSnapshot) { if len(instanceSnapshots) > 0 { instanceSnapshot := instanceSnapshots[0] instanceSnapshots := instanceSnapshots[1:] self.Params.Set("instance_snapshots", jsonutils.Marshal(instanceSnapshots)) self.SetStage("OnInstanceSnapshotDelete", nil) instanceSnapshot.SetModelManager(models.InstanceSnapshotManager, &instanceSnapshot) instanceSnapshot.StartInstanceSnapshotDeleteTask(ctx, self.UserCred, self.Id) return } snapshots, _ := guest.GetDiskSnapshotsNotInInstanceSnapshots() self.StartDeleteDiskSnapshots(ctx, guest, snapshots) } func (self *GuestDeleteSnapshotsTask) OnInstanceSnapshotDelete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { instanceSnapshots := make([]models.SInstanceSnapshot, 0) self.Params.Unmarshal(&instanceSnapshots, "instance_snapshots") self.StartDeleteInstanceSnapshots(ctx, guest, instanceSnapshots) } func (self *GuestDeleteSnapshotsTask) OnInstanceSnapshotDeleteFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { log.Errorln(data.String()) instanceSnapshots := make([]models.SInstanceSnapshot, 0) self.Params.Unmarshal(&instanceSnapshots, "instance_snapshots") self.StartDeleteInstanceSnapshots(ctx, guest, instanceSnapshots) } func (self *GuestDeleteSnapshotsTask) StartDeleteDiskSnapshots( ctx context.Context, guest *models.SGuest, snapshots []models.SSnapshot) { if len(snapshots) > 0 { snapshot := snapshots[0] snapshots := snapshots[1:] self.Params.Set("snapshots", jsonutils.Marshal(snapshots)) self.SetStage("OnSnapshotDelete", nil) snapshot.SetModelManager(models.SnapshotManager, &snapshot) snapshot.StartSnapshotDeleteTask(ctx, self.UserCred, false, self.Id, 0, 0) return } self.SetStageComplete(ctx, nil) } func (self *GuestDeleteSnapshotsTask) OnSnapshotDelete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { snapshots := make([]models.SSnapshot, 0) self.Params.Unmarshal(&snapshots, "snapshots") self.StartDeleteDiskSnapshots(ctx, guest, snapshots) } func (self *GuestDeleteSnapshotsTask) OnSnapshotDeleteFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { log.Errorln(data.String()) snapshots := make([]models.SSnapshot, 0) self.Params.Unmarshal(&snapshots, "snapshots") self.StartDeleteDiskSnapshots(ctx, guest, snapshots) } type DiskDeleteSnapshotsTask struct { taskman.STask } func (self *DiskDeleteSnapshotsTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { disk := obj.(*models.SDisk) snapshots, _ := disk.GetSnapshotsNotInInstanceSnapshot() self.StartDeleteDiskSnapshots(ctx, disk, snapshots) } func (self *DiskDeleteSnapshotsTask) StartDeleteDiskSnapshots( ctx context.Context, disk *models.SDisk, snapshots []models.SSnapshot) { if len(snapshots) > 0 { snapshot := snapshots[0] snapshots := snapshots[1:] self.Params.Set("snapshots", jsonutils.Marshal(snapshots)) self.SetStage("OnSnapshotDelete", nil) snapshot.SetModelManager(models.SnapshotManager, &snapshot) snapshot.StartSnapshotDeleteTask(ctx, self.UserCred, false, self.Id, 0, 0) return } self.SetStageComplete(ctx, nil) } func (self *DiskDeleteSnapshotsTask) OnSnapshotDelete(ctx context.Context, disk *models.SDisk, data jsonutils.JSONObject) { snapshots := make([]models.SSnapshot, 0) self.Params.Unmarshal(&snapshots, "snapshots") self.StartDeleteDiskSnapshots(ctx, disk, snapshots) } func (self *DiskDeleteSnapshotsTask) OnSnapshotDeleteFailed(ctx context.Context, disk *models.SDisk, data jsonutils.JSONObject) { log.Errorf("Delete disk snapshots failed %s", data.String()) self.SetStageFailed(ctx, data) }