yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/storagecache.go (about)

     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  
    15  package qcloud
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strings"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    25  	"yunion.io/x/pkg/errors"
    26  
    27  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    28  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    29  	"yunion.io/x/onecloud/pkg/compute/options"
    30  	"yunion.io/x/onecloud/pkg/mcclient"
    31  	"yunion.io/x/onecloud/pkg/mcclient/auth"
    32  	modules "yunion.io/x/onecloud/pkg/mcclient/modules/image"
    33  	"yunion.io/x/cloudmux/pkg/multicloud"
    34  	"yunion.io/x/onecloud/pkg/util/qemuimg"
    35  )
    36  
    37  type SStoragecache struct {
    38  	multicloud.SResourceBase
    39  	QcloudTags
    40  	region *SRegion
    41  }
    42  
    43  func (self *SStoragecache) GetId() string {
    44  	return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetId())
    45  }
    46  
    47  func (self *SStoragecache) GetName() string {
    48  	return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Name, self.region.GetId())
    49  }
    50  
    51  func (self *SStoragecache) GetStatus() string {
    52  	return "available"
    53  }
    54  
    55  func (self *SStoragecache) Refresh() error {
    56  	return nil
    57  }
    58  
    59  func (self *SStoragecache) GetGlobalId() string {
    60  	return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetGlobalId())
    61  }
    62  
    63  func (self *SStoragecache) IsEmulated() bool {
    64  	return false
    65  }
    66  
    67  func (self *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
    68  	// if imageId, err := self.region.createIImage(snapshoutId, imageName, imageDesc); err != nil {
    69  	// 	return nil, err
    70  	// } else if image, err := self.region.GetImage(imageId); err != nil {
    71  	// 	return nil, err
    72  	// } else {
    73  	// 	image.storageCache = self
    74  	// 	iimage := make([]cloudprovider.ICloudImage, 1)
    75  	// 	iimage[0] = image
    76  	// 	if err := cloudprovider.WaitStatus(iimage[0], compute.IMAGE_STATUS_ACTIVE, 15*time.Second, 3600*time.Second); err != nil {
    77  	// 		return nil, err
    78  	// 	}
    79  	// 	return iimage[0], nil
    80  	// }
    81  	return nil, nil
    82  }
    83  
    84  func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) {
    85  	//return self.downloadImage(userCred, imageId, extId, path)
    86  	return nil, nil
    87  }
    88  
    89  func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) {
    90  	return nil, cloudprovider.ErrNotImplemented
    91  }
    92  
    93  func (self *SStoragecache) GetICustomizedCloudImages() ([]cloudprovider.ICloudImage, error) {
    94  	images := make([]SImage, 0)
    95  	for {
    96  		parts, total, err := self.region.GetImages("", "PRIVATE_IMAGE", nil, "", len(images), 50)
    97  		if err != nil {
    98  			return nil, errors.Wrapf(err, "GetImages")
    99  		}
   100  		images = append(images, parts...)
   101  		if len(images) >= total {
   102  			break
   103  		}
   104  	}
   105  	ret := []cloudprovider.ICloudImage{}
   106  	for i := 0; i < len(images); i += 1 {
   107  		images[i].storageCache = self
   108  		ret = append(ret, &images[i])
   109  	}
   110  	return ret, nil
   111  }
   112  
   113  func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) {
   114  	parts, _, err := self.region.GetImages("", "", []string{extId}, "", 0, 1)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	if len(parts) == 0 {
   119  		return nil, cloudprovider.ErrNotFound
   120  	}
   121  	parts[0].storageCache = self
   122  	return &parts[0], nil
   123  }
   124  
   125  func (self *SStoragecache) GetPath() string {
   126  	return ""
   127  }
   128  
   129  func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
   130  	return self.uploadImage(ctx, userCred, image, callback)
   131  }
   132  
   133  func (self *SRegion) getCosUrl(bucket, object string) string {
   134  	//signature := cosauth.NewSignature(self.client.AppID, bucket, self.client.secretId, time.Now().Add(time.Minute*30).String(), time.Now().String(), "yunion", object).SignOnce(self.client.secretKey)
   135  	return fmt.Sprintf("http://%s-%s.cos.%s.myqcloud.com/%s", bucket, self.client.appId, self.Region, object)
   136  }
   137  
   138  func (self *SStoragecache) uploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
   139  	// first upload image to oss
   140  	s := auth.GetAdminSession(ctx, options.Options.Region)
   141  
   142  	_, reader, sizeBytes, err := modules.Images.Download(s, image.ImageId, string(qemuimg.VMDK), false)
   143  	if err != nil {
   144  		return "", err
   145  	}
   146  
   147  	bucketName := strings.Replace(strings.ToLower(self.region.GetId()+image.ImageId), "-", "", -1)
   148  	if len(bucketName) > 40 {
   149  		bucketName = bucketName[:40]
   150  	}
   151  	exists, _ := self.region.IBucketExist(bucketName)
   152  	if !exists {
   153  		log.Debugf("Bucket %s not exists, to create ...", bucketName)
   154  		err := self.region.CreateIBucket(bucketName, "", "public-read")
   155  		if err != nil {
   156  			log.Errorf("Create bucket error %s", err)
   157  			return "", err
   158  		}
   159  	} else {
   160  		log.Debugf("Bucket %s exists", bucketName)
   161  	}
   162  	defer self.region.DeleteIBucket(bucketName)
   163  	log.Debugf("To upload image to bucket %s ...", bucketName)
   164  	bucket, err := self.region.GetIBucketById(bucketName)
   165  	if err != nil {
   166  		return "", errors.Wrap(err, "GetIBucketByName")
   167  	}
   168  	body := multicloud.NewProgress(sizeBytes, 80, reader, callback)
   169  	err = cloudprovider.UploadObject(context.Background(), bucket, image.ImageId, 0, body, sizeBytes, "", "", nil, false)
   170  	// err = bucket.PutObject(context.Background(), image.ImageId, reader, sizeBytes, "", "", "")
   171  	if err != nil {
   172  		log.Errorf("UploadObject error %s %s", image.ImageId, err)
   173  		return "", errors.Wrap(err, "bucket.PutObject")
   174  	}
   175  
   176  	defer bucket.DeleteObject(context.Background(), image.ImageId)
   177  
   178  	// 腾讯云镜像名称需要小于20个字符
   179  	imageBaseName := image.ImageId[:10]
   180  	if imageBaseName[0] >= '0' && imageBaseName[0] <= '9' {
   181  		imageBaseName = fmt.Sprintf("img%s", image.ImageId[:10])
   182  	}
   183  	imageName := imageBaseName
   184  	nameIdx := 1
   185  
   186  	// check image name, avoid name conflict
   187  	for {
   188  		_, err = self.region.GetImageByName(imageName)
   189  		if err != nil {
   190  			if errors.Cause(err) == cloudprovider.ErrNotFound {
   191  				break
   192  			} else {
   193  				return "", err
   194  			}
   195  		}
   196  		imageName = fmt.Sprintf("%s-%d", imageBaseName, nameIdx)
   197  		nameIdx++
   198  	}
   199  
   200  	log.Debugf("Import image %s", imageName)
   201  	img, err := self.region.ImportImage(imageName, image.OsArch, image.OsDistribution, image.OsVersion, self.region.getCosUrl(bucketName, image.ImageId))
   202  	if err != nil {
   203  		return "", err
   204  	}
   205  	err = cloudprovider.WaitStatus(img, api.CACHED_IMAGE_STATUS_ACTIVE, 15*time.Second, 3600*time.Second)
   206  	if err != nil {
   207  		return "", err
   208  	}
   209  	if callback != nil {
   210  		callback(100)
   211  	}
   212  	return img.ImageId, nil
   213  }