| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package models
- import (
- "context"
- "fmt"
- "io"
- "os"
- "strings"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/qemuimgfmt"
- "yunion.io/x/onecloud/pkg/apis/image"
- "yunion.io/x/onecloud/pkg/image/drivers/s3"
- "yunion.io/x/onecloud/pkg/image/options"
- "yunion.io/x/onecloud/pkg/util/fileutils2"
- "yunion.io/x/onecloud/pkg/util/procutils"
- )
- var local IImageStorage = &LocalStorage{}
- var s3Instance IImageStorage = &S3Storage{}
- var storage IImageStorage
- func GetStorage() IImageStorage {
- return storage
- }
- func GetImage(ctx context.Context, location string) (int64, io.ReadCloser, error) {
- switch {
- case strings.HasPrefix(location, image.S3Prefix):
- return s3Instance.GetImage(ctx, location[len(image.S3Prefix):])
- case strings.HasPrefix(location, image.LocalFilePrefix):
- return local.GetImage(ctx, location[len(image.LocalFilePrefix):])
- default:
- return local.GetImage(ctx, location)
- }
- }
- func RemoveImage(ctx context.Context, location string) error {
- switch {
- case strings.HasPrefix(location, image.S3Prefix):
- return s3Instance.RemoveImage(ctx, location[len(image.S3Prefix):])
- case strings.HasPrefix(location, image.LocalFilePrefix):
- return local.RemoveImage(ctx, location[len(image.LocalFilePrefix):])
- default:
- return local.RemoveImage(ctx, location)
- }
- }
- func IsCheckStatusEnabled(img *SImage) bool {
- switch {
- case strings.HasPrefix(img.Location, image.S3Prefix):
- return s3Instance.IsCheckStatusEnabled()
- case strings.HasPrefix(img.Location, image.LocalFilePrefix):
- return local.IsCheckStatusEnabled()
- default:
- return local.IsCheckStatusEnabled()
- }
- }
- func Init(storageBackend string) {
- switch storageBackend {
- case image.IMAGE_STORAGE_DRIVER_LOCAL:
- storage = &LocalStorage{}
- case image.IMAGE_STORAGE_DRIVER_S3:
- storage = &S3Storage{}
- default:
- storage = &LocalStorage{}
- }
- }
- type IImageStorage interface {
- Type() string
- SaveImage(context.Context, string, func(int64)) (string, error)
- CleanTempfile(string) error
- GetImage(context.Context, string) (int64, io.ReadCloser, error)
- RemoveImage(context.Context, string) error
- IsCheckStatusEnabled() bool
- ConvertImage(ctx context.Context, image *SImage, targetFormat string, progresser func(saved int64)) (*SConverImageInfo, error)
- }
- type LocalStorage struct{}
- func (s *LocalStorage) Type() string {
- return image.IMAGE_STORAGE_DRIVER_LOCAL
- }
- func (s *LocalStorage) SaveImage(ctx context.Context, imagePath string, progresser func(saved int64)) (string, error) {
- return fmt.Sprintf("%s%s", LocalFilePrefix, imagePath), nil
- }
- func (s *LocalStorage) CleanTempfile(filePath string) error {
- return nil
- }
- func (s *LocalStorage) GetImage(ctx context.Context, imagePath string) (int64, io.ReadCloser, error) {
- fstat, err := os.Stat(imagePath)
- if err != nil {
- return -1, nil, errors.Wrapf(err, "stat file %s", imagePath)
- }
- f, err := os.Open(imagePath)
- if err != nil {
- return -1, nil, errors.Wrapf(err, "open file %s", imagePath)
- }
- return fstat.Size(), f, nil
- }
- func (s *LocalStorage) ConvertImage(ctx context.Context, image *SImage, targetFormat string, progresser func(saved int64)) (*SConverImageInfo, error) {
- location := image.GetPath(targetFormat)
- img, err := image.getQemuImage()
- if err != nil {
- return nil, errors.Wrap(err, "unable to image.getQemuImage")
- }
- nimg, err := img.Clone(location, qemuimgfmt.String2ImageFormat(targetFormat), true)
- if err != nil {
- return nil, errors.Wrap(err, "unable to img.Clone")
- }
- return &SConverImageInfo{
- Location: fmt.Sprintf("%s%s", LocalFilePrefix, location),
- SizeBytes: nimg.ActualSizeBytes,
- }, nil
- }
- func (s *LocalStorage) IsCheckStatusEnabled() bool {
- return true
- }
- func (s *LocalStorage) RemoveImage(ctx context.Context, imagePath string) error {
- return os.Remove(imagePath)
- }
- type S3Storage struct{}
- func imagePathToName(imagePath string) string {
- segs := strings.Split(imagePath, "/")
- return segs[len(segs)-1]
- }
- func (s *S3Storage) Type() string {
- return image.IMAGE_STORAGE_DRIVER_S3
- }
- func (s *S3Storage) SaveImage(ctx context.Context, imagePath string, progresser func(saved int64)) (string, error) {
- if !fileutils2.IsFile(imagePath) {
- return "", fmt.Errorf("%s not valid file", imagePath)
- }
- return s3.Put(ctx, imagePath, imagePathToName(imagePath), options.Options.S3UploadPartSizeMb, options.Options.S3UploadParallel, progresser)
- }
- func (s *S3Storage) CleanTempfile(filePath string) error {
- out, err := procutils.NewCommand("rm", "-f", filePath).Output()
- if err != nil {
- return errors.Wrapf(err, "rm %s failed %s", filePath, out)
- }
- return nil
- }
- func (s *S3Storage) getTempDir() (string, error) {
- var dir string
- if options.Options.FilesystemStoreDatadir != "" {
- dir = options.Options.FilesystemStoreDatadir + "/image-tmp"
- } else {
- dir = "/tmp/image-tmp"
- }
- if !fileutils2.Exists(dir) {
- err := procutils.NewCommand("mkdir", "-p", dir).Run()
- if err != nil {
- return "", errors.Wrapf(err, "unable to create dir %s", dir)
- }
- }
- return dir, nil
- }
- type SConverImageInfo struct {
- Location string
- SizeBytes int64
- }
- func (s *S3Storage) ConvertImage(ctx context.Context, image *SImage, targetFormat string, progresser func(saved int64)) (*SConverImageInfo, error) {
- tempDir, err := s.getTempDir()
- if err != nil {
- return nil, err
- }
- location := fmt.Sprintf("%s/%s.%s", tempDir, image.GetId(), targetFormat)
- img, err := image.getQemuImage()
- if err != nil {
- return nil, errors.Wrap(err, "unable to image.getQemuImage")
- }
- nimg, err := img.Clone(location, qemuimgfmt.String2ImageFormat(targetFormat), true)
- if err != nil {
- return nil, errors.Wrap(err, "unable to img.Clone")
- }
- defer s.CleanTempfile(location)
- s3Location, err := s.SaveImage(ctx, location, progresser)
- if err != nil {
- return nil, errors.Wrap(err, "unable to SaveImage")
- }
- return &SConverImageInfo{
- Location: s3Location,
- SizeBytes: nimg.ActualSizeBytes,
- }, nil
- }
- func (s *S3Storage) GetImage(ctx context.Context, imagePath string) (int64, io.ReadCloser, error) {
- return s3.Get(ctx, imagePathToName(imagePath))
- }
- func (s *S3Storage) IsCheckStatusEnabled() bool {
- return options.Options.S3CheckImageStatus
- }
- func (s *S3Storage) RemoveImage(ctx context.Context, fileName string) error {
- return s3.Remove(ctx, fileName)
- }
|