yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/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 hcs
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"math"
    21  	"strings"
    22  	"time"
    23  
    24  	"yunion.io/x/jsonutils"
    25  	"yunion.io/x/log"
    26  	"yunion.io/x/pkg/errors"
    27  
    28  	"yunion.io/x/onecloud/pkg/compute/options"
    29  	"yunion.io/x/onecloud/pkg/mcclient"
    30  	"yunion.io/x/onecloud/pkg/mcclient/auth"
    31  	modules "yunion.io/x/onecloud/pkg/mcclient/modules/image"
    32  	"yunion.io/x/onecloud/pkg/util/qemuimg"
    33  
    34  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    35  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    36  	"yunion.io/x/cloudmux/pkg/multicloud"
    37  	"yunion.io/x/cloudmux/pkg/multicloud/huawei"
    38  )
    39  
    40  type SStoragecache struct {
    41  	multicloud.SResourceBase
    42  	huawei.HuaweiTags
    43  	region *SRegion
    44  }
    45  
    46  func GetBucketName(regionId string, imageId string) string {
    47  	return fmt.Sprintf("imgcache-%s-%s", strings.ToLower(regionId), imageId)
    48  }
    49  
    50  func (self *SStoragecache) GetId() string {
    51  	return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetId())
    52  }
    53  
    54  func (self *SStoragecache) GetName() string {
    55  	return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Name, self.region.GetId())
    56  }
    57  
    58  func (self *SStoragecache) GetGlobalId() string {
    59  	return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetGlobalId())
    60  }
    61  
    62  func (self *SStoragecache) GetStatus() string {
    63  	return "available"
    64  }
    65  
    66  func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) {
    67  	imagesSelf, err := self.region.GetImages("", ImageOwnerSelf, "", EnvFusionCompute)
    68  	if err != nil {
    69  		return nil, errors.Wrapf(err, "GetImages")
    70  	}
    71  
    72  	ret := []cloudprovider.ICloudImage{}
    73  	for i := range imagesSelf {
    74  		imagesSelf[i].cache = self
    75  		ret = append(ret, &imagesSelf[i])
    76  	}
    77  	return ret, nil
    78  }
    79  
    80  func (self *SStoragecache) GetICustomizedCloudImages() ([]cloudprovider.ICloudImage, error) {
    81  	return nil, cloudprovider.ErrNotImplemented
    82  }
    83  
    84  func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) {
    85  	image, err := self.region.GetImage(extId)
    86  	if err != nil {
    87  		return nil, errors.Wrap(err, "self.region.GetImage")
    88  	}
    89  	image.cache = self
    90  	return image, nil
    91  }
    92  
    93  func (self *SStoragecache) GetPath() string {
    94  	return ""
    95  }
    96  
    97  // 目前支持使用vhd、zvhd、vmdk、qcow2、raw、zvhd2、vhdx、qcow、vdi或qed格式镜像文件创建私有镜像。
    98  // 快速通道功能可快速完成镜像制作,但镜像文件需转换为raw或zvhd2格式并完成镜像优化。
    99  // https://support.huaweicloud.com/api-ims/zh-cn_topic_0083905788.html
   100  func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
   101  	if imageId, err := self.region.createIImage(snapshotId, imageName, imageDesc); err != nil {
   102  		return nil, err
   103  	} else if image, err := self.region.GetImage(imageId); err != nil {
   104  		return nil, err
   105  	} else {
   106  		image.cache = self
   107  		iimage := make([]cloudprovider.ICloudImage, 1)
   108  		iimage[0] = image
   109  		if err := cloudprovider.WaitStatus(iimage[0], "avaliable", 15*time.Second, 3600*time.Second); err != nil {
   110  			return nil, err
   111  		}
   112  		return iimage[0], nil
   113  	}
   114  }
   115  
   116  func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) {
   117  	return self.downloadImage(userCred, imageId, extId)
   118  }
   119  
   120  func (self *SStoragecache) downloadImage(userCred mcclient.TokenCredential, imageId string, extId string) (jsonutils.JSONObject, error) {
   121  	return nil, cloudprovider.ErrNotImplemented
   122  }
   123  
   124  func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
   125  	return self.uploadImage(ctx, userCred, image, callback)
   126  }
   127  
   128  func (self *SStoragecache) uploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
   129  	bucketName := GetBucketName(self.region.GetId(), image.ImageId)
   130  
   131  	exist, _ := self.region.IBucketExist(bucketName)
   132  	if !exist {
   133  		err := self.region.CreateIBucket(bucketName, "", "")
   134  		if err != nil {
   135  			return "", errors.Wrap(err, "CreateIBucket")
   136  		}
   137  	}
   138  	defer self.region.DeleteIBucket(bucketName)
   139  
   140  	// upload to huawei cloud
   141  	s := auth.GetAdminSession(ctx, options.Options.Region)
   142  	meta, reader, sizeByte, err := modules.Images.Download(s, image.ImageId, string(qemuimg.VMDK), false)
   143  	if err != nil {
   144  		return "", errors.Wrap(err, "Images.Download")
   145  	}
   146  	log.Debugf("Images meta data %s", meta)
   147  
   148  	minDiskMB, _ := meta.Int("min_disk")
   149  	minDiskGB := int64(math.Ceil(float64(minDiskMB) / 1024))
   150  	// 在使用OBS桶的外部镜像文件制作镜像时生效且为必选字段。取值为40~1024GB。
   151  	if minDiskGB < 40 {
   152  		minDiskGB = 40
   153  	} else if minDiskGB > 1024 {
   154  		minDiskGB = 1024
   155  	}
   156  
   157  	bucket, err := self.region.GetIBucketByName(bucketName)
   158  	if err != nil {
   159  		return "", errors.Wrapf(err, "GetIBucketByName %s", bucketName)
   160  	}
   161  
   162  	body := multicloud.NewProgress(sizeByte, 95, reader, callback)
   163  	err = cloudprovider.UploadObject(context.Background(), bucket, image.ImageId, 0, body, sizeByte, "", "", nil, false)
   164  	if err != nil {
   165  		return "", errors.Wrap(err, "cloudprovider.UploadObject")
   166  	}
   167  
   168  	defer bucket.DeleteObject(context.Background(), image.ImageId)
   169  
   170  	// check image name, avoid name conflict
   171  	imageBaseName := image.ImageId
   172  	if imageBaseName[0] >= '0' && imageBaseName[0] <= '9' {
   173  		imageBaseName = fmt.Sprintf("img%s", image.ImageId)
   174  	}
   175  	imageName := imageBaseName
   176  	nameIdx := 1
   177  
   178  	for {
   179  		_, err = self.region.GetImageByName(imageName)
   180  		if err != nil {
   181  			if errors.Cause(err) == cloudprovider.ErrNotFound {
   182  				break
   183  			} else {
   184  				return "", err
   185  			}
   186  		}
   187  
   188  		imageName = fmt.Sprintf("%s-%d", imageBaseName, nameIdx)
   189  		nameIdx += 1
   190  		log.Debugf("uploadImage Match remote name %s", imageName)
   191  	}
   192  
   193  	err = self.region.ImportImageJob(imageName, image.OsDistribution, image.OsVersion, image.OsArch, bucketName, image.ImageId, int64(minDiskGB))
   194  	if err != nil {
   195  		return "", errors.Wrapf(err, "ImportImageJob")
   196  	}
   197  
   198  	if callback != nil {
   199  		callback(100)
   200  	}
   201  	return "", cloudprovider.ErrNotImplemented
   202  }
   203  
   204  func (self *SRegion) getStoragecache() *SStoragecache {
   205  	return &SStoragecache{region: self}
   206  }
   207  
   208  // https://support.huaweicloud.com/api-ims/zh-cn_topic_0020092109.html
   209  func (self *SRegion) createIImage(snapshotId, imageName, imageDesc string) (string, error) {
   210  	snapshot, err := self.GetSnapshot(snapshotId)
   211  	if err != nil {
   212  		return "", err
   213  	}
   214  
   215  	disk, err := self.GetDisk(snapshot.VolumeId)
   216  	if err != nil {
   217  		return "", err
   218  	}
   219  
   220  	if disk.GetDiskType() != api.DISK_TYPE_SYS {
   221  		return "", fmt.Errorf("disk type err, expected disk type %s", api.DISK_TYPE_SYS)
   222  	}
   223  
   224  	if len(disk.Attachments) == 0 {
   225  		return "", fmt.Errorf("disk is not attached.")
   226  	}
   227  
   228  	params := map[string]interface{}{
   229  		"name":        imageName,
   230  		"description": imageDesc,
   231  		"instance_id": disk.Attachments[0].ServerId,
   232  	}
   233  
   234  	ret := struct {
   235  		ImageId string
   236  	}{}
   237  	err = self.imsPerform("cloudimages", "action", params, &ret)
   238  	if err != nil {
   239  		return "", err
   240  	}
   241  	return ret.ImageId, nil
   242  }
   243  
   244  func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
   245  	storageCache := self.getStoragecache()
   246  	return []cloudprovider.ICloudStoragecache{storageCache}, nil
   247  }
   248  
   249  func (self *SRegion) GetIStoragecacheById(idstr string) (cloudprovider.ICloudStoragecache, error) {
   250  	storageCache := self.getStoragecache()
   251  	if storageCache.GetGlobalId() == idstr {
   252  		return storageCache, nil
   253  	}
   254  	return nil, cloudprovider.ErrNotFound
   255  }