imagecache_clean.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. "strings"
  18. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. computeapis "yunion.io/x/onecloud/pkg/apis/compute"
  23. "yunion.io/x/onecloud/pkg/hostman/hostutils"
  24. "yunion.io/x/onecloud/pkg/hostman/options"
  25. "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  26. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  27. baseoptions "yunion.io/x/onecloud/pkg/mcclient/options"
  28. "yunion.io/x/onecloud/pkg/util/qemuimg"
  29. "yunion.io/x/onecloud/pkg/util/stringutils2"
  30. )
  31. func cleanImages(ctx context.Context, manager IImageCacheManger, images map[string]IImageCache) (int64, error) {
  32. if !manager.IsLocal() {
  33. return 0, nil
  34. }
  35. storageCachedImages := make(map[string]computeapis.StoragecachedimageDetails)
  36. limit := 50
  37. total := -1
  38. for total < 0 || len(storageCachedImages) < total {
  39. params := baseoptions.BaseListOptions{}
  40. details := true
  41. params.Details = &details
  42. params.Limit = &limit
  43. offset := len(storageCachedImages)
  44. params.Offset = &offset
  45. result, err := compute.Storagecachedimages.ListDescendent(hostutils.GetComputeSession(ctx), manager.GetId(), jsonutils.Marshal(params))
  46. if err != nil {
  47. return 0, errors.Wrap(err, "List Storage Cached Images")
  48. }
  49. total = result.Total
  50. for i := range result.Data {
  51. ci := computeapis.StoragecachedimageDetails{}
  52. err := result.Data[i].Unmarshal(&ci)
  53. if err != nil {
  54. return 0, errors.Wrap(err, "Unmarshal")
  55. }
  56. storageCachedImages[ci.CachedimageId] = ci
  57. }
  58. }
  59. inUseCacheImageIds := findCachedImagesInUse(manager)
  60. log.Infof("found image caches in use: %v", inUseCacheImageIds)
  61. deleteSizeMb := int64(0)
  62. for imageId, image := range images {
  63. if _, ok := storageCachedImages[imageId]; !ok {
  64. atime := image.GetDesc().AccessAt
  65. if !atime.IsZero() && time.Now().Sub(atime) > time.Duration(options.HostOptions.ImageCacheExpireDays*86400)*time.Second {
  66. continue
  67. }
  68. if inUseCacheImageIds.Contains(imageId) {
  69. log.Infof("cached image not found but referenced by disks backing file")
  70. continue
  71. }
  72. log.Infof("cached image %s not found on region, to delete size %dMB ...", imageId, image.GetDesc().SizeMb)
  73. // not found on region, clean directly
  74. if options.HostOptions.ImageCacheCleanupDryRun {
  75. continue
  76. }
  77. err := manager.RemoveImage(ctx, imageId)
  78. if err != nil {
  79. return deleteSizeMb, errors.Wrapf(err, "RemoveImage %s", imageId)
  80. }
  81. deleteSizeMb += image.GetDesc().SizeMb
  82. }
  83. }
  84. log.Infof("to delete non-exist image caches %dMB", deleteSizeMb)
  85. for imgId := range storageCachedImages {
  86. if _, ok := images[imgId]; !ok {
  87. log.Infof("cached image %s in database not exists locally, to delete remotely ...", imgId)
  88. _, err := modules.Storagecachedimages.Detach(hostutils.GetComputeSession(ctx), manager.GetId(), imgId, nil)
  89. if err != nil {
  90. log.Errorf("Fail to delete host cached image %s at %s: %s", imgId, manager.GetId(), err)
  91. }
  92. continue
  93. }
  94. img := storageCachedImages[imgId]
  95. if img.Reference == 0 && (img.Size == 0 || time.Now().Sub(img.UpdatedAt) > time.Duration(options.HostOptions.ImageCacheExpireDays*86400)*time.Second) {
  96. if img.Size == 0 {
  97. img.Size = images[imgId].GetDesc().SizeMb * 1024 * 1024
  98. }
  99. if inUseCacheImageIds.Contains(imgId) {
  100. log.Infof("cached image database reference zero but referenced by disks locally")
  101. continue
  102. }
  103. log.Infof("image reference zero, to delete %s(%s) size %dMB", img.Cachedimage, img.CachedimageId, img.Size/1024/1024)
  104. if options.HostOptions.ImageCacheCleanupDryRun {
  105. continue
  106. }
  107. err := manager.RemoveImage(ctx, imgId)
  108. if err != nil {
  109. return deleteSizeMb, errors.Wrapf(err, "RemoveImage %s", imgId)
  110. }
  111. deleteSizeMb += img.Size / 1024 / 1024
  112. }
  113. }
  114. return deleteSizeMb, nil
  115. }
  116. func findCachedImagesInUse(manager IImageCacheManger) stringutils2.SSortedStrings {
  117. imageIds := stringutils2.NewSortedStrings(nil)
  118. for _, imageId := range findQumuImagesInUse(manager) {
  119. imageIds = imageIds.Append(imageId)
  120. }
  121. for _, imageId := range storageManager.host.GetIGuestManager().GetImageDeps(manager.GetStorageType()) {
  122. imageIds = imageIds.Append(imageId)
  123. }
  124. return imageIds
  125. }
  126. func findQumuImagesInUse(manager IImageCacheManger) stringutils2.SSortedStrings {
  127. imageIds := stringutils2.NewSortedStrings(nil)
  128. for i := range storageManager.Storages {
  129. storage := storageManager.Storages[i]
  130. if storage.GetStoragecacheId() != manager.GetId() {
  131. continue
  132. }
  133. // load storage disks used image cache
  134. disksPath, err := storage.GetDisksPath()
  135. if err != nil {
  136. log.Errorf("storage %s failed get disksPath: %s", storage.GetPath(), err)
  137. continue
  138. }
  139. for j := range disksPath {
  140. diskPath := disksPath[j]
  141. img, err := qemuimg.NewQemuImage(diskPath)
  142. if err != nil {
  143. log.Errorf("failed NewQemuImage of %s", diskPath)
  144. continue
  145. }
  146. backingChain, err := img.GetBackingChain()
  147. if err != nil {
  148. log.Errorf("disk %s failed get backing chain", diskPath)
  149. continue
  150. }
  151. for _, backingPath := range backingChain {
  152. if strings.HasPrefix(backingPath, manager.GetPath()) {
  153. imageId := strings.Trim(strings.TrimPrefix(backingPath, manager.GetPath()), "/")
  154. imageIds = imageIds.Append(imageId)
  155. }
  156. }
  157. }
  158. }
  159. return imageIds
  160. }