imagecachemanager_local.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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 storageman
  15. import (
  16. "context"
  17. "os"
  18. "sync"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/regutils"
  23. api "yunion.io/x/onecloud/pkg/apis/compute"
  24. imageapi "yunion.io/x/onecloud/pkg/apis/image"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  26. "yunion.io/x/onecloud/pkg/hostman/hostutils"
  27. "yunion.io/x/onecloud/pkg/hostman/options"
  28. "yunion.io/x/onecloud/pkg/httperrors"
  29. "yunion.io/x/onecloud/pkg/util/fileutils2"
  30. "yunion.io/x/onecloud/pkg/util/procutils"
  31. )
  32. type SLocalImageCacheManager struct {
  33. SBaseImageCacheManager
  34. // limit int
  35. // isTemplate bool
  36. lock lockman.ILockManager
  37. storage IStorage
  38. }
  39. func NewLocalImageCacheManager(manager IStorageManager, cachePath string, storagecacheId string, storage IStorage) *SLocalImageCacheManager {
  40. imageCacheManager := new(SLocalImageCacheManager)
  41. imageCacheManager.lock = lockman.NewInMemoryLockManager()
  42. imageCacheManager.storageManager = manager
  43. imageCacheManager.storagecacaheId = storagecacheId
  44. imageCacheManager.cachePath = cachePath
  45. imageCacheManager.storage = storage
  46. // imageCacheManager.limit = limit
  47. // imageCacheManager.isTemplate = isTemplete
  48. imageCacheManager.cachedImages = &sync.Map{} // make(map[string]IImageCache, 0)
  49. if !fileutils2.Exists(cachePath) {
  50. procutils.NewCommand("mkdir", "-p", cachePath).Run()
  51. }
  52. imageCacheManager.loadCache(context.Background())
  53. return imageCacheManager
  54. }
  55. func (c *SLocalImageCacheManager) IsLocal() bool {
  56. if c.storage != nil {
  57. return false
  58. }
  59. return true
  60. }
  61. func (c *SLocalImageCacheManager) GetStorageType() string {
  62. if c.storage == nil {
  63. return api.STORAGE_LOCAL
  64. }
  65. return c.storage.StorageType()
  66. }
  67. func (c *SLocalImageCacheManager) loadCache(ctx context.Context) {
  68. if len(c.cachePath) == 0 {
  69. return
  70. }
  71. c.lock.LockRawObject(ctx, "LOCAL", "image-cache")
  72. defer c.lock.ReleaseRawObject(ctx, "LOCAL", "image-cache")
  73. files, _ := os.ReadDir(c.cachePath)
  74. for _, f := range files {
  75. if regutils.MatchUUIDExact(f.Name()) {
  76. c.LoadImageCache(f.Name())
  77. }
  78. }
  79. }
  80. func (c *SLocalImageCacheManager) LoadImageCache(imageId string) {
  81. imageCache := NewLocalImageCache(imageId, c)
  82. if imageCache.Load() == nil {
  83. c.cachedImages.Store(imageId, imageCache)
  84. }
  85. }
  86. func (c *SLocalImageCacheManager) GetImage(imageId string) IImageCache {
  87. imgObj, ok := c.cachedImages.Load(imageId)
  88. if !ok {
  89. return nil
  90. }
  91. return imgObj.(IImageCache)
  92. }
  93. func (c *SLocalImageCacheManager) AcquireImage(ctx context.Context, input api.CacheImageInput, callback func(progress, progressMbps float64, totalSizeMb int64)) (IImageCache, error) {
  94. c.lock.LockRawObject(ctx, "image-cache", input.ImageId)
  95. defer c.lock.ReleaseRawObject(ctx, "image-cache", input.ImageId)
  96. imgObj, ok := c.cachedImages.Load(input.ImageId)
  97. if !ok {
  98. imgObj = NewLocalImageCache(input.ImageId, c)
  99. c.cachedImages.Store(input.ImageId, imgObj)
  100. }
  101. if callback == nil && len(input.ServerId) > 0 {
  102. callback = func(progress, progressMbps float64, totalSizeMb int64) {
  103. if len(input.ServerId) > 0 {
  104. hostutils.UpdateServerProgress(ctx, input.ServerId, progress, progressMbps)
  105. }
  106. }
  107. }
  108. img := imgObj.(IImageCache)
  109. return img, img.Acquire(ctx, input, callback)
  110. }
  111. func (c *SLocalImageCacheManager) ReleaseImage(ctx context.Context, imageId string) {
  112. c.lock.LockRawObject(ctx, "image-cache", imageId)
  113. defer c.lock.ReleaseRawObject(ctx, "image-cache", imageId)
  114. if img, ok := c.cachedImages.Load(imageId); ok {
  115. img.(IImageCache).Release()
  116. }
  117. }
  118. func (c *SLocalImageCacheManager) DeleteImageCache(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
  119. input, ok := data.(api.UncacheImageInput)
  120. if !ok {
  121. return nil, hostutils.ParamsError
  122. }
  123. cachedImagesInUser := findCachedImagesInUse(c)
  124. if cachedImagesInUser.Contains(input.ImageId) {
  125. return nil, httperrors.NewResourceBusyError("image cache is in use")
  126. }
  127. return nil, c.RemoveImage(ctx, input.ImageId)
  128. }
  129. func (c *SLocalImageCacheManager) RemoveImage(ctx context.Context, imageId string) error {
  130. c.lock.LockRawObject(ctx, "image-cache", imageId)
  131. defer c.lock.ReleaseRawObject(ctx, "image-cache", imageId)
  132. if img, ok := c.cachedImages.Load(imageId); ok {
  133. c.cachedImages.Delete(imageId)
  134. return img.(IImageCache).Remove(ctx)
  135. }
  136. return nil
  137. }
  138. func (c *SLocalImageCacheManager) PrefetchImageCache(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
  139. input, ok := data.(api.CacheImageInput)
  140. if !ok {
  141. return nil, hostutils.ParamsError
  142. }
  143. input.Zone = c.GetStorageManager().GetZoneId()
  144. if len(input.ImageId) == 0 {
  145. return nil, httperrors.NewMissingParameterError("image_id")
  146. }
  147. ret := struct {
  148. ImageId string
  149. Path string
  150. Name string
  151. Size int64
  152. }{}
  153. imgCache, err := c.AcquireImage(ctx, input, nil)
  154. if err != nil {
  155. return nil, errors.Wrapf(err, "AcquireImage")
  156. }
  157. defer imgCache.Release()
  158. ret.ImageId = input.ImageId
  159. ret.Path = imgCache.GetPath()
  160. if desc := imgCache.GetDesc(); desc != nil {
  161. ret.Name = desc.Name
  162. ret.Size = desc.SizeMb * 1024 * 1024 // ??? convert back to bytes?
  163. }
  164. if ret.Size == 0 {
  165. fi, err := os.Stat(imgCache.GetPath())
  166. if err != nil {
  167. log.Errorf("os.Stat(%s) error: %v", imgCache.GetPath(), err)
  168. } else {
  169. ret.Size = fi.Size()
  170. }
  171. }
  172. if len(ret.Name) == 0 {
  173. ret.Name = input.ImageId
  174. }
  175. if imgCache.GetDesc().Format == imageapi.IMAGE_DISK_FORMAT_TGZ {
  176. accessDir, err := imgCache.GetAccessDirectory()
  177. if err != nil {
  178. return nil, errors.Wrapf(err, "untar to %s", accessDir)
  179. }
  180. }
  181. return jsonutils.Marshal(ret), nil
  182. }
  183. func (c *SLocalImageCacheManager) getTotalSize(ctx context.Context) (int64, map[string]IImageCache) {
  184. total := int64(0)
  185. images := make(map[string]IImageCache)
  186. c.cachedImages.Range(func(imgId, imgObj any) bool {
  187. img := imgObj.(IImageCache)
  188. imgDesc := img.GetDesc()
  189. if imgDesc != nil {
  190. total += img.GetDesc().SizeMb
  191. images[imgId.(string)] = img
  192. }
  193. return true
  194. })
  195. return total, images
  196. }
  197. func (c *SLocalImageCacheManager) CleanImageCachefiles(ctx context.Context) {
  198. totalSize, images := c.getTotalSize(ctx)
  199. storageSize := 0
  200. if c.storage != nil {
  201. // shared file storage
  202. storageSize = c.storage.GetCapacityMb()
  203. } else {
  204. storageSize = c.storageManager.(*SStorageManager).GetTotalLocalCapacity()
  205. }
  206. ratio := float64(totalSize) / float64(storageSize)
  207. log.Infof("SLocalImageCacheManager %s total size %dMB storage %dMB ratio %f expect ratio %d", c.cachePath, totalSize, storageSize, ratio, options.HostOptions.ImageCacheCleanupPercentage)
  208. if int(ratio*100) < options.HostOptions.ImageCacheCleanupPercentage {
  209. return
  210. }
  211. deletedMb, err := cleanImages(ctx, c, images)
  212. if err != nil {
  213. log.Errorf("SLocalImageCacheManager clean image %s fail %s", c.cachePath, err)
  214. } else {
  215. log.Infof("SLocalImageCacheManager %s cleanup %dMB", c.cachePath, deletedMb)
  216. }
  217. }