storage.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. "fmt"
  18. "io"
  19. "os"
  20. "strings"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/qemuimgfmt"
  23. "yunion.io/x/onecloud/pkg/apis/image"
  24. "yunion.io/x/onecloud/pkg/image/drivers/s3"
  25. "yunion.io/x/onecloud/pkg/image/options"
  26. "yunion.io/x/onecloud/pkg/util/fileutils2"
  27. "yunion.io/x/onecloud/pkg/util/procutils"
  28. )
  29. var local IImageStorage = &LocalStorage{}
  30. var s3Instance IImageStorage = &S3Storage{}
  31. var storage IImageStorage
  32. func GetStorage() IImageStorage {
  33. return storage
  34. }
  35. func GetImage(ctx context.Context, location string) (int64, io.ReadCloser, error) {
  36. switch {
  37. case strings.HasPrefix(location, image.S3Prefix):
  38. return s3Instance.GetImage(ctx, location[len(image.S3Prefix):])
  39. case strings.HasPrefix(location, image.LocalFilePrefix):
  40. return local.GetImage(ctx, location[len(image.LocalFilePrefix):])
  41. default:
  42. return local.GetImage(ctx, location)
  43. }
  44. }
  45. func RemoveImage(ctx context.Context, location string) error {
  46. switch {
  47. case strings.HasPrefix(location, image.S3Prefix):
  48. return s3Instance.RemoveImage(ctx, location[len(image.S3Prefix):])
  49. case strings.HasPrefix(location, image.LocalFilePrefix):
  50. return local.RemoveImage(ctx, location[len(image.LocalFilePrefix):])
  51. default:
  52. return local.RemoveImage(ctx, location)
  53. }
  54. }
  55. func IsCheckStatusEnabled(img *SImage) bool {
  56. switch {
  57. case strings.HasPrefix(img.Location, image.S3Prefix):
  58. return s3Instance.IsCheckStatusEnabled()
  59. case strings.HasPrefix(img.Location, image.LocalFilePrefix):
  60. return local.IsCheckStatusEnabled()
  61. default:
  62. return local.IsCheckStatusEnabled()
  63. }
  64. }
  65. func Init(storageBackend string) {
  66. switch storageBackend {
  67. case image.IMAGE_STORAGE_DRIVER_LOCAL:
  68. storage = &LocalStorage{}
  69. case image.IMAGE_STORAGE_DRIVER_S3:
  70. storage = &S3Storage{}
  71. default:
  72. storage = &LocalStorage{}
  73. }
  74. }
  75. type IImageStorage interface {
  76. Type() string
  77. SaveImage(context.Context, string, func(int64)) (string, error)
  78. CleanTempfile(string) error
  79. GetImage(context.Context, string) (int64, io.ReadCloser, error)
  80. RemoveImage(context.Context, string) error
  81. IsCheckStatusEnabled() bool
  82. ConvertImage(ctx context.Context, image *SImage, targetFormat string, progresser func(saved int64)) (*SConverImageInfo, error)
  83. }
  84. type LocalStorage struct{}
  85. func (s *LocalStorage) Type() string {
  86. return image.IMAGE_STORAGE_DRIVER_LOCAL
  87. }
  88. func (s *LocalStorage) SaveImage(ctx context.Context, imagePath string, progresser func(saved int64)) (string, error) {
  89. return fmt.Sprintf("%s%s", LocalFilePrefix, imagePath), nil
  90. }
  91. func (s *LocalStorage) CleanTempfile(filePath string) error {
  92. return nil
  93. }
  94. func (s *LocalStorage) GetImage(ctx context.Context, imagePath string) (int64, io.ReadCloser, error) {
  95. fstat, err := os.Stat(imagePath)
  96. if err != nil {
  97. return -1, nil, errors.Wrapf(err, "stat file %s", imagePath)
  98. }
  99. f, err := os.Open(imagePath)
  100. if err != nil {
  101. return -1, nil, errors.Wrapf(err, "open file %s", imagePath)
  102. }
  103. return fstat.Size(), f, nil
  104. }
  105. func (s *LocalStorage) ConvertImage(ctx context.Context, image *SImage, targetFormat string, progresser func(saved int64)) (*SConverImageInfo, error) {
  106. location := image.GetPath(targetFormat)
  107. img, err := image.getQemuImage()
  108. if err != nil {
  109. return nil, errors.Wrap(err, "unable to image.getQemuImage")
  110. }
  111. nimg, err := img.Clone(location, qemuimgfmt.String2ImageFormat(targetFormat), true)
  112. if err != nil {
  113. return nil, errors.Wrap(err, "unable to img.Clone")
  114. }
  115. return &SConverImageInfo{
  116. Location: fmt.Sprintf("%s%s", LocalFilePrefix, location),
  117. SizeBytes: nimg.ActualSizeBytes,
  118. }, nil
  119. }
  120. func (s *LocalStorage) IsCheckStatusEnabled() bool {
  121. return true
  122. }
  123. func (s *LocalStorage) RemoveImage(ctx context.Context, imagePath string) error {
  124. return os.Remove(imagePath)
  125. }
  126. type S3Storage struct{}
  127. func imagePathToName(imagePath string) string {
  128. segs := strings.Split(imagePath, "/")
  129. return segs[len(segs)-1]
  130. }
  131. func (s *S3Storage) Type() string {
  132. return image.IMAGE_STORAGE_DRIVER_S3
  133. }
  134. func (s *S3Storage) SaveImage(ctx context.Context, imagePath string, progresser func(saved int64)) (string, error) {
  135. if !fileutils2.IsFile(imagePath) {
  136. return "", fmt.Errorf("%s not valid file", imagePath)
  137. }
  138. return s3.Put(ctx, imagePath, imagePathToName(imagePath), options.Options.S3UploadPartSizeMb, options.Options.S3UploadParallel, progresser)
  139. }
  140. func (s *S3Storage) CleanTempfile(filePath string) error {
  141. out, err := procutils.NewCommand("rm", "-f", filePath).Output()
  142. if err != nil {
  143. return errors.Wrapf(err, "rm %s failed %s", filePath, out)
  144. }
  145. return nil
  146. }
  147. func (s *S3Storage) getTempDir() (string, error) {
  148. var dir string
  149. if options.Options.FilesystemStoreDatadir != "" {
  150. dir = options.Options.FilesystemStoreDatadir + "/image-tmp"
  151. } else {
  152. dir = "/tmp/image-tmp"
  153. }
  154. if !fileutils2.Exists(dir) {
  155. err := procutils.NewCommand("mkdir", "-p", dir).Run()
  156. if err != nil {
  157. return "", errors.Wrapf(err, "unable to create dir %s", dir)
  158. }
  159. }
  160. return dir, nil
  161. }
  162. type SConverImageInfo struct {
  163. Location string
  164. SizeBytes int64
  165. }
  166. func (s *S3Storage) ConvertImage(ctx context.Context, image *SImage, targetFormat string, progresser func(saved int64)) (*SConverImageInfo, error) {
  167. tempDir, err := s.getTempDir()
  168. if err != nil {
  169. return nil, err
  170. }
  171. location := fmt.Sprintf("%s/%s.%s", tempDir, image.GetId(), targetFormat)
  172. img, err := image.getQemuImage()
  173. if err != nil {
  174. return nil, errors.Wrap(err, "unable to image.getQemuImage")
  175. }
  176. nimg, err := img.Clone(location, qemuimgfmt.String2ImageFormat(targetFormat), true)
  177. if err != nil {
  178. return nil, errors.Wrap(err, "unable to img.Clone")
  179. }
  180. defer s.CleanTempfile(location)
  181. s3Location, err := s.SaveImage(ctx, location, progresser)
  182. if err != nil {
  183. return nil, errors.Wrap(err, "unable to SaveImage")
  184. }
  185. return &SConverImageInfo{
  186. Location: s3Location,
  187. SizeBytes: nimg.ActualSizeBytes,
  188. }, nil
  189. }
  190. func (s *S3Storage) GetImage(ctx context.Context, imagePath string) (int64, io.ReadCloser, error) {
  191. return s3.Get(ctx, imagePathToName(imagePath))
  192. }
  193. func (s *S3Storage) IsCheckStatusEnabled() bool {
  194. return options.Options.S3CheckImageStatus
  195. }
  196. func (s *S3Storage) RemoveImage(ctx context.Context, fileName string) error {
  197. return s3.Remove(ctx, fileName)
  198. }