snapshots.go 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package models
  15. import (
  16. "context"
  17. "database/sql"
  18. "fmt"
  19. "sync/atomic"
  20. "time"
  21. "yunion.io/x/cloudmux/pkg/cloudprovider"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/gotypes"
  26. "yunion.io/x/pkg/util/compare"
  27. "yunion.io/x/pkg/util/rbacscope"
  28. "yunion.io/x/pkg/utils"
  29. "yunion.io/x/sqlchemy"
  30. api "yunion.io/x/onecloud/pkg/apis/compute"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  32. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  34. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  35. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  36. "yunion.io/x/onecloud/pkg/compute/options"
  37. "yunion.io/x/onecloud/pkg/httperrors"
  38. "yunion.io/x/onecloud/pkg/mcclient"
  39. "yunion.io/x/onecloud/pkg/util/rbacutils"
  40. "yunion.io/x/onecloud/pkg/util/stringutils2"
  41. )
  42. type SSnapshotManager struct {
  43. db.SVirtualResourceBaseManager
  44. db.SExternalizedResourceBaseManager
  45. SManagedResourceBaseManager
  46. SCloudregionResourceBaseManager
  47. SDiskResourceBaseManager
  48. SStorageResourceBaseManager
  49. db.SMultiArchResourceBaseManager
  50. db.SEncryptedResourceManager
  51. }
  52. type SSnapshot struct {
  53. db.SVirtualResourceBase
  54. db.SExternalizedResourceBase
  55. SManagedResourceBase
  56. SCloudregionResourceBase `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
  57. db.SMultiArchResourceBase
  58. db.SEncryptedResource
  59. // 磁盘Id
  60. DiskId string `width:"36" charset:"ascii" nullable:"true" create:"required" list:"user" index:"true"`
  61. // Only onecloud has StorageId
  62. StorageId string `width:"36" charset:"ascii" nullable:"true" list:"admin" create:"optional"`
  63. CreatedBy string `width:"36" charset:"ascii" nullable:"false" default:"manual" list:"user" create:"optional"`
  64. Location string `charset:"ascii" nullable:"true" list:"admin" create:"optional"`
  65. // 快照大小,单位Mb
  66. Size int `nullable:"false" list:"user" create:"optional"`
  67. // Virtual size, for kvm is origin disk size
  68. VirtualSize int `nullable:"false" list:"user" create:"optional"`
  69. OutOfChain bool `nullable:"false" default:"false" list:"admin" create:"optional"`
  70. FakeDeleted bool `nullable:"false" default:"false"`
  71. DiskType string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional"`
  72. // 操作系统类型
  73. OsType string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional"`
  74. // create disk from snapshot, snapshot as disk backing file
  75. RefCount int `nullable:"false" default:"0" list:"user"`
  76. BackingDiskId string `width:"36" charset:"ascii" nullable:"true" default:""`
  77. DiskBackupId string `width:"36" charset:"ascii" nullable:"true" default:""`
  78. ExpiredAt time.Time `nullable:"true" list:"user" create:"optional"`
  79. }
  80. var SnapshotManager *SSnapshotManager
  81. func init() {
  82. SnapshotManager = &SSnapshotManager{
  83. SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
  84. SSnapshot{},
  85. "snapshots_tbl",
  86. "snapshot",
  87. "snapshots",
  88. ),
  89. }
  90. SnapshotManager.SetVirtualObject(SnapshotManager)
  91. }
  92. // 快照列表
  93. func (manager *SSnapshotManager) ListItemFilter(
  94. ctx context.Context,
  95. q *sqlchemy.SQuery,
  96. userCred mcclient.TokenCredential,
  97. query api.SnapshotListInput,
  98. ) (*sqlchemy.SQuery, error) {
  99. var err error
  100. q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
  101. if err != nil {
  102. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
  103. }
  104. q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
  105. if err != nil {
  106. return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
  107. }
  108. q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
  109. if err != nil {
  110. return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
  111. }
  112. q, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, query.RegionalFilterListInput)
  113. if err != nil {
  114. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
  115. }
  116. q, err = manager.SMultiArchResourceBaseManager.ListItemFilter(ctx, q, userCred, query.MultiArchResourceBaseListInput)
  117. if err != nil {
  118. return nil, errors.Wrap(err, "SMultiArchResourceBaseManager.ListItemFilter")
  119. }
  120. if query.FakeDeleted != nil && *query.FakeDeleted {
  121. q = q.IsTrue("fake_deleted")
  122. } else {
  123. q = q.IsFalse("fake_deleted")
  124. }
  125. if query.Local != nil && *query.Local {
  126. storages := StorageManager.Query().SubQuery()
  127. sq := storages.Query(storages.Field("id")).Filter(sqlchemy.Equals(storages.Field("storage_type"), api.STORAGE_LOCAL))
  128. q = q.Filter(sqlchemy.In(q.Field("storage_id"), sq))
  129. }
  130. // Public cloud snapshot doesn't have storage id
  131. if query.Share != nil && *query.Share {
  132. storages := StorageManager.Query().SubQuery()
  133. sq := storages.Query(storages.Field("id")).NotEquals("storage_type", "local")
  134. q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("storage_id")),
  135. sqlchemy.In(q.Field("storage_id"), sq)))
  136. }
  137. if len(query.DiskType) > 0 {
  138. q = q.Equals("disk_type", query.DiskType)
  139. }
  140. if query.IsInstanceSnapshot != nil {
  141. insjsq := InstanceSnapshotJointManager.Query().SubQuery()
  142. if !*query.IsInstanceSnapshot {
  143. q = q.LeftJoin(insjsq, sqlchemy.Equals(q.Field("id"), insjsq.Field("snapshot_id"))).
  144. Filter(sqlchemy.IsNull(insjsq.Field("snapshot_id")))
  145. } else {
  146. q = q.Join(insjsq, sqlchemy.Equals(q.Field("id"), insjsq.Field("snapshot_id")))
  147. }
  148. }
  149. diskInput := api.DiskFilterListInput{
  150. DiskFilterListInputBase: query.DiskFilterListInputBase,
  151. }
  152. q, err = manager.SDiskResourceBaseManager.ListItemFilter(ctx, q, userCred, diskInput)
  153. if err != nil {
  154. return nil, errors.Wrap(err, "SDiskResourceBaseManager.ListItemFilter")
  155. }
  156. storageInput := api.StorageFilterListInput{
  157. StorageFilterListInputBase: query.StorageFilterListInputBase,
  158. }
  159. q, err = manager.SStorageResourceBaseManager.ListItemFilter(ctx, q, userCred, storageInput)
  160. if err != nil {
  161. return nil, errors.Wrap(err, "SStorageResourceBaseManager.ListItemFilter")
  162. }
  163. if query.OutOfChain != nil {
  164. if *query.OutOfChain {
  165. q = q.IsTrue("out_of_chain")
  166. } else {
  167. q = q.IsFalse("out_of_chain")
  168. }
  169. }
  170. if len(query.OsType) > 0 {
  171. q = q.In("os_type", query.OsType)
  172. }
  173. if len(query.ServerId) > 0 {
  174. iG, err := GuestManager.FetchByIdOrName(ctx, userCred, query.ServerId)
  175. if err != nil && err == sql.ErrNoRows {
  176. return nil, httperrors.NewNotFoundError("guest %s not found", query.ServerId)
  177. } else if err != nil {
  178. return nil, errors.Wrap(err, "fetch guest")
  179. }
  180. guest := iG.(*SGuest)
  181. gdq := GuestdiskManager.Query("disk_id").Equals("guest_id", guest.Id).SubQuery()
  182. q = q.In("disk_id", gdq)
  183. }
  184. if query.Unused {
  185. sq := DiskManager.Query("id").Distinct().SubQuery()
  186. q = q.NotIn("disk_id", sq)
  187. }
  188. if len(query.StorageId) > 0 {
  189. q = q.Equals("storage_id", query.StorageId)
  190. }
  191. return q, nil
  192. }
  193. func (manager *SSnapshotManager) OrderByExtraFields(
  194. ctx context.Context,
  195. q *sqlchemy.SQuery,
  196. userCred mcclient.TokenCredential,
  197. query api.SnapshotListInput,
  198. ) (*sqlchemy.SQuery, error) {
  199. var err error
  200. q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
  201. if err != nil {
  202. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
  203. }
  204. q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
  205. if err != nil {
  206. return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
  207. }
  208. q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
  209. if err != nil {
  210. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
  211. }
  212. diskInput := api.DiskFilterListInput{
  213. DiskFilterListInputBase: query.DiskFilterListInputBase,
  214. }
  215. q, err = manager.SDiskResourceBaseManager.OrderByExtraFields(ctx, q, userCred, diskInput)
  216. if err != nil {
  217. return nil, errors.Wrap(err, "SDiskResourceBaseManager.OrderByExtraFields")
  218. }
  219. storageInput := api.StorageFilterListInput{
  220. StorageFilterListInputBase: query.StorageFilterListInputBase,
  221. }
  222. q, err = manager.SStorageResourceBaseManager.OrderByExtraFields(ctx, q, userCred, storageInput)
  223. if err != nil {
  224. return nil, errors.Wrap(err, "SStorageResourceBaseManager.OrderByExtraFields")
  225. }
  226. if db.NeedOrderQuery([]string{query.OrderByGuest}) {
  227. guestSQ := GuestManager.Query("name", "id").SubQuery()
  228. guestdiskQ := GuestdiskManager.Query()
  229. guestdiskQ = guestdiskQ.Join(guestSQ, sqlchemy.Equals(guestSQ.Field("id"), guestdiskQ.Field("guest_id")))
  230. guestdiskSQ := guestdiskQ.AppendField(guestdiskQ.Field("disk_id"), guestSQ.Field("name").Label("guest_name")).SubQuery()
  231. q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(guestdiskSQ.Field("disk_id"), q.Field("disk_id")))
  232. q = q.AppendField(q.QueryFields()...)
  233. q = q.AppendField(guestdiskSQ.Field("guest_name"))
  234. q = db.OrderByFields(q, []string{query.OrderByGuest}, []sqlchemy.IQueryField{q.Field("guest_name")})
  235. }
  236. if db.NeedOrderQuery([]string{query.OrderByDiskName}) {
  237. dSQ := DiskManager.Query("name", "id").SubQuery()
  238. guestdiskQ := GuestdiskManager.Query()
  239. guestdiskQ = guestdiskQ.LeftJoin(dSQ, sqlchemy.Equals(dSQ.Field("id"), guestdiskQ.Field("disk_id")))
  240. guestdiskSQ := guestdiskQ.AppendField(guestdiskQ.Field("disk_id"), dSQ.Field("name").Label("disk_name")).SubQuery()
  241. q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(guestdiskSQ.Field("disk_id"), q.Field("disk_id")))
  242. q = q.AppendField(q.QueryFields()...)
  243. q = q.AppendField(guestdiskSQ.Field("disk_name"))
  244. q = db.OrderByFields(q, []string{query.OrderByDiskName}, []sqlchemy.IQueryField{q.Field("disk_name")})
  245. }
  246. return q, nil
  247. }
  248. func (manager *SSnapshotManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  249. var err error
  250. q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
  251. if err == nil {
  252. return q, nil
  253. }
  254. q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
  255. if err == nil {
  256. return q, nil
  257. }
  258. q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
  259. if err == nil {
  260. return q, nil
  261. }
  262. return q, httperrors.ErrNotFound
  263. }
  264. func (manager *SSnapshotManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
  265. switch resource {
  266. case StorageManager.Keyword():
  267. storages := StorageManager.Query().SubQuery()
  268. for _, field := range fields {
  269. q = q.AppendField(storages.Field(field))
  270. }
  271. q = q.Join(storages, sqlchemy.Equals(q.Field("storage_id"), storages.Field("id")))
  272. return q, nil
  273. case CloudproviderManager.Keyword():
  274. return manager.SManagedResourceBaseManager.QueryDistinctExtraFields(q, resource, fields)
  275. }
  276. return q, httperrors.ErrNotFound
  277. }
  278. type sSnapshotGuest struct {
  279. DiskId string
  280. GuestId string
  281. GuestName string
  282. GuestStatus string
  283. }
  284. func (manager *SSnapshotManager) FetchCustomizeColumns(
  285. ctx context.Context,
  286. userCred mcclient.TokenCredential,
  287. query jsonutils.JSONObject,
  288. objs []interface{},
  289. fields stringutils2.SSortedStrings,
  290. isList bool,
  291. ) []api.SnapshotDetails {
  292. rows := make([]api.SnapshotDetails, len(objs))
  293. virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  294. manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  295. regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  296. encRows := manager.SEncryptedResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  297. storageIds := make([]string, len(objs))
  298. diskIds := make([]string, len(objs))
  299. snapshotIds := make([]string, len(objs))
  300. for i := range rows {
  301. rows[i] = api.SnapshotDetails{
  302. VirtualResourceDetails: virtRows[i],
  303. ManagedResourceInfo: manRows[i],
  304. CloudregionResourceInfo: regionRows[i],
  305. EncryptedResourceDetails: encRows[i],
  306. }
  307. snapshot := objs[i].(*SSnapshot)
  308. storageIds[i] = snapshot.StorageId
  309. diskIds[i] = snapshot.DiskId
  310. snapshotIds[i] = snapshot.Id
  311. }
  312. storages := map[string]SStorage{}
  313. err := db.FetchModelObjectsByIds(StorageManager, "id", storageIds, storages)
  314. if err != nil {
  315. log.Errorf("FetchModelObjectsByIds")
  316. return rows
  317. }
  318. disks := map[string]SDisk{}
  319. err = db.FetchModelObjectsByIds(DiskManager, "id", diskIds, disks)
  320. if err != nil {
  321. log.Errorf("FetchModelObjectsByIds")
  322. return rows
  323. }
  324. iss := []SInstanceSnapshotJoint{}
  325. err = InstanceSnapshotJointManager.Query().In("snapshot_id", snapshotIds).All(&iss)
  326. if err != nil {
  327. log.Errorf("query instance snapshot joint")
  328. return rows
  329. }
  330. issMap := map[string]bool{}
  331. for i := range iss {
  332. issMap[iss[i].SnapshotId] = true
  333. }
  334. q := GuestManager.Query()
  335. gds := GuestdiskManager.Query().SubQuery()
  336. sq := q.SubQuery()
  337. guests := sq.Query(
  338. sq.Field("id").Label("guest_id"),
  339. sq.Field("name").Label("guest_name"),
  340. sq.Field("status").Label("guest_status"),
  341. gds.Field("disk_id"),
  342. ).Join(gds, sqlchemy.Equals(gds.Field("guest_id"), sq.Field("id"))).Filter(sqlchemy.In(gds.Field("disk_id"), diskIds))
  343. guestdisks := []struct {
  344. DiskId string
  345. GuestId string
  346. GuestName string
  347. GuestStatus string
  348. }{}
  349. err = guests.All(&guestdisks)
  350. if err != nil {
  351. log.Errorf("guests.All")
  352. return rows
  353. }
  354. guestMap := map[string]struct {
  355. GuestId string
  356. GuestName string
  357. GuestStatus string
  358. }{}
  359. for _, gd := range guestdisks {
  360. guestMap[gd.DiskId] = struct {
  361. GuestId string
  362. GuestName string
  363. GuestStatus string
  364. }{
  365. GuestId: gd.GuestId,
  366. GuestName: gd.GuestName,
  367. GuestStatus: gd.GuestStatus,
  368. }
  369. }
  370. for i := range rows {
  371. if storage, ok := storages[storageIds[i]]; ok {
  372. rows[i].StorageType = storage.StorageType
  373. rows[i].Storage = storage.Name
  374. }
  375. if disk, ok := disks[diskIds[i]]; ok {
  376. rows[i].DiskStatus = disk.Status
  377. rows[i].DiskName = disk.Name
  378. }
  379. if guest, ok := guestMap[diskIds[i]]; ok {
  380. rows[i].GuestId = guest.GuestId
  381. rows[i].Guest = guest.GuestName
  382. rows[i].GuestStatus = guest.GuestStatus
  383. }
  384. rows[i].IsSubSnapshot, _ = issMap[snapshotIds[i]]
  385. }
  386. return rows
  387. }
  388. func (self *SSnapshot) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
  389. res := self.SVirtualResourceBase.GetShortDesc(ctx)
  390. res.Add(jsonutils.NewInt(int64(self.VirtualSize)), "virtual_size")
  391. res.Add(jsonutils.NewInt(int64(self.Size)), "size")
  392. res.Add(jsonutils.NewString(self.DiskId), "disk_id")
  393. disk, _ := self.GetDisk()
  394. if disk != nil {
  395. if guest := disk.GetGuest(); guest != nil {
  396. res.Add(jsonutils.NewString(guest.Id), "guest_id")
  397. }
  398. }
  399. info := self.getCloudProviderInfo()
  400. res.Update(jsonutils.Marshal(&info))
  401. return res
  402. }
  403. // 创建快照
  404. func (manager *SSnapshotManager) ValidateCreateData(
  405. ctx context.Context,
  406. userCred mcclient.TokenCredential,
  407. ownerId mcclient.IIdentityProvider,
  408. query jsonutils.JSONObject,
  409. input api.SnapshotCreateInput,
  410. ) (api.SnapshotCreateInput, error) {
  411. if input.NeedEncrypt() {
  412. return input, errors.Wrap(httperrors.ErrInputParameter, "encryption should not be set")
  413. }
  414. if len(input.DiskId) == 0 {
  415. return input, httperrors.NewMissingParameterError("disk_id")
  416. }
  417. _disk, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.DiskId)
  418. if err != nil {
  419. return input, err
  420. }
  421. disk := _disk.(*SDisk)
  422. if disk.Status != api.DISK_READY {
  423. return input, httperrors.NewInvalidStatusError("disk %s status is not %s", disk.Name, api.DISK_READY)
  424. }
  425. if len(disk.SnapshotId) > 0 {
  426. if disk.GetMetadata(ctx, "merge_snapshot", userCred) == "true" {
  427. return input, httperrors.NewBadRequestError("disk %s backing snapshot not merged", disk.Id)
  428. }
  429. }
  430. if len(disk.EncryptKeyId) > 0 {
  431. input.EncryptKeyId = &disk.EncryptKeyId
  432. input.EncryptedResourceCreateInput, err = manager.SEncryptedResourceManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EncryptedResourceCreateInput)
  433. if err != nil {
  434. return input, errors.Wrap(err, "SEncryptedResourceManager.ValidateCreateData")
  435. }
  436. }
  437. input.DiskType = disk.DiskType
  438. input.Size = disk.DiskSize
  439. input.VirtualSize = disk.DiskSize
  440. input.OsArch = disk.OsArch
  441. storage, _ := disk.GetStorage()
  442. if len(disk.ExternalId) == 0 {
  443. input.StorageId = disk.StorageId
  444. }
  445. input.ManagerId = storage.ManagerId
  446. region, err := storage.GetRegion()
  447. if err != nil {
  448. return input, err
  449. }
  450. input.CloudregionId = region.Id
  451. driver, err := storage.GetRegionDriver()
  452. if err != nil {
  453. return input, errors.Wrap(err, "storage.GetRegionDriver")
  454. }
  455. input.OutOfChain = driver.SnapshotIsOutOfChain(disk)
  456. err = driver.ValidateCreateSnapshotData(ctx, userCred, disk, storage, &input)
  457. if err != nil {
  458. return input, errors.Wrap(err, "driver.ValidateCreateSnapshotData")
  459. }
  460. input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
  461. if err != nil {
  462. return input, err
  463. }
  464. pendingUsage := &SRegionQuota{Snapshot: 1}
  465. keys, err := disk.GetQuotaKeys()
  466. if err != nil {
  467. return input, err
  468. }
  469. pendingUsage.SetKeys(keys.(SComputeResourceKeys).SRegionalCloudResourceKeys)
  470. err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
  471. if err != nil {
  472. return input, err
  473. }
  474. return input, nil
  475. }
  476. func (self *SSnapshot) CustomizeCreate(
  477. ctx context.Context,
  478. userCred mcclient.TokenCredential,
  479. ownerId mcclient.IIdentityProvider,
  480. query jsonutils.JSONObject,
  481. data jsonutils.JSONObject,
  482. ) error {
  483. // use disk's ownerId instead of default ownerId
  484. diskObj, err := DiskManager.FetchById(self.DiskId)
  485. if err != nil {
  486. return errors.Wrap(err, "DiskManager.FetchById")
  487. }
  488. ownerId = diskObj.(*SDisk).GetOwnerId()
  489. return self.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
  490. }
  491. func (snapshot *SSnapshot) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  492. snapshot.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
  493. pendingUsage := SRegionQuota{Snapshot: 1}
  494. keys := snapshot.GetQuotaKeys()
  495. pendingUsage.SetKeys(keys)
  496. err := quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, true)
  497. if err != nil {
  498. log.Errorf("quotas.CancelPendingUsage fail %s", err)
  499. }
  500. disk, err := snapshot.GetDisk()
  501. if err != nil {
  502. log.Errorf("unable to GetDisk: %s", err.Error())
  503. }
  504. err = disk.InheritTo(ctx, userCred, snapshot)
  505. if err != nil {
  506. log.Errorf("unable to inherit from disk %s to snapshot %s: %s", disk.GetId(), snapshot.GetId(), err.Error())
  507. }
  508. }
  509. func (manager *SSnapshotManager) OnCreateComplete(ctx context.Context, items []db.IModel, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data []jsonutils.JSONObject) {
  510. snapshot := items[0].(*SSnapshot)
  511. snapshot.StartSnapshotCreateTask(ctx, userCred, nil, "")
  512. }
  513. func (self *SSnapshot) StartSnapshotCreateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
  514. task, err := taskman.TaskManager.NewTask(ctx, "SnapshotCreateTask", self, userCred, params, parentTaskId, "", nil)
  515. if err != nil {
  516. return errors.Wrapf(err, "NewTask")
  517. }
  518. return task.ScheduleRun(nil)
  519. }
  520. func (self *SSnapshot) GetGuest() (*SGuest, error) {
  521. iDisk, err := DiskManager.FetchById(self.DiskId)
  522. if err != nil {
  523. return nil, err
  524. }
  525. disk := iDisk.(*SDisk)
  526. guests := disk.GetGuests()
  527. if len(guests) > 1 {
  528. return nil, fmt.Errorf("Snapshot disk attach mutil guest")
  529. } else if len(guests) == 1 {
  530. return &guests[0], nil
  531. } else {
  532. return nil, sql.ErrNoRows
  533. }
  534. }
  535. func (self *SSnapshot) GetDisk() (*SDisk, error) {
  536. iDisk, err := DiskManager.FetchById(self.DiskId)
  537. if err != nil {
  538. return nil, err
  539. }
  540. disk := iDisk.(*SDisk)
  541. return disk, nil
  542. }
  543. func (self *SSnapshot) GetHost() (*SHost, error) {
  544. iStorage, err := StorageManager.FetchById(self.StorageId)
  545. if err != nil {
  546. return nil, errors.Wrapf(err, "StorageManager.FetchById(%s)", self.StorageId)
  547. }
  548. storage := iStorage.(*SStorage)
  549. return storage.GetMasterHost()
  550. }
  551. func (self *SSnapshot) GetFuseUrl() (string, error) {
  552. iStorage, err := StorageManager.FetchById(self.StorageId)
  553. if err != nil {
  554. return "", errors.Wrapf(err, "StorageManager.FetchById(%s)", self.StorageId)
  555. }
  556. storage := iStorage.(*SStorage)
  557. if storage.StorageType != api.STORAGE_LOCAL && storage.StorageType != api.STORAGE_LVM {
  558. return "", nil
  559. }
  560. host, err := storage.GetMasterHost()
  561. if err != nil {
  562. return "", err
  563. }
  564. return fmt.Sprintf("%s/snapshots/%s/%s", host.GetFetchUrl(true), self.DiskId, self.Id), nil
  565. }
  566. func (self *SSnapshotManager) AddRefCount(snapshotId string, count int) {
  567. iSnapshot, _ := self.FetchById(snapshotId)
  568. if iSnapshot != nil {
  569. snapshot := iSnapshot.(*SSnapshot)
  570. _, err := db.Update(snapshot, func() error {
  571. snapshot.RefCount += count
  572. return nil
  573. })
  574. if err != nil {
  575. log.Errorf("Snapshot add refence count error: %s", err)
  576. }
  577. }
  578. }
  579. func (self *SSnapshotManager) GetDiskSnapshotsByCreate(diskId, createdBy string) []SSnapshot {
  580. dest := make([]SSnapshot, 0)
  581. q := self.Query().SubQuery()
  582. sq := q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
  583. sqlchemy.Equals(q.Field("created_by"), createdBy),
  584. sqlchemy.Equals(q.Field("fake_deleted"), false)))
  585. err := db.FetchModelObjects(self, sq, &dest)
  586. if err != nil {
  587. log.Errorf("GetDiskSnapshots error: %s", err)
  588. return nil
  589. }
  590. return dest
  591. }
  592. func (self *SSnapshotManager) GetDiskSnapshots(diskId string) []SSnapshot {
  593. dest := make([]SSnapshot, 0)
  594. q := self.Query().Equals("disk_id", diskId).Asc("created_at")
  595. err := db.FetchModelObjects(self, q, &dest)
  596. if err != nil {
  597. log.Errorf("GetDiskSnapshots error: %s", err)
  598. return nil
  599. }
  600. return dest
  601. }
  602. func (self *SSnapshotManager) GetDiskManualSnapshotCount(diskId string) (int, error) {
  603. return self.Query().Equals("disk_id", diskId).Equals("fake_deleted", false).CountWithError()
  604. }
  605. func (self *SSnapshotManager) GetDiskFirstSnapshot(diskId string) *SSnapshot {
  606. dest := &SSnapshot{}
  607. q := self.Query().SubQuery()
  608. err := q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
  609. sqlchemy.In(q.Field("status"), []string{api.SNAPSHOT_READY, api.SNAPSHOT_DELETING}),
  610. sqlchemy.Equals(q.Field("out_of_chain"), false))).Asc("created_at").First(dest)
  611. if err != nil {
  612. log.Errorf("Get Disk First snapshot error: %s", err.Error())
  613. return nil
  614. }
  615. dest.SetModelManager(self, dest)
  616. return dest
  617. }
  618. func (self *SSnapshotManager) GetDiskSnapshotCount(diskId string) (int, error) {
  619. q := self.Query().SubQuery()
  620. return q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
  621. sqlchemy.Equals(q.Field("fake_deleted"), false))).CountWithError()
  622. }
  623. func (self *SSnapshotManager) CreateSnapshot(ctx context.Context, owner mcclient.IIdentityProvider,
  624. createdBy, diskId, guestId, location, name string, retentionDay int, isSystem bool, diskBackupId string) (*SSnapshot, error) {
  625. iDisk, err := DiskManager.FetchById(diskId)
  626. if err != nil {
  627. return nil, err
  628. }
  629. disk := iDisk.(*SDisk)
  630. storage, _ := disk.GetStorage()
  631. snapshot := &SSnapshot{}
  632. snapshot.SetModelManager(self, snapshot)
  633. snapshot.ProjectId = owner.GetProjectId()
  634. snapshot.DomainId = owner.GetProjectDomainId()
  635. snapshot.DiskId = disk.Id
  636. if len(disk.ExternalId) == 0 {
  637. snapshot.StorageId = disk.StorageId
  638. }
  639. // inherit encrypt_key_id
  640. snapshot.EncryptKeyId = disk.EncryptKeyId
  641. driver, err := storage.GetRegionDriver()
  642. if err != nil {
  643. return nil, err
  644. }
  645. snapshot.OutOfChain = driver.SnapshotIsOutOfChain(disk)
  646. snapshot.VirtualSize = disk.DiskSize
  647. snapshot.DiskType = disk.DiskType
  648. snapshot.Location = location
  649. snapshot.CreatedBy = createdBy
  650. snapshot.ManagerId = storage.ManagerId
  651. if cloudregion, _ := storage.GetRegion(); cloudregion != nil {
  652. snapshot.CloudregionId = cloudregion.GetId()
  653. }
  654. snapshot.Name = name
  655. snapshot.Status = api.SNAPSHOT_CREATING
  656. if retentionDay > 0 {
  657. snapshot.ExpiredAt = time.Now().AddDate(0, 0, retentionDay)
  658. }
  659. snapshot.IsSystem = isSystem
  660. snapshot.DiskBackupId = diskBackupId
  661. err = SnapshotManager.TableSpec().Insert(ctx, snapshot)
  662. if err != nil {
  663. return nil, err
  664. }
  665. return snapshot, nil
  666. }
  667. func (self *SSnapshot) StartSnapshotDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, reloadDisk bool, parentTaskId string, deleteSnapshotTotalCnt int, deletedSnapshotCnt int) error {
  668. params := jsonutils.NewDict()
  669. params.Set("reload_disk", jsonutils.NewBool(reloadDisk))
  670. if deleteSnapshotTotalCnt <= 0 {
  671. deleteSnapshotTotalCnt = 1
  672. }
  673. params.Set("snapshot_total_count", jsonutils.NewInt(int64(deleteSnapshotTotalCnt)))
  674. params.Set("deleted_snapshot_count", jsonutils.NewInt(int64(deletedSnapshotCnt)))
  675. self.SetStatus(ctx, userCred, api.SNAPSHOT_DELETING, "")
  676. task, err := taskman.TaskManager.NewTask(ctx, "SnapshotDeleteTask", self, userCred, params, parentTaskId, "", nil)
  677. if err != nil {
  678. log.Errorln(err)
  679. return err
  680. } else {
  681. task.ScheduleRun(nil)
  682. }
  683. return nil
  684. }
  685. func (self *SSnapshot) ValidateDeleteCondition(ctx context.Context, info *api.SnapshotDetails) error {
  686. if self.Status == api.SNAPSHOT_DELETING {
  687. return httperrors.NewBadRequestError("Cannot delete snapshot in status %s", self.Status)
  688. }
  689. if gotypes.IsNil(info) {
  690. count, err := InstanceSnapshotJointManager.Query().Equals("snapshot_id", self.Id).CountWithError()
  691. if err != nil {
  692. return httperrors.NewInternalServerError("Fetch instance snapshot error %s", err)
  693. }
  694. if count > 0 {
  695. return httperrors.NewBadRequestError("snapshot referenced by instance snapshot")
  696. }
  697. if disk, err := self.GetDisk(); err == nil {
  698. if disk.Status == api.DISK_RESET {
  699. return httperrors.NewBadRequestError("Cannot delete snapshot on disk reset")
  700. }
  701. }
  702. } else {
  703. if info.IsSubSnapshot {
  704. return httperrors.NewBadRequestError("snapshot referenced by instance snapshot")
  705. }
  706. if info.DiskStatus == api.DISK_RESET {
  707. return httperrors.NewBadRequestError("Cannot delete snapshot on disk reset")
  708. }
  709. }
  710. driver := self.GetRegionDriver()
  711. if driver != nil {
  712. return driver.ValidateSnapshotDelete(ctx, self)
  713. }
  714. return nil
  715. }
  716. func (self *SSnapshot) GetStorage() *SStorage {
  717. return StorageManager.FetchStorageById(self.StorageId)
  718. }
  719. func (self *SSnapshot) GetStorageType() string {
  720. if storage := self.GetStorage(); storage != nil {
  721. return storage.StorageType
  722. }
  723. return ""
  724. }
  725. func (self *SSnapshot) GetRegionDriver() IRegionDriver {
  726. cloudRegion, _ := self.GetRegion()
  727. if cloudRegion != nil {
  728. return cloudRegion.GetDriver()
  729. }
  730. return nil
  731. }
  732. func (self *SSnapshot) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  733. return self.StartSnapshotDeleteTask(ctx, userCred, false, "", 0, 0)
  734. }
  735. // +onecloud:swagger-gen-ignore
  736. func (self *SSnapshot) PerformDeleted(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  737. _, err := db.Update(self, func() error {
  738. self.OutOfChain = true
  739. return nil
  740. })
  741. if err != nil {
  742. return nil, err
  743. }
  744. err = self.StartSnapshotDeleteTask(ctx, userCred, true, "", 0, 0)
  745. return nil, err
  746. }
  747. // 同步快照状态
  748. func (self *SSnapshot) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SnapshotSyncstatusInput) (jsonutils.JSONObject, error) {
  749. var openTask = true
  750. count, err := taskman.TaskManager.QueryTasksOfObject(self, time.Now().Add(-3*time.Minute), &openTask).CountWithError()
  751. if err != nil {
  752. return nil, err
  753. }
  754. if count > 0 {
  755. return nil, httperrors.NewBadRequestError("Snapshot has %d task active, can't sync status", count)
  756. }
  757. return nil, StartResourceSyncStatusTask(ctx, userCred, self, "SnapshotSyncstatusTask", "")
  758. }
  759. func (self *SSnapshotManager) GetConvertSnapshot(deleteSnapshot *SSnapshot) (*SSnapshot, error) {
  760. dest := &SSnapshot{}
  761. q := self.Query()
  762. err := q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), deleteSnapshot.DiskId),
  763. sqlchemy.In(q.Field("status"), []string{api.SNAPSHOT_READY, api.SNAPSHOT_DELETING}),
  764. sqlchemy.Equals(q.Field("out_of_chain"), false),
  765. sqlchemy.GT(q.Field("created_at"), deleteSnapshot.CreatedAt))).
  766. Asc("created_at").First(dest)
  767. if err != nil {
  768. return nil, err
  769. }
  770. return dest, nil
  771. }
  772. // +onecloud:swagger-gen-ignore
  773. func (self *SSnapshotManager) PerformDeleteDiskSnapshots(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SnapshotDeleteDiskSnapshotsInput) (jsonutils.JSONObject, error) {
  774. disk, err := DiskManager.FetchById(input.DiskId)
  775. if disk != nil {
  776. return nil, httperrors.NewBadRequestError("Cannot Delete disk %s snapshots, disk exist", input.DiskId)
  777. }
  778. snapshots := self.GetDiskSnapshots(input.DiskId)
  779. if snapshots == nil || len(snapshots) == 0 {
  780. return nil, httperrors.NewNotFoundError("Disk %s dose not have snapshot", input.DiskId)
  781. }
  782. snapshotIds := []string{}
  783. for i := 0; i < len(snapshots); i++ {
  784. if snapshots[i].FakeDeleted == false {
  785. return nil, httperrors.NewBadRequestError("Can not delete disk snapshots, have manual snapshot")
  786. }
  787. snapshotIds = append(snapshotIds, snapshots[i].Id)
  788. }
  789. err = snapshots[0].StartSnapshotsDeleteTask(ctx, userCred, "", snapshotIds)
  790. return nil, err
  791. }
  792. func (self *SSnapshot) StartSnapshotsDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, snapshotIds []string) error {
  793. data := jsonutils.NewDict()
  794. data.Set("snapshot_ids", jsonutils.NewStringArray(snapshotIds))
  795. task, err := taskman.TaskManager.NewTask(ctx, "BatchSnapshotsDeleteTask", self, userCred, nil, parentTaskId, "", nil)
  796. if err != nil {
  797. log.Errorln(err)
  798. return err
  799. } else {
  800. task.ScheduleRun(nil)
  801. }
  802. return nil
  803. }
  804. func (self *SSnapshot) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  805. if len(self.DiskId) > 0 {
  806. storage := self.GetStorage()
  807. if storage != nil && storage.StorageType == api.STORAGE_RBD {
  808. disk := DiskManager.FetchDiskById(self.DiskId)
  809. if disk != nil {
  810. cnt, err := disk.GetGuestsCount()
  811. if err == nil {
  812. val := disk.GetMetadata(ctx, "disk_delete_after_snapshots", userCred)
  813. if cnt == 0 && val == "true" {
  814. disk.StartDiskDeleteTask(ctx, userCred, "", false, true, false)
  815. }
  816. } else {
  817. // very unlikely
  818. log.Errorf("disk.GetGuestsCount fail %s", err)
  819. }
  820. } else {
  821. backingDisks, err := self.GetBackingDisks()
  822. if err != nil {
  823. // very unlikely
  824. log.Errorf("self.GetBackingDisks fail %s", err)
  825. } else {
  826. storage.StartDeleteRbdDisks(ctx, userCred, backingDisks)
  827. }
  828. }
  829. }
  830. }
  831. return db.DeleteModel(ctx, userCred, self)
  832. }
  833. func (self *SSnapshot) GetBackingDisks() ([]string, error) {
  834. count, err := SnapshotManager.Query().Equals("disk_id", self.DiskId).IsNullOrEmpty("backing_disk_id").CountWithError()
  835. if err != nil {
  836. return nil, err
  837. }
  838. if count > 0 {
  839. return nil, nil
  840. } else {
  841. sps := make([]SSnapshot, 0)
  842. err := SnapshotManager.Query().Equals("disk_id", self.DiskId).All(&sps)
  843. if err != nil {
  844. return nil, err
  845. }
  846. res := make([]string, 0)
  847. for i := 0; i < len(sps); i++ {
  848. if len(sps[i].BackingDiskId) > 0 && !utils.IsInStringArray(sps[i].BackingDiskId, res) {
  849. res = append(res, sps[i].BackingDiskId)
  850. }
  851. }
  852. res = append(res, self.DiskId)
  853. return res, nil
  854. }
  855. }
  856. func (self *SSnapshot) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  857. return nil
  858. }
  859. func (self *SSnapshotManager) DeleteDiskSnapshots(ctx context.Context, userCred mcclient.TokenCredential, diskId string) error {
  860. snapshots := self.GetDiskSnapshots(diskId)
  861. for i := 0; i < len(snapshots); i++ {
  862. if err := snapshots[i].RealDelete(ctx, userCred); err != nil {
  863. return errors.Wrap(err, "delete snapshot")
  864. }
  865. }
  866. return nil
  867. }
  868. 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) {
  869. q := SnapshotManager.Query()
  870. switch scope {
  871. case rbacscope.ScopeSystem:
  872. case rbacscope.ScopeDomain:
  873. q = q.Equals("domain_id", ownerId.GetProjectDomainId())
  874. case rbacscope.ScopeProject:
  875. q = q.Equals("tenant_id", ownerId.GetProjectId())
  876. }
  877. q = db.ObjectIdQueryWithPolicyResult(ctx, q, SnapshotManager, policyResult)
  878. q = RangeObjectsFilter(q, rangeObjs, q.Field("cloudregion_id"), nil, q.Field("manager_id"), nil, nil)
  879. q = CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv)
  880. q = q.Equals("created_by", api.SNAPSHOT_MANUAL)
  881. q = q.Equals("fake_deleted", false)
  882. return q.CountWithError()
  883. }
  884. func (self *SSnapshot) syncRemoveCloudSnapshot(ctx context.Context, userCred mcclient.TokenCredential) error {
  885. lockman.LockObject(ctx, self)
  886. defer lockman.ReleaseObject(ctx, self)
  887. err := self.ValidateDeleteCondition(ctx, nil)
  888. if err != nil {
  889. err = self.SetStatus(ctx, userCred, api.SNAPSHOT_UNKNOWN, "sync to delete")
  890. } else {
  891. err = self.RealDelete(ctx, userCred)
  892. }
  893. return err
  894. }
  895. // Only sync snapshot status
  896. func (self *SSnapshot) SyncWithCloudSnapshot(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudSnapshot, syncOwnerId mcclient.IIdentityProvider, region *SCloudregion) error {
  897. diff, err := db.UpdateWithLock(ctx, self, func() error {
  898. if options.Options.EnableSyncName {
  899. newName, _ := db.GenerateAlterName(self, ext.GetName())
  900. if len(newName) > 0 {
  901. self.Name = newName
  902. }
  903. }
  904. self.Status = ext.GetStatus()
  905. self.DiskType = ext.GetDiskType()
  906. self.VirtualSize = int(ext.GetSizeMb())
  907. self.Size = int(ext.GetSizeMb())
  908. disk, _ := self.GetDisk()
  909. if gotypes.IsNil(disk) && len(ext.GetDiskId()) > 0 {
  910. disk, err := db.FetchByExternalIdAndManagerId(DiskManager, ext.GetDiskId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  911. sq := StorageManager.Query().SubQuery()
  912. return q.Join(sq, sqlchemy.Equals(q.Field("storage_id"), sq.Field("id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), self.ManagerId))
  913. })
  914. if err != nil {
  915. log.Errorf("snapshot %s missing disk?", self.Name)
  916. } else {
  917. self.DiskId = disk.GetId()
  918. }
  919. }
  920. self.CloudregionId = region.Id
  921. return nil
  922. })
  923. if err != nil {
  924. log.Errorf("SyncWithCloudSnapshot fail %s", err)
  925. return err
  926. }
  927. db.OpsLog.LogSyncUpdate(self, diff, userCred)
  928. if account := self.GetCloudaccount(); account != nil {
  929. syncVirtualResourceMetadata(ctx, userCred, self, ext, account.ReadOnly)
  930. }
  931. // bugfix for now:
  932. disk, _ := self.GetDisk()
  933. if disk != nil {
  934. self.SyncCloudProjectId(userCred, disk.GetOwnerId())
  935. } else {
  936. SyncCloudProject(ctx, userCred, self, syncOwnerId, ext, self.GetCloudprovider())
  937. }
  938. return nil
  939. }
  940. func (manager *SSnapshotManager) newFromCloudSnapshot(ctx context.Context, userCred mcclient.TokenCredential, extSnapshot cloudprovider.ICloudSnapshot, region *SCloudregion, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider) (*SSnapshot, error) {
  941. snapshot := SSnapshot{}
  942. snapshot.SetModelManager(manager, &snapshot)
  943. snapshot.Status = extSnapshot.GetStatus()
  944. snapshot.ExternalId = extSnapshot.GetGlobalId()
  945. var localDisk *SDisk
  946. if len(extSnapshot.GetDiskId()) > 0 {
  947. disk, err := db.FetchByExternalIdAndManagerId(DiskManager, extSnapshot.GetDiskId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  948. sq := StorageManager.Query().SubQuery()
  949. return q.Join(sq, sqlchemy.Equals(q.Field("storage_id"), sq.Field("id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), provider.Id))
  950. })
  951. if err != nil {
  952. log.Errorf("snapshot %s missing disk?", snapshot.Name)
  953. } else {
  954. snapshot.DiskId = disk.GetId()
  955. localDisk = disk.(*SDisk)
  956. }
  957. }
  958. snapshot.DiskType = extSnapshot.GetDiskType()
  959. snapshot.VirtualSize = int(extSnapshot.GetSizeMb())
  960. snapshot.Size = int(extSnapshot.GetSizeMb())
  961. snapshot.ManagerId = provider.Id
  962. snapshot.CloudregionId = region.Id
  963. var err = func() error {
  964. lockman.LockRawObject(ctx, manager.Keyword(), "name")
  965. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
  966. newName, err := db.GenerateName(ctx, manager, syncOwnerId, extSnapshot.GetName())
  967. if err != nil {
  968. return err
  969. }
  970. snapshot.Name = newName
  971. return manager.TableSpec().Insert(ctx, &snapshot)
  972. }()
  973. if err != nil {
  974. return nil, errors.Wrapf(err, "Insert")
  975. }
  976. syncVirtualResourceMetadata(ctx, userCred, &snapshot, extSnapshot, false)
  977. // bugfix for now:
  978. if localDisk != nil {
  979. snapshot.SyncCloudProjectId(userCred, localDisk.GetOwnerId())
  980. } else {
  981. SyncCloudProject(ctx, userCred, &snapshot, syncOwnerId, extSnapshot, provider)
  982. }
  983. db.OpsLog.LogEvent(&snapshot, db.ACT_CREATE, snapshot.GetShortDesc(ctx), userCred)
  984. return &snapshot, nil
  985. }
  986. func (manager *SSnapshotManager) getProviderSnapshotsByRegion(region *SCloudregion, provider *SCloudprovider) ([]SSnapshot, error) {
  987. if region == nil || provider == nil {
  988. return nil, fmt.Errorf("Region is nil or provider is nil")
  989. }
  990. snapshots := make([]SSnapshot, 0)
  991. q := manager.Query().Equals("cloudregion_id", region.Id).Equals("manager_id", provider.Id)
  992. err := db.FetchModelObjects(manager, q, &snapshots)
  993. if err != nil {
  994. return nil, err
  995. }
  996. return snapshots, nil
  997. }
  998. func (manager *SSnapshotManager) SyncSnapshots(
  999. ctx context.Context,
  1000. userCred mcclient.TokenCredential,
  1001. provider *SCloudprovider,
  1002. region *SCloudregion,
  1003. snapshots []cloudprovider.ICloudSnapshot,
  1004. syncOwnerId mcclient.IIdentityProvider,
  1005. xor bool,
  1006. ) compare.SyncResult {
  1007. lockman.LockRawObject(ctx, manager.Keyword(), fmt.Sprintf("%s-%s", provider.Id, region.Id))
  1008. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), fmt.Sprintf("%s-%s", provider.Id, region.Id))
  1009. syncResult := compare.SyncResult{}
  1010. dbSnapshots, err := manager.getProviderSnapshotsByRegion(region, provider)
  1011. if err != nil {
  1012. syncResult.Error(err)
  1013. return syncResult
  1014. }
  1015. removed := make([]SSnapshot, 0)
  1016. commondb := make([]SSnapshot, 0)
  1017. commonext := make([]cloudprovider.ICloudSnapshot, 0)
  1018. added := make([]cloudprovider.ICloudSnapshot, 0)
  1019. err = compare.CompareSets(dbSnapshots, snapshots, &removed, &commondb, &commonext, &added)
  1020. if err != nil {
  1021. syncResult.Error(err)
  1022. return syncResult
  1023. }
  1024. for i := 0; i < len(removed); i += 1 {
  1025. err = removed[i].syncRemoveCloudSnapshot(ctx, userCred)
  1026. if err != nil {
  1027. syncResult.DeleteError(err)
  1028. continue
  1029. }
  1030. syncResult.Delete()
  1031. }
  1032. if !xor {
  1033. for i := 0; i < len(commondb); i += 1 {
  1034. err = commondb[i].SyncWithCloudSnapshot(ctx, userCred, commonext[i], syncOwnerId, region)
  1035. if err != nil {
  1036. syncResult.UpdateError(err)
  1037. continue
  1038. }
  1039. syncResult.Update()
  1040. }
  1041. }
  1042. for i := 0; i < len(added); i += 1 {
  1043. _, err := manager.newFromCloudSnapshot(ctx, userCred, added[i], region, syncOwnerId, provider)
  1044. if err != nil {
  1045. syncResult.AddError(err)
  1046. continue
  1047. }
  1048. syncResult.Add()
  1049. }
  1050. return syncResult
  1051. }
  1052. func (self *SSnapshot) GetISnapshotRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) {
  1053. provider, err := self.GetDriver(ctx)
  1054. if err != nil {
  1055. return nil, err
  1056. }
  1057. region, err := self.GetRegion()
  1058. if err != nil {
  1059. return nil, err
  1060. }
  1061. return provider.GetIRegionById(region.GetExternalId())
  1062. }
  1063. // +onecloud:swagger-gen-ignore
  1064. func (self *SSnapshot) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1065. err := self.GetRegionDriver().ValidateSnapshotDelete(ctx, self)
  1066. if err != nil {
  1067. return nil, err
  1068. }
  1069. provider := self.GetCloudprovider()
  1070. if provider != nil {
  1071. if provider.GetEnabled() {
  1072. return nil, httperrors.NewInvalidStatusError("Cannot purge snapshot on enabled cloud provider")
  1073. }
  1074. }
  1075. err = self.RealDelete(ctx, userCred)
  1076. return nil, err
  1077. }
  1078. func (self *SSnapshot) getCloudProviderInfo() SCloudProviderInfo {
  1079. region, _ := self.GetRegion()
  1080. provider := self.GetCloudprovider()
  1081. return MakeCloudProviderInfo(region, nil, provider)
  1082. }
  1083. func (manager *SSnapshotManager) GetResourceCount() ([]db.SScopeResourceCount, error) {
  1084. virts := manager.Query().IsFalse("fake_deleted")
  1085. return db.CalculateResourceCount(virts, "tenant_id")
  1086. }
  1087. var SnapshotCleanupTaskRunning int32 = 0
  1088. func SnapshotCleanupTaskIsRunning() bool {
  1089. return atomic.LoadInt32(&SnapshotCleanupTaskRunning) == 1
  1090. }
  1091. func (manager *SSnapshotManager) CleanupSnapshots(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  1092. if SnapshotCleanupTaskIsRunning() {
  1093. log.Errorf("Previous CleanupSnapshots tasks still running !!!")
  1094. return
  1095. }
  1096. var now = time.Now()
  1097. var snapshot = new(SSnapshot)
  1098. err := manager.Query().
  1099. Equals("fake_deleted", false).
  1100. Equals("created_by", api.SNAPSHOT_AUTO).
  1101. LE("expired_at", now).First(snapshot)
  1102. if err != nil && err != sql.ErrNoRows {
  1103. log.Errorf("Cleanup snapshots job fetch snapshot failed %s", err)
  1104. return
  1105. } else if err == sql.ErrNoRows {
  1106. log.Infof("No snapshot need to clean ......")
  1107. return
  1108. }
  1109. snapshot.SetModelManager(manager, snapshot)
  1110. region, _ := snapshot.GetRegion()
  1111. if err = manager.StartSnapshotCleanupTask(ctx, userCred, region, now); err != nil {
  1112. log.Errorf("Start snaphsot cleanup task failed %s", err)
  1113. return
  1114. }
  1115. sq := manager.Query().Equals("status", api.SNAPSHOT_READY).Equals("created_by", api.SNAPSHOT_AUTO).Equals("fake_deleted", false).SubQuery()
  1116. disks := []struct {
  1117. DiskCnt int
  1118. DiskId string
  1119. }{}
  1120. q := sq.Query(
  1121. sqlchemy.COUNT("disk_cnt", sq.Field("disk_id")),
  1122. sq.Field("disk_id"),
  1123. ).GroupBy(sq.Field("disk_id"))
  1124. err = q.All(&disks)
  1125. if err != nil {
  1126. log.Errorf("Cleanup snapshots job fetch disk count failed %s", err)
  1127. return
  1128. }
  1129. diskCount := map[string]int{}
  1130. for i := range disks {
  1131. diskCount[disks[i].DiskId] = disks[i].DiskCnt
  1132. }
  1133. {
  1134. sq = SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_DISK).GT("retention_count", 0).SubQuery()
  1135. spd := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK).SubQuery()
  1136. q = sq.Query(
  1137. sq.Field("retention_count"),
  1138. spd.Field("resource_id").Label("disk_id"),
  1139. )
  1140. q = q.Join(spd, sqlchemy.Equals(q.Field("id"), spd.Field("snapshotpolicy_id")))
  1141. diskRetentions := []struct {
  1142. DiskId string
  1143. RetentionCount int
  1144. }{}
  1145. err = q.All(&diskRetentions)
  1146. if err != nil {
  1147. log.Errorf("Cleanup snapshots job fetch disk retentions failed %s", err)
  1148. return
  1149. }
  1150. diskRetentionMap := map[string]int{}
  1151. for i := range diskRetentions {
  1152. if _, ok := diskRetentionMap[diskRetentions[i].DiskId]; !ok {
  1153. diskRetentionMap[diskRetentions[i].DiskId] = diskRetentions[i].RetentionCount
  1154. }
  1155. // 取最小保留个数
  1156. if diskRetentionMap[diskRetentions[i].DiskId] > diskRetentions[i].RetentionCount {
  1157. diskRetentionMap[diskRetentions[i].DiskId] = diskRetentions[i].RetentionCount
  1158. }
  1159. }
  1160. for diskId, retentionCnt := range diskRetentionMap {
  1161. if cnt, ok := diskCount[diskId]; ok && cnt > retentionCnt {
  1162. manager.startCleanupRetentionCount(ctx, userCred, diskId, cnt-retentionCnt)
  1163. }
  1164. }
  1165. }
  1166. }
  1167. func (manager *SSnapshotManager) startCleanupRetentionCount(ctx context.Context, userCred mcclient.TokenCredential, diskId string, cnt int) error {
  1168. q := manager.Query().Equals("disk_id", diskId).Equals("created_by", api.SNAPSHOT_AUTO).Asc("created_at").Limit(cnt)
  1169. snapshots := []SSnapshot{}
  1170. err := db.FetchModelObjects(manager, q, &snapshots)
  1171. if err != nil {
  1172. return errors.Wrapf(err, "FetchModelObjects")
  1173. }
  1174. for i := range snapshots {
  1175. snapshots[i].StartSnapshotDeleteTask(ctx, userCred, false, "", 0, 0)
  1176. }
  1177. return nil
  1178. }
  1179. func (manager *SSnapshotManager) StartSnapshotCleanupTask(
  1180. ctx context.Context, userCred mcclient.TokenCredential,
  1181. region *SCloudregion, now time.Time,
  1182. ) error {
  1183. params := jsonutils.NewDict()
  1184. params.Set("tick", jsonutils.NewTimeString(now))
  1185. task, err := taskman.TaskManager.NewTask(ctx, "SnapshotCleanupTask", region, userCred, params, "", "", nil)
  1186. if err != nil {
  1187. return err
  1188. }
  1189. task.ScheduleRun(nil)
  1190. return nil
  1191. }
  1192. func (self *SSnapshot) GetQuotaKeys() quotas.IQuotaKeys {
  1193. region, _ := self.GetRegion()
  1194. return fetchRegionalQuotaKeys(
  1195. rbacscope.ScopeProject,
  1196. self.GetOwnerId(),
  1197. region,
  1198. self.GetCloudprovider(),
  1199. )
  1200. }
  1201. func (snapshot *SSnapshot) GetUsages() []db.IUsage {
  1202. if snapshot.PendingDeleted || snapshot.Deleted {
  1203. return nil
  1204. }
  1205. usage := SRegionQuota{Snapshot: 1}
  1206. keys := snapshot.GetQuotaKeys()
  1207. usage.SetKeys(keys)
  1208. return []db.IUsage{
  1209. &usage,
  1210. }
  1211. }
  1212. func (manager *SSnapshotManager) ListItemExportKeys(ctx context.Context,
  1213. q *sqlchemy.SQuery,
  1214. userCred mcclient.TokenCredential,
  1215. keys stringutils2.SSortedStrings,
  1216. ) (*sqlchemy.SQuery, error) {
  1217. var err error
  1218. q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  1219. if err != nil {
  1220. return nil, err
  1221. }
  1222. if keys.Contains("disk") {
  1223. q, err = manager.SDiskResourceBaseManager.ListItemExportKeys(ctx, q, userCred, stringutils2.NewSortedStrings([]string{"disk"}))
  1224. if err != nil {
  1225. return nil, errors.Wrap(err, "SDiskResourceBaseManager.ListItemExportKeys")
  1226. }
  1227. }
  1228. if keys.ContainsAny(manager.SStorageResourceBaseManager.GetExportKeys()...) {
  1229. q, err = manager.SStorageResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  1230. if err != nil {
  1231. return nil, errors.Wrap(err, "SStorageResourceBaseManager.ListItemExportKeys")
  1232. }
  1233. }
  1234. return q, nil
  1235. }
  1236. func (manager *SSnapshotManager) DataCleaning(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  1237. err := dataCleaning(manager.TableSpec().Name())
  1238. if err != nil {
  1239. log.Errorf("************* %s:dataCleaning error:%s ************", manager.TableSpec().Name(), err.Error())
  1240. }
  1241. }
  1242. func dataCleaning(tableName string) error {
  1243. if options.Options.KeepDeletedSnapshotDays <= 0 {
  1244. return nil
  1245. }
  1246. _, err := sqlchemy.GetDB().Exec(
  1247. fmt.Sprintf(
  1248. "delete from %s where deleted = 1 and deleted_at < ?",
  1249. tableName,
  1250. ), time.Now().AddDate(0, 0, -options.Options.KeepDeletedSnapshotDays),
  1251. )
  1252. if err != nil {
  1253. return errors.Wrapf(err, "unable to delete expired data in %q", tableName)
  1254. }
  1255. log.Infof("delete expired data in %q successfully", tableName)
  1256. return nil
  1257. }