instance_snapshots.go 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  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. "time"
  20. "yunion.io/x/cloudmux/pkg/cloudprovider"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/util/rbacscope"
  25. "yunion.io/x/pkg/utils"
  26. "yunion.io/x/sqlchemy"
  27. api "yunion.io/x/onecloud/pkg/apis/compute"
  28. schedapi "yunion.io/x/onecloud/pkg/apis/scheduler"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  30. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  32. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  33. "yunion.io/x/onecloud/pkg/compute/options"
  34. "yunion.io/x/onecloud/pkg/httperrors"
  35. "yunion.io/x/onecloud/pkg/mcclient"
  36. "yunion.io/x/onecloud/pkg/util/rbacutils"
  37. "yunion.io/x/onecloud/pkg/util/stringutils2"
  38. )
  39. func init() {
  40. InstanceSnapshotManager = &SInstanceSnapshotManager{
  41. SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
  42. SInstanceSnapshot{},
  43. "instance_snapshots_tbl",
  44. "instance_snapshot",
  45. "instance_snapshots",
  46. ),
  47. }
  48. InstanceSnapshotManager.SetVirtualObject(InstanceSnapshotManager)
  49. }
  50. type SInstanceSnapshot struct {
  51. db.SVirtualResourceBase
  52. db.SExternalizedResourceBase
  53. SManagedResourceBase
  54. SCloudregionResourceBase
  55. db.SMultiArchResourceBase
  56. db.SEncryptedResource
  57. // 云主机Id
  58. GuestId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required" index:"true"`
  59. // 云主机配置
  60. ServerConfig jsonutils.JSONObject `nullable:"true" list:"user"`
  61. // 云主机标签
  62. ServerMetadata jsonutils.JSONObject `nullable:"true" list:"user"`
  63. // 是否自动删除
  64. AutoDelete bool `default:"false" update:"user" list:"user"`
  65. // 引用次数
  66. RefCount int `default:"0" list:"user"`
  67. // 安全组
  68. SecGroups jsonutils.JSONObject `nullable:"true" list:"user"`
  69. // 秘钥Id
  70. KeypairId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
  71. // 操作系统类型
  72. OsType string `width:"36" charset:"ascii" nullable:"true" list:"user"`
  73. // 套餐名称
  74. InstanceType string `width:"64" charset:"utf8" nullable:"true" list:"user" create:"optional"`
  75. // 主机快照磁盘容量和
  76. // SizeMb int `nullable:"false" list:"user"`
  77. // 镜像ID
  78. ImageId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
  79. // 是否保存内存
  80. WithMemory bool `default:"false" get:"user" list:"user"`
  81. // 内存文件大小
  82. MemorySizeKB int `nullable:"true" get:"user" list:"user" old_name:"memory_size_mb"`
  83. // 内存文件所在宿主机
  84. MemoryFileHostId string `width:"36" charset:"ascii" nullable:"true" get:"user" list:"user"`
  85. // 内存文件路径
  86. MemoryFilePath string `width:"512" charset:"utf8" nullable:"true" get:"user" list:"user"`
  87. // 内存文件校验和
  88. MemoryFileChecksum string `width:"32" charset:"ascii" nullable:"true" get:"user" list:"user"`
  89. }
  90. type SInstanceSnapshotManager struct {
  91. db.SVirtualResourceBaseManager
  92. db.SExternalizedResourceBaseManager
  93. SManagedResourceBaseManager
  94. SCloudregionResourceBaseManager
  95. db.SMultiArchResourceBaseManager
  96. db.SEncryptedResourceManager
  97. }
  98. var InstanceSnapshotManager *SInstanceSnapshotManager
  99. // 主机快照列表
  100. func (manager *SInstanceSnapshotManager) ListItemFilter(
  101. ctx context.Context,
  102. q *sqlchemy.SQuery,
  103. userCred mcclient.TokenCredential,
  104. query api.InstanceSnapshotListInput,
  105. ) (*sqlchemy.SQuery, error) {
  106. q, err := manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
  107. if err != nil {
  108. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
  109. }
  110. q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
  111. if err != nil {
  112. return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
  113. }
  114. q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
  115. if err != nil {
  116. return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
  117. }
  118. q, err = manager.SMultiArchResourceBaseManager.ListItemFilter(ctx, q, userCred, query.MultiArchResourceBaseListInput)
  119. if err != nil {
  120. return nil, errors.Wrap(err, "SMultiArchResourceBaseManager.ListItemFilter")
  121. }
  122. guestStr := query.ServerId
  123. if len(guestStr) > 0 {
  124. guestObj, err := GuestManager.FetchByIdOrName(ctx, userCred, guestStr)
  125. if err != nil {
  126. if errors.Cause(err) == sql.ErrNoRows {
  127. return nil, httperrors.NewResourceNotFoundError2("guests", guestStr)
  128. } else {
  129. return nil, httperrors.NewGeneralError(err)
  130. }
  131. }
  132. q = q.Equals("guest_id", guestObj.GetId())
  133. }
  134. if len(query.OsType) > 0 {
  135. q = q.In("os_type", query.OsType)
  136. }
  137. if query.WithMemory != nil {
  138. if *query.WithMemory {
  139. q = q.IsTrue("with_memory")
  140. } else {
  141. q = q.IsFalse("with_memory")
  142. }
  143. }
  144. return q, nil
  145. }
  146. func (manager *SInstanceSnapshotManager) OrderByExtraFields(
  147. ctx context.Context,
  148. q *sqlchemy.SQuery,
  149. userCred mcclient.TokenCredential,
  150. query api.InstanceSnapshotListInput,
  151. ) (*sqlchemy.SQuery, error) {
  152. var err error
  153. q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
  154. if err != nil {
  155. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
  156. }
  157. q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
  158. if err != nil {
  159. return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
  160. }
  161. if db.NeedOrderQuery([]string{query.OrderByDiskSnapshotCount}) {
  162. nQ := SnapshotManager.Query()
  163. nQ = nQ.AppendField(nQ.Field("disk_id"), sqlchemy.COUNT("snapshot_count"))
  164. nQ = nQ.GroupBy("disk_id")
  165. nSQ := nQ.SubQuery()
  166. guestdiskQ := GuestdiskManager.Query()
  167. guestdiskQ = guestdiskQ.LeftJoin(nSQ, sqlchemy.Equals(nSQ.Field("disk_id"), guestdiskQ.Field("disk_id")))
  168. guestdiskSQ := guestdiskQ.AppendField(guestdiskQ.Field("guest_id"), nSQ.Field("snapshot_count")).SubQuery()
  169. q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(guestdiskSQ.Field("guest_id"), q.Field("guest_id")))
  170. q = q.AppendField(q.QueryFields()...)
  171. q = q.AppendField(guestdiskSQ.Field("snapshot_count"))
  172. q = db.OrderByFields(q, []string{query.OrderByDiskSnapshotCount}, []sqlchemy.IQueryField{q.Field("snapshot_count")})
  173. }
  174. if db.NeedOrderQuery([]string{query.OrderByGuest}) {
  175. guestQ := GuestManager.Query()
  176. guestSQ := guestQ.AppendField(guestQ.Field("id"), guestQ.Field("name").Label("guest_name")).SubQuery()
  177. q = q.LeftJoin(guestSQ, sqlchemy.Equals(guestSQ.Field("id"), q.Field("guest_id")))
  178. q = q.AppendField(q.QueryFields()...)
  179. q = q.AppendField(guestSQ.Field("guest_name"))
  180. q = db.OrderByFields(q, []string{query.OrderByGuest}, []sqlchemy.IQueryField{q.Field("guest_name")})
  181. }
  182. return q, nil
  183. }
  184. func (manager *SInstanceSnapshotManager) ListItemExportKeys(ctx context.Context,
  185. q *sqlchemy.SQuery,
  186. userCred mcclient.TokenCredential,
  187. keys stringutils2.SSortedStrings,
  188. ) (*sqlchemy.SQuery, error) {
  189. var err error
  190. q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  191. if err != nil {
  192. return nil, err
  193. }
  194. return q, nil
  195. }
  196. func (manager *SInstanceSnapshotManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  197. var err error
  198. q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
  199. if err == nil {
  200. return q, nil
  201. }
  202. q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
  203. if err == nil {
  204. return q, nil
  205. }
  206. return q, httperrors.ErrNotFound
  207. }
  208. func (manager *SInstanceSnapshotManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
  209. var err error
  210. q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraFields(q, resource, fields)
  211. if err == nil {
  212. return q, nil
  213. }
  214. return q, httperrors.ErrNotFound
  215. }
  216. func (self *SInstanceSnapshot) GetGuest() (*SGuest, error) {
  217. if len(self.GuestId) == 0 {
  218. return nil, errors.ErrNotFound
  219. }
  220. guest := GuestManager.FetchGuestById(self.GuestId)
  221. if guest == nil {
  222. return nil, errors.ErrNotFound
  223. }
  224. return guest, nil
  225. }
  226. func (self *SInstanceSnapshot) getMoreDetails(userCred mcclient.TokenCredential, out api.InstanceSnapshotDetails) api.InstanceSnapshotDetails {
  227. guest := GuestManager.FetchGuestById(self.GuestId)
  228. if guest != nil {
  229. out.Guest = guest.Name
  230. out.GuestStatus = guest.Status
  231. }
  232. var osType string
  233. provider := self.GetProviderName()
  234. if utils.IsInStringArray(provider, ProviderHasSubSnapshot) {
  235. snapshots, _ := self.GetSnapshots()
  236. out.Snapshots = []api.SimpleSnapshot{}
  237. for i := 0; i < len(snapshots); i++ {
  238. if snapshots[i].DiskType == api.DISK_TYPE_SYS {
  239. osType = snapshots[i].OsType
  240. }
  241. out.Snapshots = append(out.Snapshots, api.SimpleSnapshot{
  242. Id: snapshots[i].Id,
  243. Name: snapshots[i].Name,
  244. StorageId: snapshots[i].StorageId,
  245. DiskType: snapshots[i].DiskType,
  246. CloudregionId: snapshots[i].CloudregionId,
  247. Size: snapshots[i].Size,
  248. VirtualSize: snapshots[i].VirtualSize,
  249. Status: snapshots[i].Status,
  250. StorageType: snapshots[i].GetStorageType(),
  251. EncryptKeyId: snapshots[i].EncryptKeyId,
  252. CreatedAt: snapshots[i].CreatedAt,
  253. })
  254. out.SizeMb += snapshots[i].Size
  255. out.VirtualSizeMb += snapshots[i].VirtualSize
  256. if len(snapshots[i].StorageId) > 0 && out.StorageType == "" {
  257. out.StorageType = snapshots[i].GetStorageType()
  258. }
  259. }
  260. if out.VirtualSizeMb <= 0 && guest != nil {
  261. out.VirtualSizeMb = guest.getDiskSize()
  262. }
  263. } else if guest != nil {
  264. disk, err := guest.GetSystemDisk()
  265. if err != nil {
  266. log.Errorf("unable to GetSystemDisk of guest %q", guest.GetId())
  267. } else {
  268. s, _ := disk.GetStorage()
  269. if s != nil {
  270. out.StorageType = s.StorageType
  271. }
  272. }
  273. out.VirtualSizeMb = guest.getDiskSize()
  274. out.SizeMb = out.VirtualSizeMb
  275. }
  276. if len(osType) > 0 {
  277. out.Properties = map[string]string{"os_type": osType}
  278. }
  279. out.SizeMb += out.MemorySizeKB / 1024
  280. out.Size = out.SizeMb * 1024 * 1024
  281. return out
  282. }
  283. func (manager *SInstanceSnapshotManager) FetchCustomizeColumns(
  284. ctx context.Context,
  285. userCred mcclient.TokenCredential,
  286. query jsonutils.JSONObject,
  287. objs []interface{},
  288. fields stringutils2.SSortedStrings,
  289. isList bool,
  290. ) []api.InstanceSnapshotDetails {
  291. rows := make([]api.InstanceSnapshotDetails, len(objs))
  292. virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  293. manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  294. encRows := manager.SEncryptedResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  295. for i := range rows {
  296. rows[i] = api.InstanceSnapshotDetails{
  297. VirtualResourceDetails: virtRows[i],
  298. ManagedResourceInfo: manRows[i],
  299. EncryptedResourceDetails: encRows[i],
  300. }
  301. rows[i] = objs[i].(*SInstanceSnapshot).getMoreDetails(userCred, rows[i])
  302. }
  303. return rows
  304. }
  305. func (self *SInstanceSnapshot) StartCreateInstanceSnapshotTask(
  306. ctx context.Context,
  307. userCred mcclient.TokenCredential,
  308. pendingUsage quotas.IQuota,
  309. parentTaskId string,
  310. ) error {
  311. if task, err := taskman.TaskManager.NewTask(
  312. ctx, "InstanceSnapshotCreateTask", self, userCred, nil, parentTaskId, "", pendingUsage); err != nil {
  313. return err
  314. } else {
  315. task.ScheduleRun(nil)
  316. }
  317. return nil
  318. }
  319. func (manager *SInstanceSnapshotManager) fillInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, instanceSnapshot *SInstanceSnapshot) {
  320. instanceSnapshot.SetModelManager(manager, instanceSnapshot)
  321. instanceSnapshot.ProjectId = guest.ProjectId
  322. instanceSnapshot.DomainId = guest.DomainId
  323. instanceSnapshot.GuestId = guest.Id
  324. instanceSnapshot.InstanceType = guest.InstanceType
  325. instanceSnapshot.ImageId = guest.GetTemplateId()
  326. // inherit encrypt_key_id from guest
  327. instanceSnapshot.EncryptKeyId = guest.EncryptKeyId
  328. guestSchedInput := guest.ToSchedDesc()
  329. host, _ := guest.GetHost()
  330. instanceSnapshot.ManagerId = host.ManagerId
  331. zone, _ := host.GetZone()
  332. instanceSnapshot.CloudregionId = zone.CloudregionId
  333. for i := 0; i < len(guestSchedInput.Disks); i++ {
  334. guestSchedInput.Disks[i].ImageId = ""
  335. }
  336. guestSchedInput.Name = ""
  337. guestSchedInput.HostId = ""
  338. guestSchedInput.Project = ""
  339. guestSchedInput.Domain = ""
  340. for i := 0; i < len(guestSchedInput.Networks); i++ {
  341. guestSchedInput.Networks[i].Mac = ""
  342. guestSchedInput.Networks[i].Address = ""
  343. guestSchedInput.Networks[i].Address6 = ""
  344. }
  345. instanceSnapshot.ServerConfig = jsonutils.Marshal(guestSchedInput.ServerConfig)
  346. if len(guest.KeypairId) > 0 {
  347. keypair, _ := KeypairManager.FetchById(guest.KeypairId)
  348. if keypair != nil {
  349. instanceSnapshot.KeypairId = guest.KeypairId
  350. }
  351. }
  352. serverMetadata := jsonutils.NewDict()
  353. if loginAccount := guest.GetMetadata(ctx, "login_account", nil); len(loginAccount) > 0 {
  354. loginKey := guest.GetMetadata(ctx, "login_key", nil)
  355. if len(guest.KeypairId) == 0 && len(loginKey) > 0 {
  356. passwd, e := utils.DescryptAESBase64(guest.Id, loginKey)
  357. if e == nil {
  358. serverMetadata.Set("login_account", jsonutils.NewString(loginAccount))
  359. serverMetadata.Set("passwd", jsonutils.NewString(passwd))
  360. }
  361. } else {
  362. serverMetadata.Set("login_key", jsonutils.NewString(loginKey))
  363. serverMetadata.Set("login_account", jsonutils.NewString(loginAccount))
  364. }
  365. }
  366. if osArch := guest.GetMetadata(ctx, "os_arch", nil); len(osArch) > 0 {
  367. serverMetadata.Set("os_arch", jsonutils.NewString(osArch))
  368. }
  369. if osDist := guest.GetMetadata(ctx, "os_distribution", nil); len(osDist) > 0 {
  370. serverMetadata.Set("os_distribution", jsonutils.NewString(osDist))
  371. }
  372. if osName := guest.GetMetadata(ctx, "os_name", nil); len(osName) > 0 {
  373. serverMetadata.Set("os_name", jsonutils.NewString(osName))
  374. }
  375. if osVersion := guest.GetMetadata(ctx, "os_version", nil); len(osVersion) > 0 {
  376. serverMetadata.Set("os_version", jsonutils.NewString(osVersion))
  377. }
  378. secs, _ := guest.GetSecgroups()
  379. if len(secs) > 0 {
  380. secIds := make([]string, len(secs))
  381. for i := 0; i < len(secs); i++ {
  382. secIds[i] = secs[i].Id
  383. }
  384. instanceSnapshot.SecGroups = jsonutils.Marshal(secIds)
  385. }
  386. instanceSnapshot.OsType = guest.OsType
  387. instanceSnapshot.OsArch = guest.OsArch
  388. instanceSnapshot.ServerMetadata = serverMetadata
  389. }
  390. func (manager *SInstanceSnapshotManager) CreateInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, name string, autoDelete bool, withMemory bool) (*SInstanceSnapshot, error) {
  391. instanceSnapshot := &SInstanceSnapshot{}
  392. instanceSnapshot.SetModelManager(manager, instanceSnapshot)
  393. instanceSnapshot.Name = name
  394. instanceSnapshot.AutoDelete = autoDelete
  395. if autoDelete {
  396. // hide auto-delete instance snapshots
  397. instanceSnapshot.IsSystem = true
  398. }
  399. manager.fillInstanceSnapshot(ctx, userCred, guest, instanceSnapshot)
  400. // compute size of instanceSnapshot
  401. // instanceSnapshot.SizeMb = guest.getDiskSize()
  402. instanceSnapshot.WithMemory = withMemory
  403. instanceSnapshot.MemoryFileHostId = guest.HostId
  404. err := manager.TableSpec().Insert(ctx, instanceSnapshot)
  405. if err != nil {
  406. return nil, errors.Wrap(err, "Insert")
  407. }
  408. err = db.InheritFromTo(ctx, userCred, guest, instanceSnapshot)
  409. if err != nil {
  410. return nil, errors.Wrap(err, "Inherit ClassMetadata")
  411. }
  412. return instanceSnapshot, nil
  413. }
  414. var HypervisorIndependentInstanceSnapshot = []string{
  415. api.HYPERVISOR_KVM,
  416. }
  417. var ProviderHasSubSnapshot = []string{
  418. api.CLOUD_PROVIDER_ONECLOUD,
  419. }
  420. func (self *SInstanceSnapshot) ToInstanceCreateInput(
  421. sourceInput *api.ServerCreateInput) (*api.ServerCreateInput, error) {
  422. serverConfig := new(schedapi.ServerConfig)
  423. if err := self.ServerConfig.Unmarshal(serverConfig); err != nil {
  424. return nil, errors.Wrap(err, "unmarshal sched input")
  425. }
  426. provider := self.GetProviderName()
  427. if utils.IsInStringArray(provider, ProviderHasSubSnapshot) {
  428. isjs := make([]SInstanceSnapshotJoint, 0)
  429. err := InstanceSnapshotJointManager.Query().Equals("instance_snapshot_id", self.Id).Asc("disk_index").All(&isjs)
  430. if err != nil {
  431. return nil, errors.Wrap(err, "fetch instance snapshots")
  432. }
  433. for i := 0; i < len(serverConfig.Disks); i++ {
  434. index := serverConfig.Disks[i].Index
  435. if index < len(isjs) {
  436. serverConfig.Disks[i].SnapshotId = isjs[index].SnapshotId
  437. if i == 0 && len(self.ImageId) > 0 {
  438. // system disk, save ImageId
  439. serverConfig.Disks[i].ImageId = self.ImageId
  440. }
  441. }
  442. }
  443. }
  444. sourceInput.Disks = serverConfig.Disks
  445. if sourceInput.VmemSize == 0 {
  446. sourceInput.VmemSize = serverConfig.Memory
  447. }
  448. if sourceInput.VcpuCount == 0 {
  449. sourceInput.VcpuCount = serverConfig.Ncpu
  450. }
  451. if len(self.KeypairId) > 0 {
  452. sourceInput.KeypairId = self.KeypairId
  453. }
  454. if self.SecGroups != nil {
  455. secGroups := make([]string, 0)
  456. inputSecgs := make([]string, 0)
  457. self.SecGroups.Unmarshal(&secGroups)
  458. for i := 0; i < len(secGroups); i++ {
  459. _, err := SecurityGroupManager.FetchSecgroupById(secGroups[i])
  460. if err == nil {
  461. inputSecgs = append(inputSecgs, secGroups[i])
  462. }
  463. }
  464. sourceInput.Secgroups = inputSecgs
  465. }
  466. sourceInput.OsType = self.OsType
  467. sourceInput.OsArch = self.OsArch
  468. sourceInput.InstanceType = self.InstanceType
  469. if len(sourceInput.Networks) == 0 {
  470. sourceInput.Networks = serverConfig.Networks
  471. }
  472. if self.IsEncrypted() {
  473. if sourceInput.EncryptKeyId != nil && *sourceInput.EncryptKeyId != self.EncryptKeyId {
  474. return nil, errors.Wrap(httperrors.ErrConflict, "encrypt_key_id conflict with instance_snapshot's encrypt_key_id")
  475. }
  476. sourceInput.EncryptKeyId = &self.EncryptKeyId
  477. }
  478. return sourceInput, nil
  479. }
  480. func (self *SInstanceSnapshot) GetSnapshots() ([]SSnapshot, error) {
  481. isjq := InstanceSnapshotJointManager.Query("snapshot_id").Equals("instance_snapshot_id", self.Id)
  482. snapshots := make([]SSnapshot, 0)
  483. err := SnapshotManager.Query().In("id", isjq).All(&snapshots)
  484. if err != nil && err != sql.ErrNoRows {
  485. return nil, err
  486. } else if err != nil && err == sql.ErrNoRows {
  487. return nil, nil
  488. } else {
  489. for i := 0; i < len(snapshots); i++ {
  490. snapshots[i].SetModelManager(SnapshotManager, &snapshots[i])
  491. }
  492. return snapshots, nil
  493. }
  494. }
  495. func (self *SInstanceSnapshot) GetQuotaKeys() quotas.IQuotaKeys {
  496. region, _ := self.GetRegion()
  497. return fetchRegionalQuotaKeys(
  498. rbacscope.ScopeProject,
  499. self.GetOwnerId(),
  500. region,
  501. self.GetCloudprovider(),
  502. )
  503. }
  504. func (self *SInstanceSnapshot) GetUsages() []db.IUsage {
  505. if self.PendingDeleted || self.Deleted {
  506. return nil
  507. }
  508. usage := SRegionQuota{InstanceSnapshot: 1}
  509. keys := self.GetQuotaKeys()
  510. usage.SetKeys(keys)
  511. return []db.IUsage{
  512. &usage,
  513. }
  514. }
  515. func TotalInstanceSnapshotCount(ctx context.Context, scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string, policyResult rbacutils.SPolicyResult) (int, error) {
  516. q := InstanceSnapshotManager.Query()
  517. switch scope {
  518. case rbacscope.ScopeSystem:
  519. case rbacscope.ScopeDomain:
  520. q = q.Equals("domain_id", ownerId.GetProjectDomainId())
  521. case rbacscope.ScopeProject:
  522. q = q.Equals("tenant_id", ownerId.GetProjectId())
  523. }
  524. q = db.ObjectIdQueryWithPolicyResult(ctx, q, InstanceSnapshotManager, policyResult)
  525. q = RangeObjectsFilter(q, rangeObjs, q.Field("cloudregion_id"), nil, q.Field("manager_id"), nil, nil)
  526. q = CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv)
  527. return q.CountWithError()
  528. }
  529. func (self *SInstanceSnapshot) GetInstanceSnapshotJointAt(diskIndex int) (*SInstanceSnapshotJoint, error) {
  530. ispj := new(SInstanceSnapshotJoint)
  531. err := InstanceSnapshotJointManager.Query().
  532. Equals("instance_snapshot_id", self.Id).Equals("disk_index", diskIndex).First(ispj)
  533. return ispj, err
  534. }
  535. func (self *SInstanceSnapshot) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
  536. if self.Status == api.INSTANCE_SNAPSHOT_START_DELETE || self.Status == api.INSTANCE_SNAPSHOT_RESET {
  537. return httperrors.NewForbiddenError("can't delete instance snapshot with wrong status")
  538. }
  539. return nil
  540. }
  541. func (self *SInstanceSnapshot) CustomizeDelete(
  542. ctx context.Context, userCred mcclient.TokenCredential,
  543. query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  544. return self.StartInstanceSnapshotDeleteTask(ctx, userCred, "")
  545. }
  546. func (self *SInstanceSnapshot) StartInstanceSnapshotDeleteTask(
  547. ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  548. task, err := taskman.TaskManager.NewTask(
  549. ctx, "InstanceSnapshotDeleteTask", self, userCred, nil, parentTaskId, "", nil)
  550. if err != nil {
  551. log.Errorf("%s", err)
  552. return err
  553. }
  554. self.SetStatus(ctx, userCred, api.INSTANCE_SNAPSHOT_START_DELETE, "InstanceSnapshotDeleteTask")
  555. task.ScheduleRun(nil)
  556. return nil
  557. }
  558. func (self *SInstanceSnapshot) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  559. snapshots, err := self.GetSnapshots()
  560. if err != nil {
  561. return nil, err
  562. }
  563. for i := range snapshots {
  564. snapshotId := snapshots[i].Id
  565. isjp := new(SInstanceSnapshotJoint)
  566. err = InstanceSnapshotJointManager.Query().
  567. Equals("instance_snapshot_id", self.Id).Equals("snapshot_id", snapshotId).First(isjp)
  568. if err == nil || isjp != nil {
  569. isjp.SetModelManager(InstanceSnapshotJointManager, isjp)
  570. err = isjp.Delete(ctx, userCred)
  571. if err != nil {
  572. return nil, errors.Wrapf(err, "delete instance snapshot joint: %s", snapshotId)
  573. }
  574. } else {
  575. log.Errorf("failed get instance_snapshot %s join %s: %s", self.Id, snapshotId, err)
  576. }
  577. _, err = snapshots[i].PerformPurge(ctx, userCred, query, data)
  578. if err != nil {
  579. return nil, errors.Wrapf(err, "delete snapshot: %s", snapshotId)
  580. }
  581. }
  582. err = self.RealDelete(ctx, userCred)
  583. return nil, err
  584. }
  585. func (self *SInstanceSnapshot) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  586. return db.DeleteModel(ctx, userCred, self)
  587. }
  588. func (self *SInstanceSnapshot) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  589. return nil
  590. }
  591. func (self *SInstanceSnapshot) AddRefCount(ctx context.Context) error {
  592. lockman.LockObject(ctx, self)
  593. defer lockman.ReleaseObject(ctx, self)
  594. _, err := db.Update(self, func() error {
  595. self.RefCount += 1
  596. return nil
  597. })
  598. return err
  599. }
  600. func (self *SInstanceSnapshot) DecRefCount(ctx context.Context, userCred mcclient.TokenCredential) error {
  601. lockman.LockObject(ctx, self)
  602. defer lockman.ReleaseObject(ctx, self)
  603. _, err := db.Update(self, func() error {
  604. self.RefCount -= 1
  605. return nil
  606. })
  607. if err == nil && self.RefCount == 0 && self.AutoDelete {
  608. self.StartInstanceSnapshotDeleteTask(ctx, userCred, "")
  609. }
  610. return err
  611. }
  612. func (is *SInstanceSnapshot) syncRemoveCloudInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential) error {
  613. lockman.LockObject(ctx, is)
  614. defer lockman.ReleaseObject(ctx, is)
  615. err := is.ValidateDeleteCondition(ctx, nil)
  616. if err != nil {
  617. err = is.SetStatus(ctx, userCred, api.INSTANCE_SNAPSHOT_UNKNOWN, "sync to delete")
  618. } else {
  619. err = is.RealDelete(ctx, userCred)
  620. }
  621. return err
  622. }
  623. func (is *SInstanceSnapshot) SyncWithCloudInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudInstanceSnapshot, guest *SGuest) error {
  624. diff, err := db.UpdateWithLock(ctx, is, func() error {
  625. is.Status = ext.GetStatus()
  626. InstanceSnapshotManager.fillInstanceSnapshot(ctx, userCred, guest, is)
  627. return nil
  628. })
  629. if err != nil {
  630. return err
  631. }
  632. db.OpsLog.LogSyncUpdate(is, diff, userCred)
  633. return nil
  634. }
  635. func (manager *SInstanceSnapshotManager) newFromCloudInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, extSnapshot cloudprovider.ICloudInstanceSnapshot, guest *SGuest) (*SInstanceSnapshot, error) {
  636. instanceSnapshot := SInstanceSnapshot{}
  637. instanceSnapshot.SetModelManager(manager, &instanceSnapshot)
  638. instanceSnapshot.ExternalId = extSnapshot.GetGlobalId()
  639. instanceSnapshot.Status = extSnapshot.GetStatus()
  640. manager.fillInstanceSnapshot(ctx, userCred, guest, &instanceSnapshot)
  641. var err = func() error {
  642. lockman.LockClass(ctx, manager, "name")
  643. defer lockman.ReleaseClass(ctx, manager, "name")
  644. newName, err := db.GenerateName(ctx, manager, nil, extSnapshot.GetName())
  645. if err == nil {
  646. instanceSnapshot.Name = extSnapshot.GetName()
  647. } else {
  648. instanceSnapshot.Name = newName
  649. }
  650. return manager.TableSpec().Insert(ctx, &instanceSnapshot)
  651. }()
  652. if err != nil {
  653. return nil, err
  654. }
  655. db.OpsLog.LogEvent(&instanceSnapshot, db.ACT_CREATE, instanceSnapshot.GetShortDesc(ctx), userCred)
  656. return &instanceSnapshot, nil
  657. }
  658. func (self *SInstanceSnapshot) GetRegionDriver() IRegionDriver {
  659. provider := self.GetProviderName()
  660. return GetRegionDriver(provider)
  661. }
  662. func (ism *SInstanceSnapshotManager) InitializeData() error {
  663. q := ism.Query().IsNullOrEmpty("cloudregion_id")
  664. var isps []SInstanceSnapshot
  665. err := db.FetchModelObjects(ism, q, &isps)
  666. if err != nil {
  667. return errors.Wrap(err, "unable to FetchModelObjects")
  668. }
  669. var (
  670. cloudregionId string
  671. )
  672. for i := range isps {
  673. isp := &isps[i]
  674. guest, err := isp.GetGuest()
  675. if errors.Cause(err) == errors.ErrNotFound {
  676. cloudregionId = api.DEFAULT_REGION_ID
  677. }
  678. if err != nil {
  679. return errors.Wrapf(err, "unable to GetGuest for isp %q", isp.GetId())
  680. } else {
  681. host, _ := guest.GetHost()
  682. zone, _ := host.GetZone()
  683. cloudregionId = zone.CloudregionId
  684. }
  685. _, err = db.Update(isp, func() error {
  686. isp.CloudregionId = cloudregionId
  687. return nil
  688. })
  689. if err != nil {
  690. return errors.Wrap(err, "unable to Update db")
  691. }
  692. }
  693. return nil
  694. }
  695. func (isp *SInstanceSnapshot) GetInstanceSnapshotJointsByOrder(guest *SGuest) ([]*SInstanceSnapshotJoint, error) {
  696. disks, err := guest.GetGuestDisks()
  697. if err != nil {
  698. return nil, errors.Wrap(err, "GetGuestDisks")
  699. }
  700. ss, err := isp.GetSnapshots()
  701. if err != nil {
  702. return nil, errors.Wrapf(err, "Get %s subsnapshots", isp.GetName())
  703. }
  704. jIsps := make([]*SInstanceSnapshotJoint, 0)
  705. for idx, gd := range disks {
  706. d := gd.GetDisk()
  707. if d == nil {
  708. return nil, errors.Wrapf(err, "Not get guestdisk %d related disk", idx)
  709. }
  710. if idx >= len(ss) {
  711. break
  712. }
  713. jIsp, err := isp.GetInstanceSnapshotJointAt(idx)
  714. if err != nil {
  715. return nil, errors.Wrapf(err, "GetInstanceSnapshotJointAt %d", idx)
  716. }
  717. sd, err := ss[idx].GetDisk()
  718. if err != nil {
  719. return nil, errors.Wrapf(err, "Get snapshot %d disk", idx)
  720. }
  721. if ss[idx].GetId() != jIsp.SnapshotId {
  722. return nil, errors.Wrapf(err, "InstanceSnapshotJoint %d snapshot_id %q != %q", idx, jIsp.SnapshotId, ss[idx].GetId())
  723. }
  724. if sd.GetId() != d.GetId() {
  725. return nil, errors.Wrapf(err, "Disk Snapshot %d's disk id %q != current disk %q", idx, sd.GetId(), d.GetId())
  726. }
  727. jIsps = append(jIsps, jIsp)
  728. }
  729. return jIsps, nil
  730. }
  731. func (self *SInstanceSnapshot) CustomizeCreate(
  732. ctx context.Context,
  733. userCred mcclient.TokenCredential,
  734. ownerId mcclient.IIdentityProvider,
  735. query jsonutils.JSONObject,
  736. data jsonutils.JSONObject,
  737. ) error {
  738. // use disk's ownerId instead of default ownerId
  739. guestObj, err := GuestManager.FetchById(self.GuestId)
  740. if err != nil {
  741. return errors.Wrap(err, "GuestManager.FetchById")
  742. }
  743. ownerId = guestObj.(*SGuest).GetOwnerId()
  744. return self.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
  745. }
  746. func (manager *SInstanceSnapshotManager) GetNeedAutoSnapshotServers() ([]SSnapshotPolicyResource, error) {
  747. tz, _ := time.LoadLocation(options.Options.TimeZone)
  748. t := time.Now().In(tz)
  749. week := t.Weekday()
  750. if week == 0 { // sunday is zero
  751. week += 7
  752. }
  753. timePoint := t.Hour()
  754. policy := SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_SERVER).Equals("cloudregion_id", api.DEFAULT_REGION_ID)
  755. policy = policy.Filter(sqlchemy.Contains(policy.Field("repeat_weekdays"), fmt.Sprintf("%d", week)))
  756. sq := policy.Filter(
  757. sqlchemy.OR(
  758. sqlchemy.Contains(policy.Field("time_points"), fmt.Sprintf(",%d,", timePoint)),
  759. sqlchemy.Startswith(policy.Field("time_points"), fmt.Sprintf("[%d,", timePoint)),
  760. sqlchemy.Endswith(policy.Field("time_points"), fmt.Sprintf(",%d]", timePoint)),
  761. sqlchemy.Equals(policy.Field("time_points"), fmt.Sprintf("[%d]", timePoint)),
  762. ),
  763. ).SubQuery()
  764. servers := GuestManager.Query().SubQuery()
  765. q := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER)
  766. q = q.Join(sq, sqlchemy.Equals(q.Field("snapshotpolicy_id"), sq.Field("id")))
  767. q = q.Join(servers, sqlchemy.Equals(q.Field("resource_id"), servers.Field("id")))
  768. ret := []SSnapshotPolicyResource{}
  769. err := db.FetchModelObjects(SnapshotPolicyResourceManager, q, &ret)
  770. if err != nil {
  771. return nil, err
  772. }
  773. return ret, nil
  774. }
  775. func (manager *SInstanceSnapshotManager) AutoServerSnapshot(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  776. servers, err := manager.GetNeedAutoSnapshotServers()
  777. if err != nil {
  778. log.Errorf("Get auto snapshot servers id failed: %s", err)
  779. return
  780. }
  781. log.Infof("auto snapshot %d servers", len(servers))
  782. serverMap := map[string]*SGuest{}
  783. for i := range servers {
  784. server, err := servers[i].GetServer()
  785. if err != nil {
  786. log.Errorf("get server error: %v", err)
  787. continue
  788. }
  789. serverMap[server.Id] = server
  790. }
  791. for i := range serverMap {
  792. input := api.ServerInstanceSnapshot{}
  793. input.GenerateName = fmt.Sprintf("auto-%s-%d", serverMap[i].Name, time.Now().Unix())
  794. serverMap[i].PerformInstanceSnapshot(ctx, userCred, jsonutils.NewDict(), input)
  795. }
  796. }
  797. var instanceSnapshotCleanupTaskRunning int32 = 0
  798. func (manager *SInstanceSnapshotManager) CleanupInstanceSnapshots(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  799. if instanceSnapshotCleanupTaskRunning > 0 {
  800. log.Errorf("Previous CleanupInstanceSnapshots tasks still running !!!")
  801. return
  802. }
  803. instanceSnapshotCleanupTaskRunning = 1
  804. defer func() {
  805. instanceSnapshotCleanupTaskRunning = 0
  806. }()
  807. sq := manager.Query().Equals("status", api.INSTANCE_SNAPSHOT_READY).Startswith("name", "auto-").SubQuery()
  808. iss := []struct {
  809. GuestCnt int
  810. GuestId string
  811. }{}
  812. q := sq.Query(
  813. sqlchemy.COUNT("guest_cnt", sq.Field("guest_id")),
  814. sq.Field("guest_id"),
  815. ).GroupBy(sq.Field("guest_id"))
  816. err := q.All(&iss)
  817. if err != nil {
  818. log.Errorf("Cleanup instance snapshots job fetch instance snapshot failed %s", err)
  819. return
  820. }
  821. guestCount := map[string]int{}
  822. for i := range iss {
  823. guestCount[iss[i].GuestId] = iss[i].GuestCnt
  824. }
  825. // cleanup retention count instance snapshots
  826. {
  827. sq = SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_SERVER).GT("retention_count", 0).SubQuery()
  828. spr := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER).SubQuery()
  829. q = sq.Query(
  830. sq.Field("retention_count"),
  831. spr.Field("resource_id").Label("guest_id"),
  832. )
  833. q = q.Join(spr, sqlchemy.Equals(q.Field("id"), spr.Field("snapshotpolicy_id")))
  834. guestRetentions := []struct {
  835. GuestId string
  836. RetentionCount int
  837. }{}
  838. err = q.All(&guestRetentions)
  839. if err != nil {
  840. log.Errorf("Cleanup instance snapshots job fetch guest retentions failed %s", err)
  841. return
  842. }
  843. guestRetentionMap := map[string]int{}
  844. for i := range guestRetentions {
  845. if _, ok := guestRetentionMap[guestRetentions[i].GuestId]; !ok {
  846. guestRetentionMap[guestRetentions[i].GuestId] = guestRetentions[i].RetentionCount
  847. }
  848. // 取最小保留个数
  849. if guestRetentionMap[guestRetentions[i].GuestId] > guestRetentions[i].RetentionCount {
  850. guestRetentionMap[guestRetentions[i].GuestId] = guestRetentions[i].RetentionCount
  851. }
  852. }
  853. for guestId, retentionCnt := range guestRetentionMap {
  854. if cnt, ok := guestCount[guestId]; ok && cnt > retentionCnt {
  855. manager.startCleanupRetentionCount(ctx, userCred, guestId, cnt-retentionCnt)
  856. }
  857. }
  858. }
  859. // cleanup retention days instance snapshots
  860. {
  861. sq = SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_SERVER).GT("retention_days", 0).SubQuery()
  862. spr := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER).SubQuery()
  863. q = sq.Query(
  864. sq.Field("retention_days"),
  865. spr.Field("resource_id").Label("guest_id"),
  866. )
  867. q = q.Join(spr, sqlchemy.Equals(q.Field("id"), spr.Field("snapshotpolicy_id")))
  868. guestRetentions := []struct {
  869. GuestId string
  870. RetentionDays int
  871. }{}
  872. err = q.All(&guestRetentions)
  873. if err != nil {
  874. log.Errorf("Cleanup instance snapshots job fetch guest retentions failed %s", err)
  875. return
  876. }
  877. guestRetentionMap := map[string]int{}
  878. for i := range guestRetentions {
  879. if _, ok := guestRetentionMap[guestRetentions[i].GuestId]; !ok {
  880. guestRetentionMap[guestRetentions[i].GuestId] = guestRetentions[i].RetentionDays
  881. }
  882. // 取最小保留天数
  883. if guestRetentionMap[guestRetentions[i].GuestId] > guestRetentions[i].RetentionDays {
  884. guestRetentionMap[guestRetentions[i].GuestId] = guestRetentions[i].RetentionDays
  885. }
  886. }
  887. for guestId, retentionDays := range guestRetentionMap {
  888. manager.startCleanupRetentionDays(ctx, userCred, guestId, retentionDays)
  889. }
  890. }
  891. }
  892. func (manager *SInstanceSnapshotManager) startCleanupRetentionCount(ctx context.Context, userCred mcclient.TokenCredential, guestId string, cnt int) error {
  893. q := manager.Query().Equals("guest_id", guestId).Equals("status", api.INSTANCE_SNAPSHOT_READY).Startswith("name", "auto-").Asc("created_at").Limit(cnt)
  894. vms := []SInstanceSnapshot{}
  895. err := db.FetchModelObjects(manager, q, &vms)
  896. if err != nil {
  897. return errors.Wrapf(err, "FetchModelObjects")
  898. }
  899. for i := range vms {
  900. vms[i].StartInstanceSnapshotDeleteTask(ctx, userCred, "")
  901. }
  902. return nil
  903. }
  904. func (manager *SInstanceSnapshotManager) startCleanupRetentionDays(ctx context.Context, userCred mcclient.TokenCredential, guestId string, day int) error {
  905. expiredTime := time.Now().AddDate(0, 0, -day)
  906. q := manager.Query().Equals("guest_id", guestId).Equals("status", api.INSTANCE_SNAPSHOT_READY).Startswith("name", "auto-").LE("created_at", expiredTime)
  907. vms := []SInstanceSnapshot{}
  908. err := db.FetchModelObjects(manager, q, &vms)
  909. if err != nil {
  910. return errors.Wrapf(err, "FetchModelObjects")
  911. }
  912. for i := range vms {
  913. vms[i].StartInstanceSnapshotDeleteTask(ctx, userCred, "")
  914. }
  915. return nil
  916. }