diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index 75bd0da58a..08d79a543d 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -578,7 +578,11 @@ func (snapshot *SSnapshot) purge(ctx context.Context, userCred mcclient.TokenCre lockman.LockObject(ctx, snapshot) defer lockman.ReleaseObject(ctx, snapshot) - err := snapshot.ValidatePurgeCondition(ctx) + if snapshot.Status == api.SNAPSHOT_DELETING { + snapshot.SetStatus(userCred, api.SNAPSHOT_READY, "for purge") + } + + err := snapshot.ValidateDeleteCondition(ctx, nil) if err != nil { return errors.Wrapf(err, "ValidatePurgeCondition for snapshot %s(%s)", snapshot.Name, snapshot.Id) } diff --git a/pkg/compute/models/snapshots.go b/pkg/compute/models/snapshots.go index 712d90f00f..15ba281ebb 100644 --- a/pkg/compute/models/snapshots.go +++ b/pkg/compute/models/snapshots.go @@ -24,6 +24,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/util/rbacscope" "yunion.io/x/pkg/util/timeutils" @@ -290,6 +291,13 @@ func (manager *SSnapshotManager) QueryDistinctExtraField(q *sqlchemy.SQuery, fie return q, httperrors.ErrNotFound } +type sSnapshotGuest struct { + DiskId string + GuestId string + GuestName string + GuestStatus string +} + func (manager *SSnapshotManager) FetchCustomizeColumns( ctx context.Context, userCred mcclient.TokenCredential, @@ -305,6 +313,9 @@ func (manager *SSnapshotManager) FetchCustomizeColumns( regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) encRows := manager.SEncryptedResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + storageIds := make([]string, len(objs)) + diskIds := make([]string, len(objs)) + snapshotIds := make([]string, len(objs)) for i := range rows { rows[i] = api.SnapshotDetails{ VirtualResourceDetails: virtRows[i], @@ -313,35 +324,90 @@ func (manager *SSnapshotManager) FetchCustomizeColumns( EncryptedResourceDetails: encRows[i], } - rows[i] = objs[i].(*SSnapshot).getMoreDetails(rows[i]) + snapshot := objs[i].(*SSnapshot) + storageIds[i] = snapshot.StorageId + diskIds[i] = snapshot.DiskId + snapshotIds[i] = snapshot.Id + } + + storages := map[string]SStorage{} + err := db.FetchModelObjectsByIds(StorageManager, "id", storageIds, storages) + if err != nil { + log.Errorf("FetchModelObjectsByIds") + return rows + } + disks := map[string]SDisk{} + err = db.FetchModelObjectsByIds(DiskManager, "id", diskIds, disks) + if err != nil { + log.Errorf("FetchModelObjectsByIds") + return rows + } + iss := []SInstanceSnapshotJoint{} + err = InstanceSnapshotJointManager.Query().In("snapshot_id", snapshotIds).All(&iss) + if err != nil { + log.Errorf("query instance snapshot joint") + return rows + } + issMap := map[string]bool{} + for i := range iss { + issMap[iss[i].SnapshotId] = true + } + q := GuestManager.Query() + gds := GuestdiskManager.Query().SubQuery() + sq := q.SubQuery() + guests := sq.Query( + sq.Field("id").Label("guest_id"), + sq.Field("name").Label("guest_name"), + sq.Field("status").Label("guest_status"), + gds.Field("disk_id"), + ).Join(gds, sqlchemy.Equals(gds.Field("guest_id"), sq.Field("id"))).Filter(sqlchemy.In(gds.Field("disk_id"), diskIds)) + guestdisks := []struct { + DiskId string + GuestId string + GuestName string + GuestStatus string + }{} + err = guests.All(&guestdisks) + if err != nil { + log.Errorf("guests.All") + return rows + } + guestMap := map[string]struct { + GuestId string + GuestName string + GuestStatus string + }{} + for _, gd := range guestdisks { + guestMap[gd.DiskId] = struct { + GuestId string + GuestName string + GuestStatus string + }{ + GuestId: gd.GuestId, + GuestName: gd.GuestName, + GuestStatus: gd.GuestStatus, + } + } + + for i := range rows { + if storage, ok := storages[storageIds[i]]; ok { + rows[i].StorageType = storage.StorageType + } + if disk, ok := disks[diskIds[i]]; ok { + rows[i].DiskStatus = disk.Status + rows[i].DiskName = disk.Name + } + if guest, ok := guestMap[diskIds[i]]; ok { + rows[i].GuestId = guest.GuestId + rows[i].Guest = guest.GuestName + rows[i].GuestStatus = guest.GuestStatus + } + rows[i].IsSubSnapshot, _ = issMap[snapshotIds[i]] } return rows } -func (self *SSnapshot) getMoreDetails(out api.SnapshotDetails) api.SnapshotDetails { - if IStorage, _ := StorageManager.FetchById(self.StorageId); IStorage != nil { - storage := IStorage.(*SStorage) - out.StorageType = storage.StorageType - } - disk, _ := self.GetDisk() - if disk != nil { - out.DiskStatus = disk.Status - out.DiskName = disk.Name - guests := disk.GetGuests() - if len(guests) == 1 { - out.Guest = guests[0].Name - out.GuestId = guests[0].Id - out.GuestStatus = guests[0].Status - } - } - if t, _ := InstanceSnapshotJointManager.IsSubSnapshot(self.Id); t { - out.IsSubSnapshot = true - } - - return out -} - func (self *SSnapshot) GetShortDesc(ctx context.Context) *jsonutils.JSONDict { res := self.SVirtualResourceBase.GetShortDesc(ctx) res.Add(jsonutils.NewInt(int64(self.Size)), "size") @@ -660,23 +726,28 @@ func (self *SSnapshot) StartSnapshotDeleteTask(ctx context.Context, userCred mcc return nil } -func (self *SSnapshot) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error { +func (self *SSnapshot) ValidateDeleteCondition(ctx context.Context, info *api.SnapshotDetails) error { if self.Status == api.SNAPSHOT_DELETING { return httperrors.NewBadRequestError("Cannot delete snapshot in status %s", self.Status) } - return self.ValidatePurgeCondition(ctx) -} - -func (self *SSnapshot) ValidatePurgeCondition(ctx context.Context) error { - count, err := InstanceSnapshotJointManager.Query().Equals("snapshot_id", self.Id).CountWithError() - if err != nil { - return httperrors.NewInternalServerError("Fetch instance snapshot error %s", err) - } - if count > 0 { - return httperrors.NewBadRequestError("snapshot referenced by instance snapshot") - } - if disk, err := self.GetDisk(); err == nil { - if disk.Status == api.DISK_RESET { + if gotypes.IsNil(info) { + count, err := InstanceSnapshotJointManager.Query().Equals("snapshot_id", self.Id).CountWithError() + if err != nil { + return httperrors.NewInternalServerError("Fetch instance snapshot error %s", err) + } + if count > 0 { + return httperrors.NewBadRequestError("snapshot referenced by instance snapshot") + } + if disk, err := self.GetDisk(); err == nil { + if disk.Status == api.DISK_RESET { + return httperrors.NewBadRequestError("Cannot delete snapshot on disk reset") + } + } + } else { + if info.IsSubSnapshot { + return httperrors.NewBadRequestError("snapshot referenced by instance snapshot") + } + if info.DiskStatus == api.DISK_RESET { return httperrors.NewBadRequestError("Cannot delete snapshot on disk reset") } }