cachedimages.go 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136
  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. "math/rand"
  20. "strings"
  21. "time"
  22. "yunion.io/x/cloudmux/pkg/cloudprovider"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/errors"
  26. "yunion.io/x/pkg/gotypes"
  27. "yunion.io/x/pkg/tristate"
  28. "yunion.io/x/pkg/util/httputils"
  29. "yunion.io/x/pkg/util/rbacscope"
  30. "yunion.io/x/pkg/util/timeutils"
  31. "yunion.io/x/sqlchemy"
  32. api "yunion.io/x/onecloud/pkg/apis/compute"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  34. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  35. "yunion.io/x/onecloud/pkg/compute/options"
  36. "yunion.io/x/onecloud/pkg/httperrors"
  37. "yunion.io/x/onecloud/pkg/mcclient"
  38. "yunion.io/x/onecloud/pkg/mcclient/auth"
  39. "yunion.io/x/onecloud/pkg/mcclient/modules/image"
  40. "yunion.io/x/onecloud/pkg/util/stringutils2"
  41. )
  42. type SCachedimageManager struct {
  43. db.SSharableVirtualResourceBaseManager
  44. db.SExternalizedResourceBaseManager
  45. }
  46. var CachedimageManager *SCachedimageManager
  47. func init() {
  48. CachedimageManager = &SCachedimageManager{
  49. SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
  50. SCachedimage{},
  51. "cachedimages_tbl",
  52. "cachedimage",
  53. "cachedimages",
  54. ),
  55. }
  56. CachedimageManager.SetVirtualObject(CachedimageManager)
  57. CachedimageManager.TableSpec().AddIndex(false, "deleted", "domain_id", "tenant_id", "image_type")
  58. }
  59. type SCachedimage struct {
  60. db.SSharableVirtualResourceBase
  61. db.SExternalizedResourceBase
  62. // 镜像大小单位: Byte
  63. // example: 53687091200
  64. Size int64 `nullable:"false" list:"user" update:"admin" create:"admin_required"`
  65. // 镜像详情信息
  66. // example: {"deleted":false,"disk_format":"qcow2","id":"img-a6uucnfl","is_public":true,"min_disk":51200,"min_ram":0,"name":"FreeBSD 11.1 64bit","properties":{"os_arch":"x86_64","os_distribution":"FreeBSD","os_type":"FreeBSD","os_version":"11"},"protected":true,"size":53687091200,"status":"active"}
  67. Info jsonutils.JSONObject `nullable:"true" list:"user" update:"admin" create:"admin_required"`
  68. // 上此同步时间
  69. // example: 2020-01-17T05:28:54.000000Z
  70. LastSync time.Time `list:"admin"`
  71. // 最近一次缓存引用时间
  72. // 2020-01-17T05:20:54.000000Z
  73. LastRef time.Time `list:"admin"`
  74. // 引用次数
  75. // example: 0
  76. RefCount int `default:"0" list:"user"`
  77. // 是否支持UEFI
  78. // example: false
  79. UEFI tristate.TriState `default:"false" list:"user"`
  80. // 镜像类型, system: 公有云镜像, customized: 自定义镜像
  81. // example: system
  82. ImageType string `width:"16" default:"customized" list:"user" index:"true"`
  83. }
  84. func (manager *SCachedimageManager) GetResourceCount() ([]db.SScopeResourceCount, error) {
  85. return []db.SScopeResourceCount{}, nil
  86. }
  87. func (cachedImage SCachedimage) GetGlobalId() string {
  88. return cachedImage.ExternalId
  89. }
  90. func (cachedImage *SCachedimage) ValidateDeleteCondition(ctx context.Context, info *api.CachedimageDetails) error {
  91. if gotypes.IsNil(info) {
  92. info = &api.CachedimageDetails{}
  93. count, err := CachedimageManager.TotalResourceCount([]string{cachedImage.Id})
  94. if err != nil {
  95. return err
  96. }
  97. info.CachedimageUsage, _ = count[cachedImage.Id]
  98. }
  99. if info.CachedCount > 0 {
  100. return httperrors.NewNotEmptyError("The image has been cached on storages")
  101. }
  102. if cachedImage.GetStatus() == api.CACHED_IMAGE_STATUS_ACTIVE && !cachedImage.isReferenceSessionExpire() {
  103. return httperrors.NewConflictError("the image reference session has not been expired!")
  104. }
  105. return cachedImage.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx, nil)
  106. }
  107. func (cachedImage *SCachedimage) isReferenceSessionExpire() bool {
  108. if !cachedImage.LastRef.IsZero() && time.Now().Sub(cachedImage.LastRef) < api.CACHED_IMAGE_REFERENCE_SESSION_EXPIRE_SECONDS*time.Second {
  109. return false
  110. } else {
  111. return true
  112. }
  113. }
  114. func (cachedImage *SCachedimage) isRefreshSessionExpire() bool {
  115. if len(cachedImage.ExternalId) > 0 { // external image info never expires
  116. return false
  117. }
  118. if !cachedImage.LastRef.IsZero() && time.Now().Sub(cachedImage.LastRef) < api.CACHED_IMAGE_REFRESH_SECONDS*time.Second {
  119. return false
  120. } else {
  121. return true
  122. }
  123. }
  124. func (cachedImage *SCachedimage) GetHosts() ([]SHost, error) {
  125. q := HostManager.Query().Distinct()
  126. hs := HoststorageManager.Query().SubQuery()
  127. storages := StorageManager.Query().SubQuery()
  128. scis := StoragecachedimageManager.Query().Equals("cachedimage_id", cachedImage.Id).Equals("status", api.CACHED_IMAGE_STATUS_ACTIVE).SubQuery()
  129. q = q.Join(hs, sqlchemy.Equals(hs.Field("host_id"), q.Field("id")))
  130. q = q.Join(storages, sqlchemy.Equals(hs.Field("storage_id"), storages.Field("id")))
  131. q = q.Join(scis, sqlchemy.Equals(storages.Field("storagecache_id"), scis.Field("storagecache_id")))
  132. ret := []SHost{}
  133. err := db.FetchModelObjects(HostManager, q, &ret)
  134. if err != nil {
  135. return nil, err
  136. }
  137. return ret, err
  138. }
  139. func (cachedImage *SCachedimage) GetName() string {
  140. name, _ := cachedImage.Info.GetString("name")
  141. return name
  142. }
  143. func (cachedImage *SCachedimage) GetOwner() string {
  144. owner, _ := cachedImage.Info.GetString("owner")
  145. return owner
  146. }
  147. func (cachedImage *SCachedimage) GetFormat() string {
  148. format, _ := cachedImage.Info.GetString("disk_format")
  149. return format
  150. }
  151. func (cachedImage *SCachedimage) GetStatus() string {
  152. status, _ := cachedImage.Info.GetString("status")
  153. return status
  154. }
  155. func (cachedImage *SCachedimage) GetOSType() string {
  156. osType, _ := cachedImage.Info.GetString("properties", "os_type")
  157. return osType
  158. }
  159. func (cachedImage *SCachedimage) GetOSDistribution() string {
  160. osType, _ := cachedImage.Info.GetString("properties", "os_distribution")
  161. return osType
  162. }
  163. func (cachedImage *SCachedimage) GetOSVersion() string {
  164. osType, _ := cachedImage.Info.GetString("properties", "os_version")
  165. return osType
  166. }
  167. func (cachedImage *SCachedimage) GetHypervisor() string {
  168. osType, _ := cachedImage.Info.GetString("properties", "hypervisor")
  169. return osType
  170. }
  171. func (cachedImage *SCachedimage) GetChecksum() string {
  172. checksum, _ := cachedImage.Info.GetString("checksum")
  173. return checksum
  174. }
  175. func (cachedImage *SCachedimage) getStoragecacheQuery() *sqlchemy.SQuery {
  176. q := StoragecachedimageManager.Query().Equals("cachedimage_id", cachedImage.Id)
  177. return q
  178. }
  179. func (cachedImage *SCachedimage) getStoragecacheCount() (int, error) {
  180. return cachedImage.getStoragecacheQuery().CountWithError()
  181. }
  182. func (cachedImage *SCachedimage) GetImage() (*cloudprovider.SImage, error) {
  183. image := cloudprovider.SImage{
  184. ExternalId: cachedImage.ExternalId,
  185. }
  186. err := cachedImage.Info.Unmarshal(&image)
  187. if err != nil {
  188. log.Errorf("unmarshal %s fail %s", cachedImage.Info, err)
  189. return nil, errors.Wrap(err, "cachedImage.Info.Unmarshal")
  190. } else {
  191. // hack, make cached image ID consistent
  192. image.Id = cachedImage.Id
  193. return &image, nil
  194. }
  195. }
  196. func (cachedImage *SCachedimage) syncClassMetadata(ctx context.Context, userCred mcclient.TokenCredential) error {
  197. session := auth.GetSessionWithInternal(ctx, userCred, "")
  198. ret, err := image.Images.GetSpecific(session, cachedImage.Id, "class-metadata", nil)
  199. if err != nil {
  200. return errors.Wrap(err, "unable to get class_metadata")
  201. }
  202. classMetadata := make(map[string]string, 0)
  203. err = ret.Unmarshal(&classMetadata)
  204. if err != nil {
  205. return err
  206. }
  207. return cachedImage.SetClassMetadataAll(ctx, classMetadata, userCred)
  208. }
  209. func (manager *SCachedimageManager) cacheGlanceImageInfo(ctx context.Context, userCred mcclient.TokenCredential, info jsonutils.JSONObject) (*SCachedimage, error) {
  210. lockman.LockRawObject(ctx, manager.Keyword(), "name")
  211. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
  212. img := struct {
  213. Id string
  214. Size int64
  215. Name string
  216. Status string
  217. IsPublic bool
  218. ProjectId string `json:"tenant_id"`
  219. DomainId string
  220. PublicScope string
  221. }{}
  222. err := info.Unmarshal(&img)
  223. if err != nil {
  224. return nil, errors.Wrapf(err, "Unmarshal %s", info.String())
  225. }
  226. if len(img.Id) == 0 {
  227. return nil, fmt.Errorf("invalid image info")
  228. }
  229. if len(img.Name) == 0 {
  230. img.Name = img.Id
  231. }
  232. imageCache := SCachedimage{}
  233. imageCache.SetModelManager(manager, &imageCache)
  234. err = manager.RawQuery().Equals("id", img.Id).First(&imageCache)
  235. if err != nil {
  236. if errors.Cause(err) == sql.ErrNoRows { // insert
  237. imageCache.Id = img.Id
  238. imageCache.Name = img.Name
  239. imageCache.Size = img.Size
  240. imageCache.Info = info
  241. imageCache.Status = img.Status
  242. imageCache.IsPublic = img.IsPublic
  243. imageCache.PublicScope = img.PublicScope
  244. imageCache.ProjectId = img.ProjectId
  245. imageCache.DomainId = img.DomainId
  246. imageCache.LastSync = timeutils.UtcNow()
  247. err = manager.TableSpec().Insert(ctx, &imageCache)
  248. if err != nil {
  249. return nil, err
  250. }
  251. db.OpsLog.LogEvent(&imageCache, db.ACT_CREATE, info, userCred)
  252. return &imageCache, nil
  253. } else {
  254. log.Errorf("fetching image cache (%s) failed: %s", img.Id, err)
  255. return nil, err
  256. }
  257. } else { // update
  258. diff, err := db.Update(&imageCache, func() error {
  259. imageCache.Size = img.Size
  260. imageCache.Info = info
  261. imageCache.Status = img.Status
  262. imageCache.IsPublic = img.IsPublic
  263. imageCache.PublicScope = img.PublicScope
  264. imageCache.LastSync = timeutils.UtcNow()
  265. imageCache.ProjectId = img.ProjectId
  266. imageCache.DomainId = img.DomainId
  267. if imageCache.Deleted {
  268. imageCache.Deleted = false
  269. imageCache.DeletedAt = time.Time{}
  270. imageCache.RefCount = 0
  271. imageCache.UpdateVersion = 0
  272. }
  273. return nil
  274. })
  275. if err != nil {
  276. return nil, err
  277. }
  278. db.OpsLog.LogEvent(&imageCache, db.ACT_UPDATE, diff, userCred)
  279. return &imageCache, nil
  280. }
  281. }
  282. func (manager *SCachedimageManager) RecoverCachedImage(ctx context.Context, userCred mcclient.TokenCredential, imgId string) (*SCachedimage, error) {
  283. lockman.LockRawObject(ctx, manager.Keyword(), "name")
  284. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
  285. imageCache := SCachedimage{}
  286. imageCache.SetModelManager(manager, &imageCache)
  287. err := manager.RawQuery().Equals("id", imgId).First(&imageCache)
  288. if err != nil {
  289. return nil, err
  290. }
  291. diff, err := db.Update(&imageCache, func() error {
  292. imageCache.Status = api.CACHED_IMAGE_STATUS_ACTIVE
  293. imageCache.LastSync = timeutils.UtcNow()
  294. if imageCache.Deleted {
  295. imageCache.Deleted = false
  296. imageCache.DeletedAt = time.Time{}
  297. imageCache.RefCount = 0
  298. imageCache.UpdateVersion = 0
  299. }
  300. return nil
  301. })
  302. if err != nil {
  303. return nil, err
  304. }
  305. db.OpsLog.LogEvent(&imageCache, db.ACT_UPDATE, diff, userCred)
  306. return &imageCache, nil
  307. }
  308. func (image *SCachedimage) GetStorages() ([]SStorage, error) {
  309. sq := StorageManager.Query()
  310. storagecacheimageSubq := StoragecachedimageManager.Query("storagecache_id").Equals("cachedimage_id", image.GetId()).SubQuery()
  311. sq.Join(storagecacheimageSubq, sqlchemy.Equals(sq.Field("storagecache_id"), storagecacheimageSubq.Field("storagecache_id")))
  312. storages := make([]SStorage, 0, 1)
  313. err := db.FetchModelObjects(StorageManager, sq, &storages)
  314. if err != nil {
  315. return nil, errors.Wrap(err, "FetchModelObjects")
  316. }
  317. return storages, nil
  318. }
  319. func (manager *SCachedimageManager) GetCachedimageById(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*SCachedimage, error) {
  320. img, err := manager.FetchById(imageId)
  321. if err == nil {
  322. cachedImage := img.(*SCachedimage)
  323. oSTypeOk := options.Options.NoCheckOsTypeForCachedImage || len(cachedImage.GetOSType()) > 0
  324. if (!refresh && cachedImage.GetStatus() == cloudprovider.IMAGE_STATUS_ACTIVE && oSTypeOk && !cachedImage.isRefreshSessionExpire()) || options.Options.ProhibitRefreshingCloudImage || len(cachedImage.ExternalId) > 0 {
  325. return cachedImage, nil
  326. }
  327. }
  328. if err != nil && errors.Cause(err) != sql.ErrNoRows {
  329. return nil, err
  330. }
  331. s := auth.GetAdminSession(ctx, options.Options.Region)
  332. obj, err := image.Images.Get(s, imageId, nil)
  333. if err != nil {
  334. log.Errorf("GetImageById %s error %s", imageId, err)
  335. return nil, errors.Wrap(err, "modules.Images.Get")
  336. }
  337. cachedImage, err := manager.cacheGlanceImageInfo(ctx, userCred, obj)
  338. if err != nil {
  339. return nil, errors.Wrap(err, "manager.cacheGlanceImageInfo")
  340. }
  341. return cachedImage, nil
  342. }
  343. func (manager *SCachedimageManager) GetImageById(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
  344. imgObj, _ := manager.FetchById(imageId)
  345. if imgObj != nil {
  346. cachedImage := imgObj.(*SCachedimage)
  347. oSTypeOk := options.Options.NoCheckOsTypeForCachedImage || len(cachedImage.GetOSType()) > 0
  348. if !refresh && cachedImage.GetStatus() == cloudprovider.IMAGE_STATUS_ACTIVE && oSTypeOk && !cachedImage.isRefreshSessionExpire() {
  349. return cachedImage.GetImage()
  350. } else if options.Options.ProhibitRefreshingCloudImage {
  351. return cachedImage.GetImage()
  352. } else if len(cachedImage.ExternalId) > 0 { // external image, request refresh
  353. return cachedImage.requestRefreshExternalImage(ctx, userCred)
  354. }
  355. }
  356. s := auth.GetAdminSession(ctx, options.Options.Region)
  357. obj, err := image.Images.Get(s, imageId, nil)
  358. if err != nil {
  359. return nil, errors.Wrap(err, "modules.Images.Get")
  360. }
  361. cachedImage, err := manager.cacheGlanceImageInfo(ctx, userCred, obj)
  362. if err != nil {
  363. return nil, errors.Wrap(err, "manager.cacheGlanceImageInfo")
  364. }
  365. return cachedImage.GetImage()
  366. }
  367. func (manager *SCachedimageManager) getImageByName(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
  368. imgObj, _ := manager.FetchByName(ctx, userCred, imageId)
  369. if imgObj != nil {
  370. cachedImage := imgObj.(*SCachedimage)
  371. if !refresh && cachedImage.GetStatus() == cloudprovider.IMAGE_STATUS_ACTIVE && len(cachedImage.GetOSType()) > 0 && !cachedImage.isRefreshSessionExpire() {
  372. return cachedImage.GetImage()
  373. } else if options.Options.ProhibitRefreshingCloudImage {
  374. return cachedImage.GetImage()
  375. } else if len(cachedImage.ExternalId) > 0 { // external image, request refresh
  376. return cachedImage.requestRefreshExternalImage(ctx, userCred)
  377. }
  378. }
  379. s := auth.GetSession(ctx, userCred, options.Options.Region)
  380. obj, err := image.Images.GetByName(s, imageId, nil)
  381. if err != nil {
  382. if httputils.ErrorCode(err) == 404 {
  383. err = httperrors.ErrNotFound
  384. }
  385. return nil, errors.Wrap(err, "modules.Images.GetByName")
  386. }
  387. cachedImage, err := manager.cacheGlanceImageInfo(ctx, userCred, obj)
  388. if err != nil {
  389. return nil, errors.Wrap(err, "manager.cacheGlanceImageInfo")
  390. }
  391. return cachedImage.GetImage()
  392. }
  393. func (manager *SCachedimageManager) GetImageInfo(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
  394. return manager.getImageInfo(ctx, userCred, imageId, refresh)
  395. }
  396. func (manager *SCachedimageManager) getImageInfo(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
  397. img, err := manager.GetImageById(ctx, userCred, imageId, refresh)
  398. if err == nil {
  399. return img, nil
  400. }
  401. log.Errorf("getImageInfoById %s fail %s", imageId, err)
  402. return manager.getImageByName(ctx, userCred, imageId, refresh)
  403. }
  404. func (cm *SCachedimageManager) query(manager db.IModelManager, field string, cacheIds []string, filter func(*sqlchemy.SQuery) *sqlchemy.SQuery) *sqlchemy.SSubQuery {
  405. q := manager.Query()
  406. if filter != nil {
  407. q = filter(q)
  408. }
  409. sq := q.SubQuery()
  410. return sq.Query(
  411. sq.Field("cachedimage_id"),
  412. sqlchemy.COUNT(field),
  413. ).In("cachedimage_id", cacheIds).GroupBy(sq.Field("cachedimage_id")).SubQuery()
  414. }
  415. type CachedimageUsageCount struct {
  416. Id string
  417. api.CachedimageUsage
  418. }
  419. func (manager *SCachedimageManager) TotalResourceCount(cacheIds []string) (map[string]api.CachedimageUsage, error) {
  420. ret := map[string]api.CachedimageUsage{}
  421. scSQ := manager.query(StoragecachedimageManager, "cached_cnt", cacheIds, nil)
  422. caches := manager.Query().SubQuery()
  423. cachesQ := caches.Query(
  424. sqlchemy.SUM("cached_count", scSQ.Field("cached_cnt")),
  425. )
  426. cachesQ.AppendField(cachesQ.Field("id"))
  427. cachesQ = cachesQ.LeftJoin(scSQ, sqlchemy.Equals(cachesQ.Field("id"), scSQ.Field("cachedimage_id")))
  428. cachesQ = cachesQ.Filter(sqlchemy.In(cachesQ.Field("id"), cacheIds)).GroupBy(cachesQ.Field("id"))
  429. counts := []CachedimageUsageCount{}
  430. err := cachesQ.All(&counts)
  431. if err != nil {
  432. return nil, errors.Wrapf(err, "cachesQ.All")
  433. }
  434. for i := range counts {
  435. ret[counts[i].Id] = counts[i].CachedimageUsage
  436. }
  437. return ret, nil
  438. }
  439. func (manager *SCachedimageManager) FetchCustomizeColumns(
  440. ctx context.Context,
  441. userCred mcclient.TokenCredential,
  442. query jsonutils.JSONObject,
  443. objs []interface{},
  444. fields stringutils2.SSortedStrings,
  445. isList bool,
  446. ) []api.CachedimageDetails {
  447. rows := make([]api.CachedimageDetails, len(objs))
  448. virtRows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  449. cacheIds := make([]string, len(objs))
  450. for i := range rows {
  451. ci := objs[i].(*SCachedimage)
  452. rows[i] = api.CachedimageDetails{
  453. SharableVirtualResourceDetails: virtRows[i],
  454. OsType: ci.GetOSType(),
  455. OsDistribution: ci.GetOSDistribution(),
  456. OsVersion: ci.GetOSVersion(),
  457. Hypervisor: ci.GetHypervisor(),
  458. }
  459. cacheIds[i] = ci.Id
  460. }
  461. usage, err := manager.TotalResourceCount(cacheIds)
  462. if err != nil {
  463. log.Errorf("TotalResourceCount error: %v", err)
  464. return rows
  465. }
  466. for i := range rows {
  467. rows[i].CachedimageUsage, _ = usage[cacheIds[i]]
  468. }
  469. return rows
  470. }
  471. func (cachedImage *SCachedimage) PerformRefresh(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  472. img, err := CachedimageManager.GetImageById(ctx, userCred, cachedImage.Id, true)
  473. if err != nil {
  474. return nil, err
  475. }
  476. return jsonutils.Marshal(img), nil
  477. }
  478. // 清除镜像缓存
  479. func (cachedImage *SCachedimage) PerformUncacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CachedImageUncacheImageInput) (jsonutils.JSONObject, error) {
  480. if len(input.StoragecacheId) == 0 {
  481. return nil, httperrors.NewMissingParameterError("storagecache_id")
  482. }
  483. _storagecache, err := StoragecacheManager.FetchById(input.StoragecacheId)
  484. if err != nil {
  485. if errors.Cause(err) == sql.ErrNoRows {
  486. return nil, httperrors.NewResourceNotFoundError("failed to found storagecache %s", input.StoragecacheId)
  487. }
  488. return nil, errors.Wrap(err, "StoragecacheManager.FetchById")
  489. }
  490. storagecache := _storagecache.(*SStoragecache)
  491. return storagecache.PerformUncacheImage(ctx, userCred, query, jsonutils.Marshal(map[string]interface{}{"image": cachedImage.Id, "is_force": input.IsForce}))
  492. }
  493. func (manager *SCachedimageManager) PerformCacheImage(
  494. ctx context.Context,
  495. userCred mcclient.TokenCredential,
  496. query jsonutils.JSONObject,
  497. input api.CachedImageManagerCacheImageInput,
  498. ) (jsonutils.JSONObject, error) {
  499. if len(input.ImageId) == 0 {
  500. return nil, httperrors.NewMissingParameterError("image_id")
  501. }
  502. s := auth.GetAdminSession(ctx, options.Options.Region)
  503. obj, err := image.Images.Get(s, input.ImageId, nil)
  504. if err != nil {
  505. return nil, errors.Wrap(err, "modules.Images.Get")
  506. }
  507. cimg, err := manager.cacheGlanceImageInfo(ctx, userCred, obj)
  508. if err != nil {
  509. return nil, errors.Wrap(err, "manager.cacheGlanceImageInfo")
  510. }
  511. if input.AutoCache {
  512. storageCaches, err := StoragecacheManager.FetchStoragecachesByFilters(ctx, input.SStorageCacheFilters)
  513. if err != nil {
  514. return nil, errors.Wrap(err, "FetchStoragecachesByFilters")
  515. }
  516. err = StoragecacheManager.StartImageCacheTask(ctx, userCred, storageCaches, api.CacheImageInput{
  517. ImageId: cimg.Id,
  518. PreCache: true,
  519. })
  520. if err != nil {
  521. return nil, errors.Wrap(err, "StoragecacheManager.StartImageCacheTask")
  522. }
  523. }
  524. return nil, nil
  525. }
  526. func (cachedImage *SCachedimage) addRefCount() {
  527. if cachedImage.GetStatus() != api.CACHED_IMAGE_STATUS_ACTIVE {
  528. return
  529. }
  530. _, err := db.Update(cachedImage, func() error {
  531. cachedImage.RefCount += 1
  532. cachedImage.LastRef = timeutils.UtcNow()
  533. return nil
  534. })
  535. if err != nil {
  536. log.Errorf("addRefCount fail %s", err)
  537. }
  538. }
  539. func (cachedImage *SCachedimage) ChooseSourceStoragecacheInRange(hostType string, excludes []string, rangeObjs []interface{}) (*SStoragecachedimage, error) {
  540. storageCachedImage := StoragecachedimageManager.Query().SubQuery()
  541. storage := StorageManager.Query().SubQuery()
  542. hostStorage := HoststorageManager.Query().SubQuery()
  543. host := HostManager.Query().SubQuery()
  544. scimgs := make([]SStoragecachedimage, 0)
  545. q := storageCachedImage.Query().
  546. Join(storage, sqlchemy.Equals(storage.Field("storagecache_id"), storageCachedImage.Field("storagecache_id"))).
  547. Join(hostStorage, sqlchemy.Equals(hostStorage.Field("storage_id"), storage.Field("id"))).
  548. Join(host, sqlchemy.Equals(hostStorage.Field("host_id"), host.Field("id"))).
  549. Filter(sqlchemy.Equals(storageCachedImage.Field("cachedimage_id"), cachedImage.Id)).
  550. Filter(sqlchemy.Equals(storageCachedImage.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE)).
  551. Filter(sqlchemy.Equals(host.Field("status"), api.HOST_STATUS_RUNNING)).
  552. Filter(sqlchemy.IsTrue(host.Field("enabled"))).
  553. Filter(sqlchemy.Equals(host.Field("host_status"), api.HOST_ONLINE)).
  554. Filter(sqlchemy.IsTrue(storage.Field("enabled"))).
  555. Filter(sqlchemy.In(storage.Field("status"), []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE})).
  556. Filter(sqlchemy.Equals(storage.Field("storage_type"), api.STORAGE_LOCAL))
  557. if len(excludes) > 0 {
  558. q = q.Filter(sqlchemy.NotIn(host.Field("id"), excludes))
  559. }
  560. if len(hostType) > 0 {
  561. q = q.Filter(sqlchemy.Equals(host.Field("host_type"), hostType))
  562. }
  563. for _, rangeObj := range rangeObjs {
  564. switch v := rangeObj.(type) {
  565. case *SHost:
  566. q = q.Filter(sqlchemy.Equals(host.Field("id"), v.Id))
  567. case *SZone:
  568. q = q.Filter(sqlchemy.Equals(host.Field("zone_id"), v.Id))
  569. case *SCloudprovider:
  570. q = q.Filter(sqlchemy.Equals(host.Field("manager_id"), v.Id))
  571. }
  572. }
  573. err := db.FetchModelObjects(StoragecachedimageManager, q, &scimgs)
  574. if err != nil {
  575. return nil, err
  576. }
  577. if len(scimgs) == 0 {
  578. return nil, nil
  579. }
  580. return &scimgs[rand.Intn(len(scimgs))], nil
  581. }
  582. func (manager *SCachedimageManager) ImageAddRefCount(imageId string) {
  583. cachedObj, _ := manager.FetchById(imageId)
  584. if cachedObj != nil {
  585. cachedImage := (cachedObj).(*SCachedimage)
  586. cachedImage.addRefCount()
  587. }
  588. }
  589. func (cachedImage *SCachedimage) canDeleteLastCache() bool {
  590. cnt, err := cachedImage.getStoragecacheCount()
  591. if err != nil {
  592. // database error
  593. return false
  594. }
  595. if cnt != 1 {
  596. return true
  597. }
  598. if cachedImage.isReferenceSessionExpire() {
  599. return true
  600. }
  601. return false
  602. }
  603. func (cachedImage *SCachedimage) syncWithCloudImage(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, image cloudprovider.ICloudImage, provider *SCloudprovider) error {
  604. diff, err := db.UpdateWithLock(ctx, cachedImage, func() error {
  605. if options.Options.EnableSyncName {
  606. newName, err := db.GenerateAlterName(cachedImage, image.GetName())
  607. if err != nil {
  608. return errors.Wrap(err, "GenerateAlterName")
  609. }
  610. cachedImage.Name = newName
  611. }
  612. cachedImage.Size = image.GetSizeByte()
  613. cachedImage.ExternalId = image.GetGlobalId()
  614. cachedImage.ImageType = string(image.GetImageType())
  615. cachedImage.PublicScope = string(image.GetPublicScope())
  616. cachedImage.Status = image.GetStatus()
  617. if image.GetPublicScope() == rbacscope.ScopeSystem {
  618. cachedImage.IsPublic = true
  619. }
  620. cachedImage.UEFI = tristate.NewFromBool(cloudprovider.IsUEFI(image))
  621. sImage := cloudprovider.CloudImage2Image(image)
  622. cachedImage.Info = jsonutils.Marshal(&sImage)
  623. cachedImage.LastSync = time.Now().UTC()
  624. return nil
  625. })
  626. db.OpsLog.LogSyncUpdate(cachedImage, diff, userCred)
  627. if provider != nil {
  628. SyncCloudProject(ctx, userCred, cachedImage, ownerId, image, provider)
  629. }
  630. return err
  631. }
  632. func (manager *SCachedimageManager) newFromCloudImage(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, image cloudprovider.ICloudImage, provider *SCloudprovider) (*SCachedimage, error) {
  633. cachedImage := SCachedimage{}
  634. cachedImage.SetModelManager(manager, &cachedImage)
  635. cachedImage.Size = image.GetSizeByte()
  636. cachedImage.UEFI = tristate.NewFromBool(cloudprovider.IsUEFI(image))
  637. sImage := cloudprovider.CloudImage2Image(image)
  638. cachedImage.Info = jsonutils.Marshal(&sImage)
  639. cachedImage.LastSync = time.Now().UTC()
  640. cachedImage.ImageType = string(image.GetImageType())
  641. cachedImage.ExternalId = image.GetGlobalId()
  642. cachedImage.Status = image.GetStatus()
  643. cachedImage.ProjectId = ownerId.GetProjectId()
  644. cachedImage.DomainId = ownerId.GetProjectDomainId()
  645. cachedImage.PublicScope = string(image.GetPublicScope())
  646. switch image.GetPublicScope() {
  647. case rbacscope.ScopeNone:
  648. default:
  649. cachedImage.IsPublic = true
  650. }
  651. var err error
  652. err = func() error {
  653. lockman.LockRawObject(ctx, manager.Keyword(), "name")
  654. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
  655. cachedImage.Name, err = db.GenerateName(ctx, manager, nil, image.GetName())
  656. if err != nil {
  657. return err
  658. }
  659. return manager.TableSpec().Insert(ctx, &cachedImage)
  660. }()
  661. if err != nil {
  662. return nil, err
  663. }
  664. if provider != nil {
  665. SyncCloudProject(ctx, userCred, &cachedImage, ownerId, image, provider)
  666. }
  667. return &cachedImage, nil
  668. }
  669. func (image *SCachedimage) requestRefreshExternalImage(ctx context.Context, userCred mcclient.TokenCredential) (*cloudprovider.SImage, error) {
  670. caches := image.getValidStoragecache()
  671. if len(caches) == 0 {
  672. log.Errorf("requestRefreshExternalImage: no valid storage cache")
  673. return nil, fmt.Errorf("no valid storage cache")
  674. }
  675. var cache *SStoragecache
  676. var cachedImage *SStoragecachedimage
  677. for i := range caches {
  678. ci := StoragecachedimageManager.GetStoragecachedimage(caches[i].Id, image.Id)
  679. if ci != nil {
  680. cache = &caches[i]
  681. cachedImage = ci
  682. break
  683. }
  684. }
  685. if cache == nil {
  686. return nil, fmt.Errorf("no cached image found")
  687. }
  688. iCache, err := cache.GetIStorageCache(ctx)
  689. if err != nil {
  690. log.Errorf("GetIStorageCache fail %s", err)
  691. return nil, err
  692. }
  693. iImage, err := iCache.GetIImageById(cachedImage.ExternalId)
  694. if err != nil {
  695. log.Errorf("iCache.GetIImageById fail %s", err)
  696. return nil, err
  697. }
  698. err = image.syncWithCloudImage(ctx, userCred, nil, iImage, nil)
  699. if err != nil {
  700. log.Errorf("image.syncWithCloudImage fail %s", err)
  701. return nil, err
  702. }
  703. return image.GetImage()
  704. }
  705. func (image *SCachedimage) getValidStoragecache() []SStoragecache {
  706. storagecaches := StoragecacheManager.Query().SubQuery()
  707. storagecacheimages := StoragecachedimageManager.Query().SubQuery()
  708. providers := usableCloudProviders().SubQuery()
  709. q := storagecaches.Query()
  710. q = q.Join(providers, sqlchemy.Equals(providers.Field("id"), storagecaches.Field("manager_id")))
  711. q = q.Join(storagecacheimages, sqlchemy.Equals(storagecaches.Field("id"), storagecacheimages.Field("storagecache_id")))
  712. q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("cachedimage_id"), image.Id))
  713. q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE))
  714. caches := make([]SStoragecache, 0)
  715. err := db.FetchModelObjects(StoragecacheManager, q, &caches)
  716. if err != nil {
  717. log.Errorf("getValidStoragecache fail %s", err)
  718. return nil
  719. }
  720. return caches
  721. }
  722. func (image *SCachedimage) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  723. db.SharedResourceManager.CleanModelShares(ctx, userCred, image.GetISharableVirtualModel())
  724. return db.RealDeleteModel(ctx, userCred, image)
  725. }
  726. func (image *SCachedimage) GetRegions() ([]SCloudregion, error) {
  727. regions := []SCloudregion{}
  728. caches := image.getValidStoragecache()
  729. for _, cache := range caches {
  730. region, err := cache.GetRegion()
  731. if err != nil {
  732. return nil, err
  733. }
  734. regions = append(regions, *region)
  735. }
  736. return regions, nil
  737. }
  738. func (image *SCachedimage) GetUsableZoneIds() ([]string, error) {
  739. zones := ZoneManager.Query().SubQuery()
  740. storages := StorageManager.Query().SubQuery()
  741. storagecaches := StoragecacheManager.Query().SubQuery()
  742. storagecacheimages := StoragecachedimageManager.Query().SubQuery()
  743. providers := usableCloudProviders().SubQuery()
  744. q := zones.Query(zones.Field("id"))
  745. q = q.Join(storages, sqlchemy.Equals(q.Field("id"), storages.Field("zone_id")))
  746. q = q.Join(storagecaches, sqlchemy.Equals(storages.Field("storagecache_id"), storagecaches.Field("id")))
  747. q = q.Join(providers, sqlchemy.Equals(providers.Field("id"), storagecaches.Field("manager_id")))
  748. q = q.Join(storagecacheimages, sqlchemy.Equals(storagecaches.Field("id"), storagecacheimages.Field("storagecache_id")))
  749. q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("cachedimage_id"), image.Id))
  750. q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE))
  751. q = q.Filter(sqlchemy.Equals(q.Field("status"), api.ZONE_ENABLE))
  752. result := []string{}
  753. rows, err := q.Rows()
  754. if err != nil {
  755. if err == sql.ErrNoRows {
  756. return nil, nil
  757. }
  758. return nil, err
  759. }
  760. defer rows.Close()
  761. for rows.Next() {
  762. var zoneId string
  763. if err := rows.Scan(&zoneId); err != nil {
  764. return nil, err
  765. }
  766. result = append(result, zoneId)
  767. }
  768. return result, nil
  769. }
  770. func (image *SCachedimage) GetCloudprovider() (*SCloudprovider, error) {
  771. caches := image.getValidStoragecache()
  772. if len(caches) == 0 {
  773. return nil, fmt.Errorf("no valid storagecache for image %s(%s)", image.Name, image.Id)
  774. }
  775. cloudprovider := caches[0].GetCloudprovider()
  776. if cloudprovider == nil {
  777. return nil, fmt.Errorf("failed to found cloudprovider for storagecache %s(%s)", caches[0].Name, caches[0].Id)
  778. }
  779. return cloudprovider, nil
  780. }
  781. // 缓存镜像列表
  782. func (manager *SCachedimageManager) ListItemFilter(
  783. ctx context.Context,
  784. q *sqlchemy.SQuery,
  785. userCred mcclient.TokenCredential,
  786. query api.CachedimageListInput,
  787. ) (*sqlchemy.SQuery, error) {
  788. var err error
  789. q, err = manager.SSharableBaseResourceManager.ListItemFilter(ctx, q, userCred, query.SharableResourceBaseListInput)
  790. if err != nil {
  791. return nil, errors.Wrapf(err, "SSharableBaseResourceManager.ListItemFilter")
  792. }
  793. q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.SharableVirtualResourceListInput)
  794. if err != nil {
  795. return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemFilter")
  796. }
  797. q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
  798. if err != nil {
  799. return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
  800. }
  801. {
  802. storagecachedImages := StoragecachedimageManager.Query("cachedimage_id").Equals("status", api.CACHED_IMAGE_STATUS_ACTIVE) // .SubQuery()
  803. storagecachedImages = storagecachedImages.Snapshot()
  804. storagesQ := StorageManager.Query("storagecache_id")
  805. storagesQ = storagesQ.Snapshot()
  806. if query.Valid {
  807. storagesQ = storagesQ.In("status", []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}).IsTrue("enabled")
  808. }
  809. if len(query.CloudproviderId) > 0 {
  810. storagesQ = storagesQ.In("manager_id", query.CloudproviderId)
  811. }
  812. if len(query.CloudEnv) > 0 {
  813. switch query.CloudEnv {
  814. case api.CLOUD_ENV_PUBLIC_CLOUD:
  815. pubQ := CloudproviderManager.GetPublicProviderIdsQuery()
  816. storagesQ = storagesQ.In("manager_id", pubQ)
  817. case api.CLOUD_ENV_PRIVATE_CLOUD:
  818. privQ := CloudproviderManager.GetPrivateProviderIdsQuery()
  819. storagesQ = storagesQ.In("manager_id", privQ)
  820. case api.CLOUD_ENV_ON_PREMISE:
  821. storagesQ = storagesQ.IsNullOrEmpty("manager_id")
  822. case api.CLOUD_ENV_PRIVATE_ON_PREMISE:
  823. privQ := CloudproviderManager.GetPrivateProviderIdsQuery()
  824. storagesQ = storagesQ.Filter(
  825. sqlchemy.OR(
  826. sqlchemy.In(storagesQ.Field("manager_id"), privQ),
  827. sqlchemy.IsNullOrEmpty(storagesQ.Field("manager_id")),
  828. ),
  829. )
  830. }
  831. }
  832. if len(query.HostSchedtagId) > 0 {
  833. hostschedtags := HostschedtagManager.Query("host_id").Equals("schedtag_id", query.HostSchedtagId)
  834. hoststorages := HoststorageManager.Query("storage_id").In("host_id", hostschedtags.Distinct().SubQuery())
  835. storagesQ = storagesQ.In("id", hoststorages.Distinct().SubQuery())
  836. }
  837. zonesQ := ZoneManager.Query("id")
  838. zonesQ = zonesQ.Snapshot()
  839. if len(query.ZoneId) > 0 {
  840. zonesQ = zonesQ.Equals("id", query.ZoneId)
  841. }
  842. if len(query.CloudregionId) > 0 {
  843. zonesQ = zonesQ.In("cloudregion_id", query.CloudregionId)
  844. }
  845. if zonesQ.IsAltered() {
  846. storagesQ = storagesQ.Filter(
  847. sqlchemy.In(storagesQ.Field("zone_id"), zonesQ.Distinct().SubQuery()),
  848. )
  849. }
  850. if storagesQ.IsAltered() {
  851. storagecachedImages = storagecachedImages.Filter(
  852. sqlchemy.In(storagecachedImages.Field("storagecache_id"), storagesQ.Distinct().SubQuery()),
  853. )
  854. }
  855. if storagecachedImages.IsAltered() {
  856. q = q.In("id", storagecachedImages.Distinct().SubQuery())
  857. }
  858. }
  859. if len(query.ImageType) > 0 {
  860. q = q.Equals("image_type", query.ImageType)
  861. }
  862. return q, nil
  863. }
  864. func (manager *SCachedimageManager) OrderByExtraFields(
  865. ctx context.Context,
  866. q *sqlchemy.SQuery,
  867. userCred mcclient.TokenCredential,
  868. query api.CachedimageListInput,
  869. ) (*sqlchemy.SQuery, error) {
  870. var err error
  871. q, err = manager.SSharableVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.SharableVirtualResourceListInput)
  872. if err != nil {
  873. return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.OrderByExtraFields")
  874. }
  875. return q, nil
  876. }
  877. func (manager *SCachedimageManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  878. var err error
  879. q, err = manager.SSharableVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
  880. if err == nil {
  881. return q, nil
  882. }
  883. return q, httperrors.ErrNotFound
  884. }
  885. func (manager *SCachedimageManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
  886. q, err := manager.SSharableVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  887. if err != nil {
  888. return nil, errors.Wrapf(err, "SSharableVirtualResourceBaseManager.L.ListItemExportKeys")
  889. }
  890. return q, nil
  891. }
  892. // 清理已经删除的镜像缓存
  893. func (manager *SCachedimageManager) AutoCleanImageCaches(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  894. defer func() {
  895. err := manager.cleanExternalImages()
  896. if err != nil {
  897. log.Errorf("cleanExternalImages error: %v", err)
  898. }
  899. err = manager.cleanStoragecachedimages()
  900. if err != nil {
  901. log.Errorf("cleanStoragecachedimages error: %v", err)
  902. }
  903. }()
  904. lastSync := time.Now().Add(time.Duration(-1*api.CACHED_IMAGE_REFERENCE_SESSION_EXPIRE_SECONDS) * time.Second)
  905. q := manager.Query()
  906. q = q.LT("last_sync", lastSync).Equals("status", api.CACHED_IMAGE_STATUS_ACTIVE).IsNullOrEmpty("external_id").Limit(50)
  907. caches := []SCachedimage{}
  908. err := db.FetchModelObjects(manager, q, &caches)
  909. if err != nil {
  910. return
  911. }
  912. s := auth.GetAdminSession(ctx, options.Options.Region)
  913. for i := range caches {
  914. _, err := image.Images.Get(s, caches[i].Id, nil)
  915. if err != nil {
  916. if e, ok := err.(*httputils.JSONClientError); ok && e.Code == 404 {
  917. e := caches[i].ValidateDeleteCondition(ctx, nil)
  918. if e == nil {
  919. caches[i].Delete(ctx, userCred)
  920. continue
  921. }
  922. deleteMark := "-deleted@"
  923. db.Update(&caches[i], func() error {
  924. if !strings.Contains(caches[i].Name, deleteMark) {
  925. caches[i].Name = fmt.Sprintf("%s%s%s", caches[i].Name, deleteMark, timeutils.ShortDate(time.Now()))
  926. }
  927. return nil
  928. })
  929. }
  930. continue
  931. }
  932. db.Update(&caches[i], func() error {
  933. caches[i].LastSync = time.Now()
  934. return nil
  935. })
  936. }
  937. }
  938. func (manager *SCachedimageManager) getExpireExternalImageIds() ([]string, error) {
  939. ids := []string{}
  940. templatedIds := DiskManager.Query("template_id").IsNotEmpty("template_id").Distinct().SubQuery()
  941. cachedimageIds := StoragecachedimageManager.Query("cachedimage_id").Distinct().SubQuery()
  942. externalIds := CloudimageManager.Query("external_id").Distinct().SubQuery()
  943. q := manager.RawQuery("id")
  944. q = q.Filter(
  945. sqlchemy.AND(
  946. sqlchemy.IsNotEmpty(q.Field("external_id")),
  947. sqlchemy.NotIn(q.Field("id"), templatedIds),
  948. sqlchemy.NotIn(q.Field("id"), cachedimageIds),
  949. sqlchemy.NotIn(q.Field("external_id"), externalIds),
  950. ),
  951. )
  952. rows, err := q.Rows()
  953. if err != nil {
  954. if errors.Cause(err) == sql.ErrNoRows {
  955. return ids, nil
  956. }
  957. return nil, errors.Wrap(err, "Query")
  958. }
  959. defer rows.Close()
  960. for rows.Next() {
  961. var id string
  962. err := rows.Scan(&id)
  963. if err != nil {
  964. return nil, errors.Wrapf(err, "rows.Scan")
  965. }
  966. ids = append(ids, id)
  967. }
  968. return ids, nil
  969. }
  970. func (manager *SCachedimageManager) cleanExternalImages() error {
  971. ids, err := manager.getExpireExternalImageIds()
  972. if err != nil {
  973. return errors.Wrapf(err, "getExpireExternalImageIds")
  974. }
  975. err = db.Purge(manager, "id", ids, true)
  976. if err != nil {
  977. return errors.Wrapf(err, "purge")
  978. }
  979. log.Debugf("clean %d expired external images", len(ids))
  980. return nil
  981. }
  982. func (manager *SCachedimageManager) cleanStoragecachedimages() error {
  983. ids, err := db.FetchField(StoragecachedimageManager, "row_id", func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  984. sq := manager.Query("id").Distinct().SubQuery()
  985. return q.NotIn("cachedimage_id", sq)
  986. })
  987. if err != nil {
  988. return errors.Wrapf(err, "getExpireExternalImageIds")
  989. }
  990. err = db.Purge(StoragecachedimageManager, "row_id", ids, true)
  991. if err != nil {
  992. return errors.Wrapf(err, "purge")
  993. }
  994. log.Debugf("clean %d invalid storagecachedimages", len(ids))
  995. return nil
  996. }
  997. func (image *SCachedimage) GetAllClassMetadata() (map[string]string, error) {
  998. meta, err := image.SSharableVirtualResourceBase.GetAllClassMetadata()
  999. if err != nil {
  1000. return nil, errors.Wrap(err, "SSharableVirtualResourceBase.GetAllClassMetadata")
  1001. }
  1002. metaDict, _ := image.Info.GetMap("metadata")
  1003. for k, v := range metaDict {
  1004. if !strings.HasPrefix(k, db.CLASS_TAG_PREFIX) {
  1005. continue
  1006. }
  1007. meta[k[len(db.CLASS_TAG_PREFIX):]], _ = v.GetString()
  1008. }
  1009. return meta, nil
  1010. }