yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/zstack/image.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 zstack 16 17 import ( 18 "context" 19 "fmt" 20 "io" 21 "net/http" 22 "net/url" 23 "sort" 24 "time" 25 26 "yunion.io/x/jsonutils" 27 "yunion.io/x/log" 28 "yunion.io/x/pkg/errors" 29 30 api "yunion.io/x/cloudmux/pkg/apis/compute" 31 "yunion.io/x/cloudmux/pkg/cloudprovider" 32 "yunion.io/x/cloudmux/pkg/multicloud" 33 "yunion.io/x/onecloud/pkg/util/httputils" 34 "yunion.io/x/onecloud/pkg/util/imagetools" 35 "yunion.io/x/onecloud/pkg/util/multipart" 36 ) 37 38 type SBackupStorageRef struct { 39 BackupStorageUUID string `json:"backupStorageUuid"` 40 CreateDate time.Time `json:"createDate"` 41 ImageUUID string `json:"ImageUuid"` 42 InstallPath string `json:"installPath"` 43 LastOpDate time.Time `json:"lastOpDate"` 44 Status string `json:"status"` 45 } 46 47 type SImage struct { 48 multicloud.SImageBase 49 ZStackTags 50 storageCache *SStoragecache 51 52 // normalized image info 53 imgInfo *imagetools.ImageInfo 54 55 BackupStorageRefs []SBackupStorageRef `json:"backupStorageRefs"` 56 ActualSize int `json:"actualSize"` 57 CreateDate time.Time `json:"createDate"` 58 Description string `json:"description"` 59 Format string `json:"format"` 60 LastOpDate time.Time `json:"lastOpDate"` 61 MD5Sum string `json:"md5sum"` 62 MediaType string `json:"mediaType"` 63 Name string `json:"name"` 64 Platform string `json:"platform"` 65 Size int `json:"size"` 66 State string `json:"state"` 67 Status string `json:"Ready"` 68 System bool `json:"system"` 69 Type string `json:"type"` 70 URL string `json:"url"` 71 UUID string `json:"uuid"` 72 } 73 74 func (image *SImage) GetMinRamSizeMb() int { 75 return 0 76 } 77 78 func (image *SImage) GetId() string { 79 return image.UUID 80 } 81 82 func (image *SImage) GetName() string { 83 return image.Name 84 } 85 86 func (image *SImage) IsEmulated() bool { 87 return false 88 } 89 90 func (image *SImage) Delete(ctx context.Context) error { 91 return image.storageCache.region.DeleteImage(image.UUID) 92 } 93 94 func (image *SImage) GetGlobalId() string { 95 return image.UUID 96 } 97 98 func (region *SRegion) DeleteImage(imageId string) error { 99 err := region.client.delete("images", imageId, "") 100 if err != nil { 101 return err 102 } 103 params := map[string]interface{}{ 104 "expungeImage": jsonutils.NewDict(), 105 } 106 _, err = region.client.put("images", imageId, jsonutils.Marshal(params)) 107 return err 108 } 109 110 func (image *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache { 111 return image.storageCache 112 } 113 114 func (image *SImage) GetStatus() string { 115 if image.State != "Enabled" { 116 return api.CACHED_IMAGE_STATUS_CACHING 117 } 118 switch image.Status { 119 case "Ready": 120 return api.CACHED_IMAGE_STATUS_ACTIVE 121 case "Downloading": 122 return api.CACHED_IMAGE_STATUS_CACHING 123 case "Deleted": 124 return api.CACHED_IMAGE_STATUS_DELETING 125 default: 126 log.Errorf("Unknown image status: %s", image.Status) 127 return api.CACHED_IMAGE_STATUS_CACHE_FAILED 128 } 129 } 130 131 func (image *SImage) GetImageStatus() string { 132 switch image.Status { 133 case "Ready": 134 return cloudprovider.IMAGE_STATUS_ACTIVE 135 case "Deleted": 136 return cloudprovider.IMAGE_STATUS_DELETED 137 default: 138 return cloudprovider.IMAGE_STATUS_KILLED 139 } 140 } 141 142 func (image *SImage) Refresh() error { 143 new, err := image.storageCache.region.GetImage(image.UUID) 144 if err != nil { 145 return err 146 } 147 return jsonutils.Update(image, new) 148 } 149 150 func (image *SImage) GetImageType() cloudprovider.TImageType { 151 return cloudprovider.ImageTypeSystem 152 } 153 154 func (image *SImage) GetSizeByte() int64 { 155 return int64(image.Size) 156 } 157 158 func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo { 159 if self.imgInfo == nil { 160 imgInfo := imagetools.NormalizeImageInfo(self.URL, "", self.Platform, self.Platform, "") 161 self.imgInfo = &imgInfo 162 } 163 164 return self.imgInfo 165 } 166 167 func (image *SImage) GetOsType() cloudprovider.TOsType { 168 return cloudprovider.TOsType(image.getNormalizedImageInfo().OsType) 169 } 170 171 func (image *SImage) GetOsDist() string { 172 return image.getNormalizedImageInfo().OsDistro 173 } 174 175 func (image *SImage) GetOsVersion() string { 176 return image.getNormalizedImageInfo().OsVersion 177 } 178 179 func (image *SImage) GetOsArch() string { 180 return image.getNormalizedImageInfo().OsArch 181 } 182 183 func (image *SImage) GetBios() cloudprovider.TBiosType { 184 return cloudprovider.TBiosType(image.getNormalizedImageInfo().OsBios) 185 } 186 187 func (image *SImage) GetOsLang() string { 188 return image.getNormalizedImageInfo().OsLang 189 } 190 191 func (image *SImage) GetFullOsName() string { 192 return image.URL 193 } 194 195 func (image *SImage) GetMinOsDiskSizeGb() int { 196 return image.Size / 1024 / 1024 / 1024 197 } 198 199 func (image *SImage) GetImageFormat() string { 200 return image.Format 201 } 202 203 func (image *SImage) GetCreatedAt() time.Time { 204 return image.CreateDate 205 } 206 207 func (region *SRegion) GetImage(imageId string) (*SImage, error) { 208 image := &SImage{} 209 err := region.client.getResource("images", imageId, image) 210 if err != nil { 211 return nil, errors.Wrapf(err, "region.GetImage") 212 } 213 return image, nil 214 } 215 216 func (region *SRegion) GetImages(zoneId string, imageId string) ([]SImage, error) { 217 images := []SImage{} 218 params := url.Values{} 219 params.Add("q", "system=false") 220 if len(zoneId) > 0 { 221 params.Add("q", "backupStorage.zone.uuid="+zoneId) 222 } 223 if len(imageId) > 0 { 224 params.Add("q", "uuid="+imageId) 225 } 226 if SkipEsxi { 227 params.Add("q", "type!=vmware") 228 } 229 return images, region.client.listAll("images", params, &images) 230 } 231 232 func (region *SRegion) GetBackupStorageUUID(zondId string) ([]string, error) { 233 imageServers, err := region.GetImageServers(zondId, "") 234 if err != nil { 235 return nil, err 236 } 237 if len(imageServers) == 0 { 238 return nil, fmt.Errorf("failed to found any image servers") 239 } 240 servers := ImageServers(imageServers) 241 sort.Sort(servers) 242 return []string{servers[0].UUID}, nil 243 } 244 245 func (region *SRegion) CreateImage(zoneId string, imageName, format, osType, desc string, reader io.Reader, size int64, callback func(progress float32)) (*SImage, error) { 246 backupStorageUUIDs, err := region.GetBackupStorageUUID(zoneId) 247 if err != nil { 248 return nil, err 249 } 250 platform := "" 251 switch osType { 252 case "linux": 253 platform = "Linux" 254 case "windows": 255 platform = "Windows" 256 default: 257 platform = "Other" 258 } 259 parmas := map[string]interface{}{ 260 "params": map[string]interface{}{ 261 "name": imageName, 262 "url": fmt.Sprintf("upload://%s", imageName), 263 "description": desc, 264 "mediaType": "RootVolumeTemplate", 265 "system": false, 266 "format": format, 267 "platform": platform, 268 "backupStorageUuids": backupStorageUUIDs, 269 "systemTags": []string{"qemuga", "bootMode::Legacy"}, 270 }, 271 } 272 273 if reader == nil { 274 return nil, fmt.Errorf("invalid reader") 275 } 276 277 if size == 0 { 278 return nil, fmt.Errorf("invalid image size") 279 } 280 281 body := multipart.NewReader(reader, "", imageName) 282 283 image := &SImage{} 284 err = region.client.create("images", jsonutils.Marshal(parmas), image) 285 if err != nil { 286 return nil, err 287 } 288 289 if len(image.BackupStorageRefs) < 0 { 290 return nil, fmt.Errorf("no InstallPath reture") 291 } 292 header := http.Header{} 293 header.Add("X-IMAGE-UUID", image.UUID) 294 header.Add("X-IMAGE-SIZE", fmt.Sprintf("%d", size)) 295 header.Add("Content-Type", body.FormDataContentType()) 296 r := multicloud.NewProgress(size, 99, body, callback) 297 resp, err := httputils.Request(httputils.GetTimeoutClient(0), context.Background(), "POST", image.BackupStorageRefs[0].InstallPath, header, r, false) 298 if err != nil { 299 return nil, err 300 } 301 defer resp.Body.Close() 302 return image, nil 303 }