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 }