| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402 |
- // 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 models
- import (
- "context"
- "database/sql"
- "fmt"
- "sync/atomic"
- "time"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "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/utils"
- "yunion.io/x/sqlchemy"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "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/validators"
- "yunion.io/x/onecloud/pkg/compute/options"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- )
- type SSnapshotManager struct {
- db.SVirtualResourceBaseManager
- db.SExternalizedResourceBaseManager
- SManagedResourceBaseManager
- SCloudregionResourceBaseManager
- SDiskResourceBaseManager
- SStorageResourceBaseManager
- db.SMultiArchResourceBaseManager
- db.SEncryptedResourceManager
- }
- type SSnapshot struct {
- db.SVirtualResourceBase
- db.SExternalizedResourceBase
- SManagedResourceBase
- SCloudregionResourceBase `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
- db.SMultiArchResourceBase
- db.SEncryptedResource
- // 磁盘Id
- DiskId string `width:"36" charset:"ascii" nullable:"true" create:"required" list:"user" index:"true"`
- // 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:"user" create:"optional"`
- Location string `charset:"ascii" nullable:"true" list:"admin" create:"optional"`
- // 快照大小,单位Mb
- Size int `nullable:"false" list:"user" create:"optional"`
- // Virtual size, for kvm is origin disk size
- VirtualSize int `nullable:"false" list:"user" create:"optional"`
- 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"`
- // 操作系统类型
- OsType string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional"`
- // create disk from snapshot, snapshot as disk backing file
- RefCount int `nullable:"false" default:"0" list:"user"`
- BackingDiskId string `width:"36" charset:"ascii" nullable:"true" default:""`
- DiskBackupId string `width:"36" charset:"ascii" nullable:"true" default:""`
- ExpiredAt time.Time `nullable:"true" list:"user" create:"optional"`
- }
- var SnapshotManager *SSnapshotManager
- func init() {
- SnapshotManager = &SSnapshotManager{
- SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
- SSnapshot{},
- "snapshots_tbl",
- "snapshot",
- "snapshots",
- ),
- }
- SnapshotManager.SetVirtualObject(SnapshotManager)
- }
- // 快照列表
- func (manager *SSnapshotManager) ListItemFilter(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.SnapshotListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, query.RegionalFilterListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SMultiArchResourceBaseManager.ListItemFilter(ctx, q, userCred, query.MultiArchResourceBaseListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SMultiArchResourceBaseManager.ListItemFilter")
- }
- if query.FakeDeleted != nil && *query.FakeDeleted {
- q = q.IsTrue("fake_deleted")
- } else {
- q = q.IsFalse("fake_deleted")
- }
- if query.Local != nil && *query.Local {
- storages := StorageManager.Query().SubQuery()
- sq := storages.Query(storages.Field("id")).Filter(sqlchemy.Equals(storages.Field("storage_type"), api.STORAGE_LOCAL))
- q = q.Filter(sqlchemy.In(q.Field("storage_id"), sq))
- }
- // Public cloud snapshot doesn't have storage id
- if query.Share != nil && *query.Share {
- storages := StorageManager.Query().SubQuery()
- sq := storages.Query(storages.Field("id")).NotEquals("storage_type", "local")
- q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("storage_id")),
- sqlchemy.In(q.Field("storage_id"), sq)))
- }
- if len(query.DiskType) > 0 {
- q = q.Equals("disk_type", query.DiskType)
- }
- if query.IsInstanceSnapshot != nil {
- insjsq := InstanceSnapshotJointManager.Query().SubQuery()
- if !*query.IsInstanceSnapshot {
- q = q.LeftJoin(insjsq, sqlchemy.Equals(q.Field("id"), insjsq.Field("snapshot_id"))).
- Filter(sqlchemy.IsNull(insjsq.Field("snapshot_id")))
- } else {
- q = q.Join(insjsq, sqlchemy.Equals(q.Field("id"), insjsq.Field("snapshot_id")))
- }
- }
- diskInput := api.DiskFilterListInput{
- DiskFilterListInputBase: query.DiskFilterListInputBase,
- }
- q, err = manager.SDiskResourceBaseManager.ListItemFilter(ctx, q, userCred, diskInput)
- if err != nil {
- return nil, errors.Wrap(err, "SDiskResourceBaseManager.ListItemFilter")
- }
- storageInput := api.StorageFilterListInput{
- StorageFilterListInputBase: query.StorageFilterListInputBase,
- }
- q, err = manager.SStorageResourceBaseManager.ListItemFilter(ctx, q, userCred, storageInput)
- if err != nil {
- return nil, errors.Wrap(err, "SStorageResourceBaseManager.ListItemFilter")
- }
- if query.OutOfChain != nil {
- if *query.OutOfChain {
- q = q.IsTrue("out_of_chain")
- } else {
- q = q.IsFalse("out_of_chain")
- }
- }
- if len(query.OsType) > 0 {
- q = q.In("os_type", query.OsType)
- }
- if len(query.ServerId) > 0 {
- iG, err := GuestManager.FetchByIdOrName(ctx, userCred, query.ServerId)
- if err != nil && err == sql.ErrNoRows {
- return nil, httperrors.NewNotFoundError("guest %s not found", query.ServerId)
- } else if err != nil {
- return nil, errors.Wrap(err, "fetch guest")
- }
- guest := iG.(*SGuest)
- gdq := GuestdiskManager.Query("disk_id").Equals("guest_id", guest.Id).SubQuery()
- q = q.In("disk_id", gdq)
- }
- if query.Unused {
- sq := DiskManager.Query("id").Distinct().SubQuery()
- q = q.NotIn("disk_id", sq)
- }
- if len(query.StorageId) > 0 {
- q = q.Equals("storage_id", query.StorageId)
- }
- return q, nil
- }
- func (manager *SSnapshotManager) OrderByExtraFields(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.SnapshotListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
- }
- q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
- }
- q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
- }
- diskInput := api.DiskFilterListInput{
- DiskFilterListInputBase: query.DiskFilterListInputBase,
- }
- q, err = manager.SDiskResourceBaseManager.OrderByExtraFields(ctx, q, userCred, diskInput)
- if err != nil {
- return nil, errors.Wrap(err, "SDiskResourceBaseManager.OrderByExtraFields")
- }
- storageInput := api.StorageFilterListInput{
- StorageFilterListInputBase: query.StorageFilterListInputBase,
- }
- q, err = manager.SStorageResourceBaseManager.OrderByExtraFields(ctx, q, userCred, storageInput)
- if err != nil {
- return nil, errors.Wrap(err, "SStorageResourceBaseManager.OrderByExtraFields")
- }
- if db.NeedOrderQuery([]string{query.OrderByGuest}) {
- guestSQ := GuestManager.Query("name", "id").SubQuery()
- guestdiskQ := GuestdiskManager.Query()
- guestdiskQ = guestdiskQ.Join(guestSQ, sqlchemy.Equals(guestSQ.Field("id"), guestdiskQ.Field("guest_id")))
- guestdiskSQ := guestdiskQ.AppendField(guestdiskQ.Field("disk_id"), guestSQ.Field("name").Label("guest_name")).SubQuery()
- q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(guestdiskSQ.Field("disk_id"), q.Field("disk_id")))
- q = q.AppendField(q.QueryFields()...)
- q = q.AppendField(guestdiskSQ.Field("guest_name"))
- q = db.OrderByFields(q, []string{query.OrderByGuest}, []sqlchemy.IQueryField{q.Field("guest_name")})
- }
- if db.NeedOrderQuery([]string{query.OrderByDiskName}) {
- dSQ := DiskManager.Query("name", "id").SubQuery()
- guestdiskQ := GuestdiskManager.Query()
- guestdiskQ = guestdiskQ.LeftJoin(dSQ, sqlchemy.Equals(dSQ.Field("id"), guestdiskQ.Field("disk_id")))
- guestdiskSQ := guestdiskQ.AppendField(guestdiskQ.Field("disk_id"), dSQ.Field("name").Label("disk_name")).SubQuery()
- q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(guestdiskSQ.Field("disk_id"), q.Field("disk_id")))
- q = q.AppendField(q.QueryFields()...)
- q = q.AppendField(guestdiskSQ.Field("disk_name"))
- q = db.OrderByFields(q, []string{query.OrderByDiskName}, []sqlchemy.IQueryField{q.Field("disk_name")})
- }
- return q, nil
- }
- func (manager *SSnapshotManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (manager *SSnapshotManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
- switch resource {
- case StorageManager.Keyword():
- storages := StorageManager.Query().SubQuery()
- for _, field := range fields {
- q = q.AppendField(storages.Field(field))
- }
- q = q.Join(storages, sqlchemy.Equals(q.Field("storage_id"), storages.Field("id")))
- return q, nil
- case CloudproviderManager.Keyword():
- return manager.SManagedResourceBaseManager.QueryDistinctExtraFields(q, resource, fields)
- }
- 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,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
- ) []api.SnapshotDetails {
- rows := make([]api.SnapshotDetails, len(objs))
- virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- 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],
- ManagedResourceInfo: manRows[i],
- CloudregionResourceInfo: regionRows[i],
- EncryptedResourceDetails: encRows[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
- rows[i].Storage = storage.Name
- }
- 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) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
- res := self.SVirtualResourceBase.GetShortDesc(ctx)
- res.Add(jsonutils.NewInt(int64(self.VirtualSize)), "virtual_size")
- res.Add(jsonutils.NewInt(int64(self.Size)), "size")
- res.Add(jsonutils.NewString(self.DiskId), "disk_id")
- disk, _ := self.GetDisk()
- if disk != nil {
- if guest := disk.GetGuest(); guest != nil {
- res.Add(jsonutils.NewString(guest.Id), "guest_id")
- }
- }
- info := self.getCloudProviderInfo()
- res.Update(jsonutils.Marshal(&info))
- return res
- }
- // 创建快照
- func (manager *SSnapshotManager) ValidateCreateData(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider,
- query jsonutils.JSONObject,
- input api.SnapshotCreateInput,
- ) (api.SnapshotCreateInput, error) {
- if input.NeedEncrypt() {
- return input, errors.Wrap(httperrors.ErrInputParameter, "encryption should not be set")
- }
- if len(input.DiskId) == 0 {
- return input, httperrors.NewMissingParameterError("disk_id")
- }
- _disk, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.DiskId)
- if err != nil {
- return input, err
- }
- disk := _disk.(*SDisk)
- if disk.Status != api.DISK_READY {
- return input, httperrors.NewInvalidStatusError("disk %s status is not %s", disk.Name, api.DISK_READY)
- }
- if len(disk.SnapshotId) > 0 {
- if disk.GetMetadata(ctx, "merge_snapshot", userCred) == "true" {
- return input, httperrors.NewBadRequestError("disk %s backing snapshot not merged", disk.Id)
- }
- }
- if len(disk.EncryptKeyId) > 0 {
- input.EncryptKeyId = &disk.EncryptKeyId
- input.EncryptedResourceCreateInput, err = manager.SEncryptedResourceManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EncryptedResourceCreateInput)
- if err != nil {
- return input, errors.Wrap(err, "SEncryptedResourceManager.ValidateCreateData")
- }
- }
- input.DiskType = disk.DiskType
- input.Size = disk.DiskSize
- input.VirtualSize = disk.DiskSize
- input.OsArch = disk.OsArch
- storage, _ := disk.GetStorage()
- if len(disk.ExternalId) == 0 {
- input.StorageId = disk.StorageId
- }
- input.ManagerId = storage.ManagerId
- region, err := storage.GetRegion()
- if err != nil {
- return input, err
- }
- input.CloudregionId = region.Id
- driver, err := storage.GetRegionDriver()
- if err != nil {
- return input, errors.Wrap(err, "storage.GetRegionDriver")
- }
- input.OutOfChain = driver.SnapshotIsOutOfChain(disk)
- err = driver.ValidateCreateSnapshotData(ctx, userCred, disk, storage, &input)
- if err != nil {
- return input, errors.Wrap(err, "driver.ValidateCreateSnapshotData")
- }
- input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
- if err != nil {
- return input, err
- }
- pendingUsage := &SRegionQuota{Snapshot: 1}
- keys, err := disk.GetQuotaKeys()
- if err != nil {
- return input, err
- }
- pendingUsage.SetKeys(keys.(SComputeResourceKeys).SRegionalCloudResourceKeys)
- err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
- if err != nil {
- return input, err
- }
- return input, nil
- }
- func (self *SSnapshot) CustomizeCreate(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- ) error {
- // use disk's ownerId instead of default ownerId
- diskObj, err := DiskManager.FetchById(self.DiskId)
- if err != nil {
- return errors.Wrap(err, "DiskManager.FetchById")
- }
- ownerId = diskObj.(*SDisk).GetOwnerId()
- return self.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
- }
- func (snapshot *SSnapshot) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
- snapshot.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
- pendingUsage := SRegionQuota{Snapshot: 1}
- keys := snapshot.GetQuotaKeys()
- pendingUsage.SetKeys(keys)
- err := quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, true)
- if err != nil {
- log.Errorf("quotas.CancelPendingUsage fail %s", err)
- }
- disk, err := snapshot.GetDisk()
- if err != nil {
- log.Errorf("unable to GetDisk: %s", err.Error())
- }
- err = disk.InheritTo(ctx, userCred, snapshot)
- if err != nil {
- log.Errorf("unable to inherit from disk %s to snapshot %s: %s", disk.GetId(), snapshot.GetId(), err.Error())
- }
- }
- func (manager *SSnapshotManager) OnCreateComplete(ctx context.Context, items []db.IModel, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data []jsonutils.JSONObject) {
- snapshot := items[0].(*SSnapshot)
- snapshot.StartSnapshotCreateTask(ctx, userCred, nil, "")
- }
- func (self *SSnapshot) StartSnapshotCreateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
- task, err := taskman.TaskManager.NewTask(ctx, "SnapshotCreateTask", self, userCred, params, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrapf(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (self *SSnapshot) GetGuest() (*SGuest, error) {
- iDisk, err := DiskManager.FetchById(self.DiskId)
- if err != nil {
- return nil, err
- }
- disk := iDisk.(*SDisk)
- guests := disk.GetGuests()
- if len(guests) > 1 {
- return nil, fmt.Errorf("Snapshot disk attach mutil guest")
- } else if len(guests) == 1 {
- return &guests[0], nil
- } else {
- return nil, sql.ErrNoRows
- }
- }
- func (self *SSnapshot) GetDisk() (*SDisk, error) {
- iDisk, err := DiskManager.FetchById(self.DiskId)
- if err != nil {
- return nil, err
- }
- disk := iDisk.(*SDisk)
- return disk, nil
- }
- func (self *SSnapshot) GetHost() (*SHost, error) {
- iStorage, err := StorageManager.FetchById(self.StorageId)
- if err != nil {
- return nil, errors.Wrapf(err, "StorageManager.FetchById(%s)", self.StorageId)
- }
- storage := iStorage.(*SStorage)
- return storage.GetMasterHost()
- }
- func (self *SSnapshot) GetFuseUrl() (string, error) {
- iStorage, err := StorageManager.FetchById(self.StorageId)
- if err != nil {
- return "", errors.Wrapf(err, "StorageManager.FetchById(%s)", self.StorageId)
- }
- storage := iStorage.(*SStorage)
- if storage.StorageType != api.STORAGE_LOCAL && storage.StorageType != api.STORAGE_LVM {
- return "", nil
- }
- host, err := storage.GetMasterHost()
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("%s/snapshots/%s/%s", host.GetFetchUrl(true), self.DiskId, self.Id), nil
- }
- func (self *SSnapshotManager) AddRefCount(snapshotId string, count int) {
- iSnapshot, _ := self.FetchById(snapshotId)
- if iSnapshot != nil {
- snapshot := iSnapshot.(*SSnapshot)
- _, err := db.Update(snapshot, func() error {
- snapshot.RefCount += count
- return nil
- })
- if err != nil {
- log.Errorf("Snapshot add refence count error: %s", err)
- }
- }
- }
- func (self *SSnapshotManager) GetDiskSnapshotsByCreate(diskId, createdBy string) []SSnapshot {
- dest := make([]SSnapshot, 0)
- q := self.Query().SubQuery()
- sq := q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
- sqlchemy.Equals(q.Field("created_by"), createdBy),
- sqlchemy.Equals(q.Field("fake_deleted"), false)))
- err := db.FetchModelObjects(self, sq, &dest)
- if err != nil {
- log.Errorf("GetDiskSnapshots error: %s", err)
- return nil
- }
- return dest
- }
- func (self *SSnapshotManager) GetDiskSnapshots(diskId string) []SSnapshot {
- dest := make([]SSnapshot, 0)
- q := self.Query().Equals("disk_id", diskId).Asc("created_at")
- err := db.FetchModelObjects(self, q, &dest)
- if err != nil {
- log.Errorf("GetDiskSnapshots error: %s", err)
- return nil
- }
- return dest
- }
- func (self *SSnapshotManager) GetDiskManualSnapshotCount(diskId string) (int, error) {
- return self.Query().Equals("disk_id", diskId).Equals("fake_deleted", false).CountWithError()
- }
- func (self *SSnapshotManager) GetDiskFirstSnapshot(diskId string) *SSnapshot {
- dest := &SSnapshot{}
- q := self.Query().SubQuery()
- err := q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
- sqlchemy.In(q.Field("status"), []string{api.SNAPSHOT_READY, api.SNAPSHOT_DELETING}),
- sqlchemy.Equals(q.Field("out_of_chain"), false))).Asc("created_at").First(dest)
- if err != nil {
- log.Errorf("Get Disk First snapshot error: %s", err.Error())
- return nil
- }
- dest.SetModelManager(self, dest)
- return dest
- }
- func (self *SSnapshotManager) GetDiskSnapshotCount(diskId string) (int, error) {
- q := self.Query().SubQuery()
- return q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
- sqlchemy.Equals(q.Field("fake_deleted"), false))).CountWithError()
- }
- func (self *SSnapshotManager) CreateSnapshot(ctx context.Context, owner mcclient.IIdentityProvider,
- createdBy, diskId, guestId, location, name string, retentionDay int, isSystem bool, diskBackupId string) (*SSnapshot, error) {
- iDisk, err := DiskManager.FetchById(diskId)
- if err != nil {
- return nil, err
- }
- disk := iDisk.(*SDisk)
- storage, _ := disk.GetStorage()
- snapshot := &SSnapshot{}
- snapshot.SetModelManager(self, snapshot)
- snapshot.ProjectId = owner.GetProjectId()
- snapshot.DomainId = owner.GetProjectDomainId()
- snapshot.DiskId = disk.Id
- if len(disk.ExternalId) == 0 {
- snapshot.StorageId = disk.StorageId
- }
- // inherit encrypt_key_id
- snapshot.EncryptKeyId = disk.EncryptKeyId
- driver, err := storage.GetRegionDriver()
- if err != nil {
- return nil, err
- }
- snapshot.OutOfChain = driver.SnapshotIsOutOfChain(disk)
- snapshot.VirtualSize = disk.DiskSize
- snapshot.DiskType = disk.DiskType
- snapshot.Location = location
- snapshot.CreatedBy = createdBy
- snapshot.ManagerId = storage.ManagerId
- if cloudregion, _ := storage.GetRegion(); cloudregion != nil {
- snapshot.CloudregionId = cloudregion.GetId()
- }
- snapshot.Name = name
- snapshot.Status = api.SNAPSHOT_CREATING
- if retentionDay > 0 {
- snapshot.ExpiredAt = time.Now().AddDate(0, 0, retentionDay)
- }
- snapshot.IsSystem = isSystem
- snapshot.DiskBackupId = diskBackupId
- err = SnapshotManager.TableSpec().Insert(ctx, snapshot)
- if err != nil {
- return nil, err
- }
- return snapshot, nil
- }
- func (self *SSnapshot) StartSnapshotDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, reloadDisk bool, parentTaskId string, deleteSnapshotTotalCnt int, deletedSnapshotCnt int) error {
- params := jsonutils.NewDict()
- params.Set("reload_disk", jsonutils.NewBool(reloadDisk))
- if deleteSnapshotTotalCnt <= 0 {
- deleteSnapshotTotalCnt = 1
- }
- params.Set("snapshot_total_count", jsonutils.NewInt(int64(deleteSnapshotTotalCnt)))
- params.Set("deleted_snapshot_count", jsonutils.NewInt(int64(deletedSnapshotCnt)))
- self.SetStatus(ctx, userCred, api.SNAPSHOT_DELETING, "")
- task, err := taskman.TaskManager.NewTask(ctx, "SnapshotDeleteTask", self, userCred, params, parentTaskId, "", nil)
- if err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- 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)
- }
- 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")
- }
- }
- driver := self.GetRegionDriver()
- if driver != nil {
- return driver.ValidateSnapshotDelete(ctx, self)
- }
- return nil
- }
- func (self *SSnapshot) GetStorage() *SStorage {
- return StorageManager.FetchStorageById(self.StorageId)
- }
- func (self *SSnapshot) GetStorageType() string {
- if storage := self.GetStorage(); storage != nil {
- return storage.StorageType
- }
- return ""
- }
- func (self *SSnapshot) GetRegionDriver() IRegionDriver {
- cloudRegion, _ := self.GetRegion()
- if cloudRegion != nil {
- return cloudRegion.GetDriver()
- }
- return nil
- }
- func (self *SSnapshot) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- return self.StartSnapshotDeleteTask(ctx, userCred, false, "", 0, 0)
- }
- // +onecloud:swagger-gen-ignore
- func (self *SSnapshot) PerformDeleted(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- _, err := db.Update(self, func() error {
- self.OutOfChain = true
- return nil
- })
- if err != nil {
- return nil, err
- }
- err = self.StartSnapshotDeleteTask(ctx, userCred, true, "", 0, 0)
- return nil, err
- }
- // 同步快照状态
- func (self *SSnapshot) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SnapshotSyncstatusInput) (jsonutils.JSONObject, error) {
- var openTask = true
- count, err := taskman.TaskManager.QueryTasksOfObject(self, time.Now().Add(-3*time.Minute), &openTask).CountWithError()
- if err != nil {
- return nil, err
- }
- if count > 0 {
- return nil, httperrors.NewBadRequestError("Snapshot has %d task active, can't sync status", count)
- }
- return nil, StartResourceSyncStatusTask(ctx, userCred, self, "SnapshotSyncstatusTask", "")
- }
- func (self *SSnapshotManager) GetConvertSnapshot(deleteSnapshot *SSnapshot) (*SSnapshot, error) {
- dest := &SSnapshot{}
- q := self.Query()
- err := q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), deleteSnapshot.DiskId),
- sqlchemy.In(q.Field("status"), []string{api.SNAPSHOT_READY, api.SNAPSHOT_DELETING}),
- sqlchemy.Equals(q.Field("out_of_chain"), false),
- sqlchemy.GT(q.Field("created_at"), deleteSnapshot.CreatedAt))).
- Asc("created_at").First(dest)
- if err != nil {
- return nil, err
- }
- return dest, nil
- }
- // +onecloud:swagger-gen-ignore
- func (self *SSnapshotManager) PerformDeleteDiskSnapshots(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SnapshotDeleteDiskSnapshotsInput) (jsonutils.JSONObject, error) {
- disk, err := DiskManager.FetchById(input.DiskId)
- if disk != nil {
- return nil, httperrors.NewBadRequestError("Cannot Delete disk %s snapshots, disk exist", input.DiskId)
- }
- snapshots := self.GetDiskSnapshots(input.DiskId)
- if snapshots == nil || len(snapshots) == 0 {
- return nil, httperrors.NewNotFoundError("Disk %s dose not have snapshot", input.DiskId)
- }
- snapshotIds := []string{}
- for i := 0; i < len(snapshots); i++ {
- if snapshots[i].FakeDeleted == false {
- return nil, httperrors.NewBadRequestError("Can not delete disk snapshots, have manual snapshot")
- }
- snapshotIds = append(snapshotIds, snapshots[i].Id)
- }
- err = snapshots[0].StartSnapshotsDeleteTask(ctx, userCred, "", snapshotIds)
- return nil, err
- }
- func (self *SSnapshot) StartSnapshotsDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, snapshotIds []string) error {
- data := jsonutils.NewDict()
- data.Set("snapshot_ids", jsonutils.NewStringArray(snapshotIds))
- task, err := taskman.TaskManager.NewTask(ctx, "BatchSnapshotsDeleteTask", self, userCred, nil, parentTaskId, "", nil)
- if err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (self *SSnapshot) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- if len(self.DiskId) > 0 {
- storage := self.GetStorage()
- if storage != nil && storage.StorageType == api.STORAGE_RBD {
- disk := DiskManager.FetchDiskById(self.DiskId)
- if disk != nil {
- cnt, err := disk.GetGuestsCount()
- if err == nil {
- val := disk.GetMetadata(ctx, "disk_delete_after_snapshots", userCred)
- if cnt == 0 && val == "true" {
- disk.StartDiskDeleteTask(ctx, userCred, "", false, true, false)
- }
- } else {
- // very unlikely
- log.Errorf("disk.GetGuestsCount fail %s", err)
- }
- } else {
- backingDisks, err := self.GetBackingDisks()
- if err != nil {
- // very unlikely
- log.Errorf("self.GetBackingDisks fail %s", err)
- } else {
- storage.StartDeleteRbdDisks(ctx, userCred, backingDisks)
- }
- }
- }
- }
- return db.DeleteModel(ctx, userCred, self)
- }
- func (self *SSnapshot) GetBackingDisks() ([]string, error) {
- count, err := SnapshotManager.Query().Equals("disk_id", self.DiskId).IsNullOrEmpty("backing_disk_id").CountWithError()
- if err != nil {
- return nil, err
- }
- if count > 0 {
- return nil, nil
- } else {
- sps := make([]SSnapshot, 0)
- err := SnapshotManager.Query().Equals("disk_id", self.DiskId).All(&sps)
- if err != nil {
- return nil, err
- }
- res := make([]string, 0)
- for i := 0; i < len(sps); i++ {
- if len(sps[i].BackingDiskId) > 0 && !utils.IsInStringArray(sps[i].BackingDiskId, res) {
- res = append(res, sps[i].BackingDiskId)
- }
- }
- res = append(res, self.DiskId)
- return res, nil
- }
- }
- func (self *SSnapshot) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
- 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(ctx context.Context, scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string, policyResult rbacutils.SPolicyResult) (int, error) {
- q := SnapshotManager.Query()
- switch scope {
- case rbacscope.ScopeSystem:
- case rbacscope.ScopeDomain:
- q = q.Equals("domain_id", ownerId.GetProjectDomainId())
- case rbacscope.ScopeProject:
- q = q.Equals("tenant_id", ownerId.GetProjectId())
- }
- q = db.ObjectIdQueryWithPolicyResult(ctx, q, SnapshotManager, policyResult)
- q = RangeObjectsFilter(q, rangeObjs, q.Field("cloudregion_id"), nil, q.Field("manager_id"), nil, nil)
- q = CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv)
- q = q.Equals("created_by", api.SNAPSHOT_MANUAL)
- q = q.Equals("fake_deleted", false)
- return q.CountWithError()
- }
- func (self *SSnapshot) syncRemoveCloudSnapshot(ctx context.Context, userCred mcclient.TokenCredential) error {
- lockman.LockObject(ctx, self)
- defer lockman.ReleaseObject(ctx, self)
- err := self.ValidateDeleteCondition(ctx, nil)
- if err != nil {
- err = self.SetStatus(ctx, userCred, api.SNAPSHOT_UNKNOWN, "sync to delete")
- } else {
- err = self.RealDelete(ctx, userCred)
- }
- return err
- }
- // Only sync snapshot status
- func (self *SSnapshot) SyncWithCloudSnapshot(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudSnapshot, syncOwnerId mcclient.IIdentityProvider, region *SCloudregion) error {
- diff, err := db.UpdateWithLock(ctx, self, func() error {
- if options.Options.EnableSyncName {
- newName, _ := db.GenerateAlterName(self, ext.GetName())
- if len(newName) > 0 {
- self.Name = newName
- }
- }
- self.Status = ext.GetStatus()
- self.DiskType = ext.GetDiskType()
- self.VirtualSize = int(ext.GetSizeMb())
- self.Size = int(ext.GetSizeMb())
- disk, _ := self.GetDisk()
- if gotypes.IsNil(disk) && len(ext.GetDiskId()) > 0 {
- disk, err := db.FetchByExternalIdAndManagerId(DiskManager, ext.GetDiskId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- sq := StorageManager.Query().SubQuery()
- return q.Join(sq, sqlchemy.Equals(q.Field("storage_id"), sq.Field("id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), self.ManagerId))
- })
- if err != nil {
- log.Errorf("snapshot %s missing disk?", self.Name)
- } else {
- self.DiskId = disk.GetId()
- }
- }
- self.CloudregionId = region.Id
- return nil
- })
- if err != nil {
- log.Errorf("SyncWithCloudSnapshot fail %s", err)
- return err
- }
- db.OpsLog.LogSyncUpdate(self, diff, userCred)
- if account := self.GetCloudaccount(); account != nil {
- syncVirtualResourceMetadata(ctx, userCred, self, ext, account.ReadOnly)
- }
- // bugfix for now:
- disk, _ := self.GetDisk()
- if disk != nil {
- self.SyncCloudProjectId(userCred, disk.GetOwnerId())
- } else {
- SyncCloudProject(ctx, userCred, self, syncOwnerId, ext, self.GetCloudprovider())
- }
- return nil
- }
- func (manager *SSnapshotManager) newFromCloudSnapshot(ctx context.Context, userCred mcclient.TokenCredential, extSnapshot cloudprovider.ICloudSnapshot, region *SCloudregion, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider) (*SSnapshot, error) {
- snapshot := SSnapshot{}
- snapshot.SetModelManager(manager, &snapshot)
- snapshot.Status = extSnapshot.GetStatus()
- snapshot.ExternalId = extSnapshot.GetGlobalId()
- var localDisk *SDisk
- if len(extSnapshot.GetDiskId()) > 0 {
- disk, err := db.FetchByExternalIdAndManagerId(DiskManager, extSnapshot.GetDiskId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- sq := StorageManager.Query().SubQuery()
- return q.Join(sq, sqlchemy.Equals(q.Field("storage_id"), sq.Field("id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), provider.Id))
- })
- if err != nil {
- log.Errorf("snapshot %s missing disk?", snapshot.Name)
- } else {
- snapshot.DiskId = disk.GetId()
- localDisk = disk.(*SDisk)
- }
- }
- snapshot.DiskType = extSnapshot.GetDiskType()
- snapshot.VirtualSize = int(extSnapshot.GetSizeMb())
- snapshot.Size = int(extSnapshot.GetSizeMb())
- snapshot.ManagerId = provider.Id
- snapshot.CloudregionId = region.Id
- var err = func() error {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- newName, err := db.GenerateName(ctx, manager, syncOwnerId, extSnapshot.GetName())
- if err != nil {
- return err
- }
- snapshot.Name = newName
- return manager.TableSpec().Insert(ctx, &snapshot)
- }()
- if err != nil {
- return nil, errors.Wrapf(err, "Insert")
- }
- syncVirtualResourceMetadata(ctx, userCred, &snapshot, extSnapshot, false)
- // bugfix for now:
- if localDisk != nil {
- snapshot.SyncCloudProjectId(userCred, localDisk.GetOwnerId())
- } else {
- SyncCloudProject(ctx, userCred, &snapshot, syncOwnerId, extSnapshot, provider)
- }
- db.OpsLog.LogEvent(&snapshot, db.ACT_CREATE, snapshot.GetShortDesc(ctx), userCred)
- return &snapshot, nil
- }
- func (manager *SSnapshotManager) getProviderSnapshotsByRegion(region *SCloudregion, provider *SCloudprovider) ([]SSnapshot, error) {
- if region == nil || provider == nil {
- return nil, fmt.Errorf("Region is nil or provider is nil")
- }
- snapshots := make([]SSnapshot, 0)
- q := manager.Query().Equals("cloudregion_id", region.Id).Equals("manager_id", provider.Id)
- err := db.FetchModelObjects(manager, q, &snapshots)
- if err != nil {
- return nil, err
- }
- return snapshots, nil
- }
- func (manager *SSnapshotManager) SyncSnapshots(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- provider *SCloudprovider,
- region *SCloudregion,
- snapshots []cloudprovider.ICloudSnapshot,
- syncOwnerId mcclient.IIdentityProvider,
- xor bool,
- ) compare.SyncResult {
- lockman.LockRawObject(ctx, manager.Keyword(), fmt.Sprintf("%s-%s", provider.Id, region.Id))
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), fmt.Sprintf("%s-%s", provider.Id, region.Id))
- syncResult := compare.SyncResult{}
- dbSnapshots, err := manager.getProviderSnapshotsByRegion(region, provider)
- if err != nil {
- syncResult.Error(err)
- return syncResult
- }
- removed := make([]SSnapshot, 0)
- commondb := make([]SSnapshot, 0)
- commonext := make([]cloudprovider.ICloudSnapshot, 0)
- added := make([]cloudprovider.ICloudSnapshot, 0)
- err = compare.CompareSets(dbSnapshots, snapshots, &removed, &commondb, &commonext, &added)
- if err != nil {
- syncResult.Error(err)
- return syncResult
- }
- for i := 0; i < len(removed); i += 1 {
- err = removed[i].syncRemoveCloudSnapshot(ctx, userCred)
- if err != nil {
- syncResult.DeleteError(err)
- continue
- }
- syncResult.Delete()
- }
- if !xor {
- for i := 0; i < len(commondb); i += 1 {
- err = commondb[i].SyncWithCloudSnapshot(ctx, userCred, commonext[i], syncOwnerId, region)
- if err != nil {
- syncResult.UpdateError(err)
- continue
- }
- syncResult.Update()
- }
- }
- for i := 0; i < len(added); i += 1 {
- _, err := manager.newFromCloudSnapshot(ctx, userCred, added[i], region, syncOwnerId, provider)
- if err != nil {
- syncResult.AddError(err)
- continue
- }
- syncResult.Add()
- }
- return syncResult
- }
- func (self *SSnapshot) GetISnapshotRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) {
- provider, err := self.GetDriver(ctx)
- if err != nil {
- return nil, err
- }
- region, err := self.GetRegion()
- if err != nil {
- return nil, err
- }
- return provider.GetIRegionById(region.GetExternalId())
- }
- // +onecloud:swagger-gen-ignore
- func (self *SSnapshot) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- err := self.GetRegionDriver().ValidateSnapshotDelete(ctx, self)
- if err != nil {
- return nil, err
- }
- provider := self.GetCloudprovider()
- if provider != nil {
- if provider.GetEnabled() {
- return nil, httperrors.NewInvalidStatusError("Cannot purge snapshot on enabled cloud provider")
- }
- }
- err = self.RealDelete(ctx, userCred)
- return nil, err
- }
- func (self *SSnapshot) getCloudProviderInfo() SCloudProviderInfo {
- region, _ := self.GetRegion()
- provider := self.GetCloudprovider()
- return MakeCloudProviderInfo(region, nil, provider)
- }
- func (manager *SSnapshotManager) GetResourceCount() ([]db.SScopeResourceCount, error) {
- virts := manager.Query().IsFalse("fake_deleted")
- 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().
- Equals("fake_deleted", false).
- Equals("created_by", api.SNAPSHOT_AUTO).
- LE("expired_at", now).First(snapshot)
- if err != nil && err != sql.ErrNoRows {
- log.Errorf("Cleanup snapshots job fetch snapshot failed %s", err)
- return
- } else if err == sql.ErrNoRows {
- log.Infof("No snapshot need to clean ......")
- return
- }
- snapshot.SetModelManager(manager, snapshot)
- region, _ := snapshot.GetRegion()
- if err = manager.StartSnapshotCleanupTask(ctx, userCred, region, now); err != nil {
- log.Errorf("Start snaphsot cleanup task failed %s", err)
- return
- }
- sq := manager.Query().Equals("status", api.SNAPSHOT_READY).Equals("created_by", api.SNAPSHOT_AUTO).Equals("fake_deleted", false).SubQuery()
- disks := []struct {
- DiskCnt int
- DiskId string
- }{}
- q := sq.Query(
- sqlchemy.COUNT("disk_cnt", sq.Field("disk_id")),
- sq.Field("disk_id"),
- ).GroupBy(sq.Field("disk_id"))
- err = q.All(&disks)
- if err != nil {
- log.Errorf("Cleanup snapshots job fetch disk count failed %s", err)
- return
- }
- diskCount := map[string]int{}
- for i := range disks {
- diskCount[disks[i].DiskId] = disks[i].DiskCnt
- }
- {
- sq = SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_DISK).GT("retention_count", 0).SubQuery()
- spd := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK).SubQuery()
- q = sq.Query(
- sq.Field("retention_count"),
- spd.Field("resource_id").Label("disk_id"),
- )
- q = q.Join(spd, sqlchemy.Equals(q.Field("id"), spd.Field("snapshotpolicy_id")))
- diskRetentions := []struct {
- DiskId string
- RetentionCount int
- }{}
- err = q.All(&diskRetentions)
- if err != nil {
- log.Errorf("Cleanup snapshots job fetch disk retentions failed %s", err)
- return
- }
- diskRetentionMap := map[string]int{}
- for i := range diskRetentions {
- if _, ok := diskRetentionMap[diskRetentions[i].DiskId]; !ok {
- diskRetentionMap[diskRetentions[i].DiskId] = diskRetentions[i].RetentionCount
- }
- // 取最小保留个数
- if diskRetentionMap[diskRetentions[i].DiskId] > diskRetentions[i].RetentionCount {
- diskRetentionMap[diskRetentions[i].DiskId] = diskRetentions[i].RetentionCount
- }
- }
- for diskId, retentionCnt := range diskRetentionMap {
- if cnt, ok := diskCount[diskId]; ok && cnt > retentionCnt {
- manager.startCleanupRetentionCount(ctx, userCred, diskId, cnt-retentionCnt)
- }
- }
- }
- }
- func (manager *SSnapshotManager) startCleanupRetentionCount(ctx context.Context, userCred mcclient.TokenCredential, diskId string, cnt int) error {
- q := manager.Query().Equals("disk_id", diskId).Equals("created_by", api.SNAPSHOT_AUTO).Asc("created_at").Limit(cnt)
- snapshots := []SSnapshot{}
- err := db.FetchModelObjects(manager, q, &snapshots)
- if err != nil {
- return errors.Wrapf(err, "FetchModelObjects")
- }
- for i := range snapshots {
- snapshots[i].StartSnapshotDeleteTask(ctx, userCred, false, "", 0, 0)
- }
- return nil
- }
- func (manager *SSnapshotManager) StartSnapshotCleanupTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- region *SCloudregion, now time.Time,
- ) error {
- params := jsonutils.NewDict()
- params.Set("tick", jsonutils.NewTimeString(now))
- task, err := taskman.TaskManager.NewTask(ctx, "SnapshotCleanupTask", region, userCred, params, "", "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SSnapshot) GetQuotaKeys() quotas.IQuotaKeys {
- region, _ := self.GetRegion()
- return fetchRegionalQuotaKeys(
- rbacscope.ScopeProject,
- self.GetOwnerId(),
- region,
- self.GetCloudprovider(),
- )
- }
- func (snapshot *SSnapshot) GetUsages() []db.IUsage {
- if snapshot.PendingDeleted || snapshot.Deleted {
- return nil
- }
- usage := SRegionQuota{Snapshot: 1}
- keys := snapshot.GetQuotaKeys()
- usage.SetKeys(keys)
- return []db.IUsage{
- &usage,
- }
- }
- func (manager *SSnapshotManager) ListItemExportKeys(ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- keys stringutils2.SSortedStrings,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, err
- }
- if keys.Contains("disk") {
- q, err = manager.SDiskResourceBaseManager.ListItemExportKeys(ctx, q, userCred, stringutils2.NewSortedStrings([]string{"disk"}))
- if err != nil {
- return nil, errors.Wrap(err, "SDiskResourceBaseManager.ListItemExportKeys")
- }
- }
- if keys.ContainsAny(manager.SStorageResourceBaseManager.GetExportKeys()...) {
- q, err = manager.SStorageResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SStorageResourceBaseManager.ListItemExportKeys")
- }
- }
- return q, nil
- }
- func (manager *SSnapshotManager) DataCleaning(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
- err := dataCleaning(manager.TableSpec().Name())
- if err != nil {
- log.Errorf("************* %s:dataCleaning error:%s ************", manager.TableSpec().Name(), err.Error())
- }
- }
- func dataCleaning(tableName string) error {
- if options.Options.KeepDeletedSnapshotDays <= 0 {
- return nil
- }
- _, err := sqlchemy.GetDB().Exec(
- fmt.Sprintf(
- "delete from %s where deleted = 1 and deleted_at < ?",
- tableName,
- ), time.Now().AddDate(0, 0, -options.Options.KeepDeletedSnapshotDays),
- )
- if err != nil {
- return errors.Wrapf(err, "unable to delete expired data in %q", tableName)
- }
- log.Infof("delete expired data in %q successfully", tableName)
- return nil
- }
|