fix(region): snapshot policy with multi disk on same guest fix(region): delete overdued snapshots on cleanup snapshot task only (#21967)

This commit is contained in:
wanyaoqi
2025-01-16 10:43:12 +08:00
committed by GitHub
parent 8b02911971
commit bed9513cc4
5 changed files with 163 additions and 66 deletions
+59 -46
View File
@@ -2804,40 +2804,25 @@ func (manager *SDiskManager) AutoDiskSnapshot(ctx context.Context, userCred mccl
log.Errorf("Get auto snapshot disks id failed: %s", err)
return
}
log.Debugf("auto snapshot %d disks", len(disks))
now := time.Now()
log.Infof("auto snapshot %d disks", len(disks))
guestDps := map[string][]SSnapshotPolicyDisk{}
for i := 0; i < len(disks); i++ {
disk, err := disks[i].GetDisk()
if err != nil {
log.Errorf("get disk error: %v", err)
continue
}
err = func() error {
policy, err := disks[i].GetSnapshotPolicy()
if err != nil {
return errors.Wrapf(err, "GetSnapshotPolicy")
if guest := disk.GetGuest(); guest != nil {
if dps, ok := guestDps[guest.Id]; ok {
guestDps[guest.Id] = append(dps, disks[i])
} else {
guestDps[guest.Id] = []SSnapshotPolicyDisk{disks[i]}
}
continue
}
if len(disk.ExternalId) == 0 {
err = disk.validateDiskAutoCreateSnapshot()
if err != nil {
return errors.Wrapf(err, "validateDiskAutoCreateSnapshot")
}
}
snapshot, err := disk.CreateSnapshotAuto(ctx, userCred, policy)
if err != nil {
return errors.Wrapf(err, "CreateSnapshotAuto")
}
if err = disk.CleanOverduedSnapshots(ctx, userCred, policy, now); err != nil {
log.Errorf("failed clean overdued snapshots %s", err)
}
db.OpsLog.LogEvent(disk, db.ACT_DISK_AUTO_SNAPSHOT, snapshot.Name, userCred)
policy.ExecuteNotify(ctx, userCred, disk.GetName())
return nil
}()
err = manager.DoAutoSnapshot(ctx, userCred, &disks[i], disk, "")
if err != nil {
log.Errorf("auto snapshot %s error: %v", disk.Name, err)
db.OpsLog.LogEvent(disk, db.ACT_DISK_AUTO_SNAPSHOT_FAIL, err.Error(), userCred)
@@ -2845,11 +2830,56 @@ func (manager *SDiskManager) AutoDiskSnapshot(ctx context.Context, userCred mccl
}
}
for gid, diskSnapshotPolicies := range guestDps {
guest := GuestManager.FetchGuestById(gid)
err = manager.OrderCreateDisksSnapshotsBySnapshotPolicy(ctx, userCred, guest, diskSnapshotPolicies)
if err != nil {
log.Errorf("failed start OrderCreateDisksSnapshotsBySnapshotPolicy")
}
}
}
func (manager *SDiskManager) OrderCreateDisksSnapshotsBySnapshotPolicy(
ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, snapshotPolicyDisks []SSnapshotPolicyDisk,
) error {
params := jsonutils.NewDict()
params.Set("snapshot_policy_disks", jsonutils.Marshal(snapshotPolicyDisks))
task, err := taskman.TaskManager.NewTask(ctx, "GuestDisksSnapshotPolicyExecuteTask", guest, userCred, params, "", "", nil)
if err != nil {
return errors.Wrapf(err, "NewTask")
}
return task.ScheduleRun(nil)
}
func (manager *SDiskManager) DoAutoSnapshot(
ctx context.Context, userCred mcclient.TokenCredential,
diskSnapshotPolicy *SSnapshotPolicyDisk, disk *SDisk, parentTaskId string,
) error {
policy, err := diskSnapshotPolicy.GetSnapshotPolicy()
if err != nil {
return errors.Wrapf(err, "GetSnapshotPolicy")
}
if len(disk.ExternalId) == 0 {
err = disk.validateDiskAutoCreateSnapshot()
if err != nil {
return errors.Wrapf(err, "validateDiskAutoCreateSnapshot")
}
}
snapshot, err := disk.CreateSnapshotAuto(ctx, userCred, policy, parentTaskId)
if err != nil {
return errors.Wrapf(err, "CreateSnapshotAuto")
}
db.OpsLog.LogEvent(disk, db.ACT_DISK_AUTO_SNAPSHOT, snapshot.Name, userCred)
policy.ExecuteNotify(ctx, userCred, disk.GetName())
return nil
}
func (self *SDisk) CreateSnapshotAuto(
ctx context.Context, userCred mcclient.TokenCredential,
policy *SSnapshotPolicy,
ctx context.Context, userCred mcclient.TokenCredential, policy *SSnapshotPolicy, parentTaskId string,
) (*SSnapshot, error) {
storage, err := self.GetStorage()
if err != nil {
@@ -2892,30 +2922,13 @@ func (self *SDisk) CreateSnapshotAuto(
}
db.OpsLog.LogEvent(snapshot, db.ACT_CREATE, "disk create snapshot auto", userCred)
err = snapshot.StartSnapshotCreateTask(ctx, userCred, nil, "")
err = snapshot.StartSnapshotCreateTask(ctx, userCred, nil, parentTaskId)
if err != nil {
return nil, errors.Wrap(err, "disk auto snapshot start snapshot task")
}
return snapshot, nil
}
func (self *SDisk) CleanOverduedSnapshots(ctx context.Context, userCred mcclient.TokenCredential, sp *SSnapshotPolicy, now time.Time) error {
snapshot := new(SSnapshot)
err := SnapshotManager.Query().Equals("disk_id", self.Id).
Equals("created_by", api.SNAPSHOT_AUTO).Equals("fake_deleted", false).Asc("created_at").First(snapshot)
if err != nil {
return errors.Wrap(err, "get snapshot")
}
snapshot.SetModelManager(SnapshotManager, snapshot)
if snapshot.ExpiredAt.Before(now) {
err = snapshot.StartSnapshotDeleteTask(ctx, userCred, false, self.Id, 0, 0)
if err != nil {
return err
}
}
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)
+11
View File
@@ -18,6 +18,7 @@ import (
"context"
"database/sql"
"fmt"
"sync/atomic"
"time"
"yunion.io/x/cloudmux/pkg/cloudprovider"
@@ -1166,7 +1167,17 @@ func (manager *SSnapshotManager) GetResourceCount() ([]db.SScopeResourceCount, e
return db.CalculateResourceCount(virts, "tenant_id")
}
var SnapshotCleanupTaskRunning int32 = 0
func SnapshotCleanupTaskIsRunning() bool {
return atomic.LoadInt32(&SnapshotCleanupTaskRunning) == 1
}
func (manager *SSnapshotManager) CleanupSnapshots(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
if SnapshotCleanupTaskIsRunning() {
log.Errorf("Previous CleanupSnapshots tasks still running !!!")
return
}
var now = time.Now()
var snapshot = new(SSnapshot)
err := manager.Query().
@@ -18,6 +18,7 @@ import (
"context"
"database/sql"
"fmt"
"sync/atomic"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
@@ -40,10 +41,23 @@ type SnapshotCleanupTask struct {
taskman.STask
}
func (self *SnapshotCleanupTask) taskFailed(ctx context.Context, reason *jsonutils.JSONString) {
log.Infof("SnapshotCleanupTask failed %s", reason)
atomic.CompareAndSwapInt32(&models.SnapshotCleanupTaskRunning, 1, 0)
self.SetStageFailed(ctx, reason)
}
func (self *SnapshotCleanupTask) taskCompleted(ctx context.Context, data jsonutils.JSONObject) {
log.Infof("SnapshotCleanupTask completed %s", data)
atomic.CompareAndSwapInt32(&models.SnapshotCleanupTaskRunning, 1, 0)
self.SetStageComplete(ctx, nil)
}
func (self *SnapshotCleanupTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
atomic.CompareAndSwapInt32(&models.SnapshotCleanupTaskRunning, 0, 1)
now, err := self.Params.GetTime("tick")
if err != nil {
self.SetStageFailed(ctx, jsonutils.NewString("failed get tick"))
self.taskFailed(ctx, jsonutils.NewString("failed get tick"))
return
}
var snapshots = make([]models.SSnapshot, 0)
@@ -52,13 +66,17 @@ func (self *SnapshotCleanupTask) OnInit(ctx context.Context, obj db.IStandaloneM
Equals("created_by", compute.SNAPSHOT_AUTO).
LE("expired_at", now).All(&snapshots)
if err == sql.ErrNoRows {
self.SetStageComplete(ctx, nil)
self.taskCompleted(ctx, nil)
return
} else if err != nil {
self.SetStageFailed(ctx, jsonutils.NewString(fmt.Sprintf("failed get snapshot %s", err)))
self.taskFailed(ctx, jsonutils.NewString(fmt.Sprintf("failed get snapshot %s", err)))
return
}
self.StartSnapshotsDelete(ctx, snapshots)
snapshotIds := make([]string, len(snapshots))
for i := range snapshots {
snapshotIds[i] = snapshots[i].Id
}
self.StartSnapshotsDelete(ctx, snapshotIds)
}
func (self *SnapshotCleanupTask) OnInitFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
@@ -66,35 +84,47 @@ func (self *SnapshotCleanupTask) OnInitFailed(ctx context.Context, obj db.IStand
self.OnInit(ctx, obj, data)
}
func (self *SnapshotCleanupTask) StartSnapshotsDelete(ctx context.Context, snapshots []models.SSnapshot) {
snapshot := snapshots[0]
snapshots = snapshots[1:]
if len(snapshots) > 0 {
self.Params.Set("snapshots", jsonutils.Marshal(snapshots))
func (self *SnapshotCleanupTask) StartSnapshotsDelete(ctx context.Context, snapshotIds []string) {
snapshotId := snapshotIds[0]
snapshotIds = snapshotIds[1:]
if len(snapshotIds) > 0 {
self.Params.Set("snapshots", jsonutils.Marshal(snapshotIds))
}
self.SetStage("OnDeleteSnapshot", nil)
snapshot.SetModelManager(models.SnapshotManager, &snapshot)
err := snapshot.StartSnapshotDeleteTask(ctx, self.UserCred, false, self.GetId(), 0, 0)
iSnapshot, err := models.SnapshotManager.FetchById(snapshotId)
if err != nil {
self.OnDeleteSnapshotFailed(ctx, self.GetObject(), nil)
return
}
snapshot := iSnapshot.(*models.SSnapshot)
if snapshot.Status == compute.SNAPSHOT_DELETING {
self.OnDeleteSnapshot(ctx, snapshot, nil)
return
}
snapshot.SetModelManager(models.SnapshotManager, snapshot)
err = snapshot.StartSnapshotDeleteTask(ctx, self.UserCred, false, self.GetId(), 0, 0)
if err != nil {
self.OnDeleteSnapshotFailed(ctx, self.GetObject(), nil)
}
}
func (self *SnapshotCleanupTask) OnDeleteSnapshot(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
var snapshots = make([]models.SSnapshot, 0)
err := self.Params.Unmarshal(&snapshots, "snapshots")
var snapshotIds = make([]string, 0)
err := self.Params.Unmarshal(&snapshotIds, "snapshots")
if err != nil {
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
self.taskFailed(ctx, jsonutils.NewString(err.Error()))
return
}
if len(snapshots) > 0 {
self.StartSnapshotsDelete(ctx, snapshots)
if len(snapshotIds) > 0 {
self.StartSnapshotsDelete(ctx, snapshotIds)
} else {
self.SetStageComplete(ctx, nil)
self.taskCompleted(ctx, nil)
}
}
func (self *SnapshotCleanupTask) OnDeleteSnapshotFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
log.Errorf("snapshot delete faield %s", data)
self.SetStageFailed(ctx, data)
reason := fmt.Sprintf("snapshot delete faield %s", data)
self.taskFailed(ctx, jsonutils.NewString(reason))
}
@@ -94,7 +94,6 @@ func (self *GuestDiskSnapshotTask) OnDiskSnapshotComplete(ctx context.Context, g
return
}
guest.SetStatus(ctx, self.UserCred, api.VM_SNAPSHOT_SUCC, "")
self.TaskComplete(ctx, guest, nil)
}
+44
View File
@@ -18,6 +18,8 @@ import (
"context"
"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"
@@ -33,6 +35,7 @@ type SnapshotCreateTask struct {
func init() {
taskman.RegisterTask(SnapshotCreateTask{})
taskman.RegisterTask(GuestDisksSnapshotPolicyExecuteTask{})
}
func (self *SnapshotCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
@@ -72,3 +75,44 @@ func (self *SnapshotCreateTask) OnCreateSnapshot(ctx context.Context, snapshot *
func (self *SnapshotCreateTask) OnCreateSnapshotFailed(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
self.TaskFailed(ctx, snapshot, data)
}
type GuestDisksSnapshotPolicyExecuteTask struct {
taskman.STask
}
func (self *GuestDisksSnapshotPolicyExecuteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
self.OnDiskSnapshot(ctx, obj, data)
}
func (self *GuestDisksSnapshotPolicyExecuteTask) OnDiskSnapshot(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
snapshotPolicyDisks := make([]models.SSnapshotPolicyDisk, 0)
self.Params.Unmarshal(&snapshotPolicyDisks, "snapshot_policy_disks")
if len(snapshotPolicyDisks) == 0 {
self.SetStageComplete(ctx, nil)
return
}
snapshotPolicyDisk := snapshotPolicyDisks[0]
self.Params.Set("snapshot_policy_disks", jsonutils.Marshal(snapshotPolicyDisks[1:]))
self.SetStage("OnDiskSnapshot", nil)
disk, err := snapshotPolicyDisk.GetDisk()
if err != nil {
log.Errorf("disk snapshot policy %s failed get disk %s", snapshotPolicyDisk.SnapshotpolicyId, err)
self.OnDiskSnapshot(ctx, obj, data)
return
}
err = models.DiskManager.DoAutoSnapshot(ctx, self.UserCred, &snapshotPolicyDisk, disk, self.GetTaskId())
if err != nil {
log.Errorf("disk.CreateSnapshotAuto failed %s %s", disk.Id, err)
db.OpsLog.LogEvent(disk, db.ACT_DISK_AUTO_SNAPSHOT_FAIL, err.Error(), self.UserCred)
notifyclient.NotifySystemErrorWithCtx(ctx, disk.Id, disk.Name, db.ACT_DISK_AUTO_SNAPSHOT_FAIL, errors.Wrapf(err, "Disk auto create snapshot").Error())
self.OnDiskSnapshot(ctx, obj, data)
return
}
}
func (self *GuestDisksSnapshotPolicyExecuteTask) OnDiskSnapshotFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
log.Errorf("Guest create snapshot failed %s: %s", obj.GetId(), data)
self.OnDiskSnapshot(ctx, obj, data)
}