yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/apsara/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 apsara 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 "time" 22 23 "github.com/aliyun/aliyun-oss-go-sdk/oss" 24 25 "yunion.io/x/jsonutils" 26 "yunion.io/x/log" 27 "yunion.io/x/pkg/errors" 28 29 "yunion.io/x/cloudmux/pkg/cloudprovider" 30 "yunion.io/x/onecloud/pkg/compute/options" 31 "yunion.io/x/onecloud/pkg/mcclient" 32 "yunion.io/x/onecloud/pkg/mcclient/auth" 33 modules "yunion.io/x/onecloud/pkg/mcclient/modules/image" 34 "yunion.io/x/cloudmux/pkg/multicloud" 35 "yunion.io/x/onecloud/pkg/util/qemuimg" 36 ) 37 38 type SStoragecache struct { 39 multicloud.SResourceBase 40 ApsaraTags 41 region *SRegion 42 43 iimages []cloudprovider.ICloudImage 44 } 45 46 func (self *SStoragecache) GetId() string { 47 return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetId()) 48 } 49 50 func (self *SStoragecache) GetName() string { 51 return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Name, self.region.GetId()) 52 } 53 54 func (self *SStoragecache) GetStatus() string { 55 return "available" 56 } 57 58 func (self *SStoragecache) Refresh() error { 59 return nil 60 } 61 62 func (self *SStoragecache) GetGlobalId() string { 63 return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetGlobalId()) 64 } 65 66 func (self *SStoragecache) IsEmulated() bool { 67 return false 68 } 69 70 func (self *SStoragecache) getImages(owner ImageOwnerType) ([]SImage, error) { 71 images := make([]SImage, 0) 72 for { 73 parts, total, err := self.region.GetImages(ImageStatusType(""), owner, nil, "", len(images), 50) 74 if err != nil { 75 return nil, err 76 } 77 images = append(images, parts...) 78 if len(images) >= total { 79 break 80 } 81 } 82 return images, nil 83 } 84 85 func (self *SStoragecache) fetchImages() error { 86 images := make([]SImage, 0) 87 for _, owner := range []ImageOwnerType{ImageOwnerSelf, ImageOwnerSystem} { 88 _images, err := self.getImages(owner) 89 if err != nil { 90 return errors.Wrapf(err, "GetImage(%s)", owner) 91 } 92 images = append(images, _images...) 93 } 94 self.iimages = make([]cloudprovider.ICloudImage, len(images)) 95 for i := 0; i < len(images); i += 1 { 96 images[i].storageCache = self 97 self.iimages[i] = &images[i] 98 } 99 return nil 100 } 101 102 func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) { 103 if self.iimages == nil { 104 err := self.fetchImages() 105 if err != nil { 106 return nil, err 107 } 108 } 109 return self.iimages, nil 110 } 111 112 func (self *SStoragecache) GetICustomizedCloudImages() ([]cloudprovider.ICloudImage, error) { 113 return nil, cloudprovider.ErrNotImplemented 114 } 115 116 func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { 117 img, err := self.region.GetImage(extId) 118 if err != nil { 119 return nil, err 120 } 121 img.storageCache = self 122 return img, 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 131 if len(image.ExternalId) > 0 { 132 status, err := self.region.GetImageStatus(image.ExternalId) 133 if err != nil { 134 log.Errorf("GetImageStatus error %s", err) 135 } 136 log.Debugf("UploadImage: Image external ID %s exists, status %s", image.ExternalId, status) 137 // 不能直接删除 ImageStatusCreating 状态的image ,需要先取消importImage Task 138 if status == ImageStatusCreating { 139 err := self.region.CancelImageImportTasks() 140 if err != nil { 141 log.Errorln(err) 142 } 143 } 144 } 145 146 return self.uploadImage(ctx, userCred, image, callback) 147 } 148 149 func (self *SStoragecache) uploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) { 150 // first upload image to oss 151 s := auth.GetAdminSession(ctx, options.Options.Region) 152 153 meta, reader, sizeByte, err := modules.Images.Download(s, image.ImageId, string(qemuimg.QCOW2), false) 154 if err != nil { 155 return "", err 156 } 157 log.Infof("meta data %s", meta) 158 159 bucketName := strings.ToLower(fmt.Sprintf("imgcache-%s-%s", self.region.GetId(), image.ImageId)) 160 exist, err := self.region.IBucketExist(bucketName) 161 if err != nil { 162 log.Errorf("IsBucketExist err %s", err) 163 return "", err 164 } 165 if !exist { 166 log.Debugf("Bucket %s not exists, to create ...", bucketName) 167 err = self.region.CreateIBucket(bucketName, "", "") 168 if err != nil { 169 log.Errorf("Create bucket error %s", err) 170 return "", err 171 } 172 } else { 173 log.Debugf("Bucket %s exists", bucketName) 174 } 175 176 defer self.region.DeleteIBucket(bucketName) // remove bucket 177 178 bucket, err := self.region.GetIBucketByName(bucketName) 179 if err != nil { 180 log.Errorf("Bucket error %s %s", bucketName, err) 181 return "", err 182 } 183 log.Debugf("To upload image to bucket %s ...", bucketName) 184 body := multicloud.NewProgress(sizeByte, 80, reader, callback) 185 err = cloudprovider.UploadObject(context.Background(), bucket, image.ImageId, 0, body, sizeByte, "", "", nil, false) 186 // err = bucket.PutObject(image.ImageId, reader) 187 if err != nil { 188 log.Errorf("PutObject error %s %s", image.ImageId, err) 189 return "", err 190 } 191 192 defer bucket.DeleteObject(context.Background(), image.ImageId) // remove object 193 194 imageBaseName := image.ImageId 195 if imageBaseName[0] >= '0' && imageBaseName[0] <= '9' { 196 imageBaseName = fmt.Sprintf("img%s", image.ImageId) 197 } 198 imageName := imageBaseName 199 nameIdx := 1 200 201 // check image name, avoid name conflict 202 for { 203 _, err = self.region.GetImageByName(imageName) 204 if err != nil { 205 if errors.Cause(err) == cloudprovider.ErrNotFound { 206 break 207 } else { 208 return "", err 209 } 210 } 211 imageName = fmt.Sprintf("%s-%d", imageBaseName, nameIdx) 212 nameIdx += 1 213 } 214 215 log.Debugf("Import image %s", imageName) 216 217 // ensure privileges 218 err = self.region.GetClient().EnableImageImport() 219 if err != nil { 220 log.Errorf("fail to enable import privileges: %s", err) 221 return "", err 222 } 223 224 task, err := self.region.ImportImage(imageName, image.OsArch, image.OsType, image.OsDistribution, bucketName, image.ImageId) 225 226 if err != nil { 227 log.Errorf("ImportImage error %s %s %s", image.ImageId, bucketName, err) 228 return "", err 229 } 230 231 // timeout: 1hour = 3600 seconds 232 err = self.region.waitTaskStatus(ImportImageTask, task.TaskId, TaskStatusFinished, 15*time.Second, 3600*time.Second) 233 if err != nil { 234 log.Errorf("waitTaskStatus %s", err) 235 return task.ImageId, err 236 } 237 238 if callback != nil { 239 callback(100) 240 } 241 242 return task.ImageId, nil 243 } 244 245 func (self *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) { 246 if imageId, err := self.region.createIImage(snapshoutId, imageName, imageDesc); err != nil { 247 return nil, err 248 } else if image, err := self.region.GetImage(imageId); err != nil { 249 return nil, err 250 } else { 251 image.storageCache = self 252 iimage := make([]cloudprovider.ICloudImage, 1) 253 iimage[0] = image 254 if err := cloudprovider.WaitStatus(iimage[0], cloudprovider.IMAGE_STATUS_ACTIVE, 15*time.Second, 3600*time.Second); err != nil { 255 return nil, err 256 } 257 return iimage[0], nil 258 } 259 } 260 261 func (self *SRegion) CreateImage(snapshoutId, imageName, imageDesc string) (string, error) { 262 return self.createIImage(snapshoutId, imageName, imageDesc) 263 } 264 265 func (self *SRegion) createIImage(snapshoutId, imageName, imageDesc string) (string, error) { 266 params := make(map[string]string) 267 params["RegionId"] = self.RegionId 268 params["OssBucket"] = strings.ToLower(fmt.Sprintf("imgcache-%s", self.GetId())) 269 params["SnapshotId"] = snapshoutId 270 params["ImageName"] = imageName 271 params["Description"] = imageDesc 272 273 if body, err := self.ecsRequest("CreateImage", params); err != nil { 274 log.Errorf("CreateImage fail %s", err) 275 return "", err 276 } else { 277 log.Infof("%s", body) 278 return body.GetString("ImageId") 279 } 280 } 281 282 func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) { 283 return self.downloadImage(userCred, imageId, extId, path) 284 } 285 286 // 定义进度条监听器。 287 type OssProgressListener struct { 288 } 289 290 // 定义进度变更事件处理函数。 291 func (listener *OssProgressListener) ProgressChanged(event *oss.ProgressEvent) { 292 switch event.EventType { 293 case oss.TransferStartedEvent: 294 log.Debugf("Transfer Started, ConsumedBytes: %d, TotalBytes %d.\n", 295 event.ConsumedBytes, event.TotalBytes) 296 case oss.TransferDataEvent: 297 log.Debugf("\rTransfer Data, ConsumedBytes: %d, TotalBytes %d, %d%%.", 298 event.ConsumedBytes, event.TotalBytes, event.ConsumedBytes*100/event.TotalBytes) 299 case oss.TransferCompletedEvent: 300 log.Debugf("\nTransfer Completed, ConsumedBytes: %d, TotalBytes %d.\n", 301 event.ConsumedBytes, event.TotalBytes) 302 case oss.TransferFailedEvent: 303 log.Debugf("\nTransfer Failed, ConsumedBytes: %d, TotalBytes %d.\n", 304 event.ConsumedBytes, event.TotalBytes) 305 default: 306 } 307 } 308 309 func (self *SStoragecache) downloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) { 310 return nil, cloudprovider.ErrNotImplemented 311 } 312 313 func (region *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) { 314 storageCache := region.getStoragecache() 315 return []cloudprovider.ICloudStoragecache{storageCache}, nil 316 } 317 318 func (region *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { 319 storageCache := region.getStoragecache() 320 if id == storageCache.GetGlobalId() { 321 return storageCache, nil 322 } 323 return nil, cloudprovider.ErrNotFound 324 }