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 }