yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/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 hcs 16 17 import ( 18 "context" 19 "fmt" 20 "net/url" 21 "strings" 22 "time" 23 24 "yunion.io/x/jsonutils" 25 "yunion.io/x/log" 26 27 "yunion.io/x/cloudmux/pkg/apis" 28 api "yunion.io/x/cloudmux/pkg/apis/compute" 29 "yunion.io/x/cloudmux/pkg/cloudprovider" 30 "yunion.io/x/cloudmux/pkg/multicloud" 31 "yunion.io/x/cloudmux/pkg/multicloud/huawei" 32 "yunion.io/x/onecloud/pkg/util/imagetools" 33 ) 34 35 type TImageOwnerType string 36 37 const ( 38 ImageOwnerPublic TImageOwnerType = "gold" // 公共镜像:gold 39 ImageOwnerSelf TImageOwnerType = "private" // 私有镜像:private 40 ImageOwnerShared TImageOwnerType = "shared" // 共享镜像:shared 41 42 EnvFusionCompute = "FusionCompute" 43 EnvIronic = "Ironic" 44 ) 45 46 const ( 47 ImageStatusQueued = "queued" // queued:表示镜像元数据已经创建成功,等待上传镜像文件。 48 ImageStatusSaving = "saving" // saving:表示镜像正在上传文件到后端存储。 49 ImageStatusDeleted = "deleted" // deleted:表示镜像已经删除。 50 ImageStatusKilled = "killed" // killed:表示镜像上传错误。 51 ImageStatusActive = "active" // active:表示镜像可以正常使用 52 ) 53 54 // https://support.huaweicloud.com/api-ims/zh-cn_topic_0020091565.html 55 type SImage struct { 56 multicloud.SImageBase 57 huawei.HuaweiTags 58 cache *SStoragecache 59 60 // normalized image info 61 imgInfo *imagetools.ImageInfo 62 63 Schema string `json:"schema"` 64 MinDiskGB int64 `json:"min_disk"` 65 CreatedAt time.Time `json:"created_at"` 66 ImageSourceType string `json:"__image_source_type"` 67 ContainerFormat string `json:"container_format"` 68 File string `json:"file"` 69 UpdatedAt time.Time `json:"updated_at"` 70 Protected bool `json:"protected"` 71 Checksum string `json:"checksum"` 72 Id string `json:"id"` 73 Isregistered string `json:"__isregistered"` 74 MinRamMB int `json:"min_ram"` 75 Lazyloading string `json:"__lazyloading"` 76 Owner string `json:"owner"` 77 OSType string `json:"__os_type"` 78 Imagetype string `json:"__imagetype"` 79 Visibility string `json:"visibility"` 80 VirtualEnvType string `json:"virtual_env_type"` 81 Platform string `json:"__platform"` 82 SizeGB int `json:"size"` 83 ImageSize int64 `json:"__image_size"` 84 OSBit string `json:"__os_bit"` 85 OSVersion string `json:"__os_version"` 86 Architecture string 87 Name string `json:"name"` 88 Self string `json:"self"` 89 DiskFormat string `json:"disk_format"` 90 Status string `json:"status"` 91 SupportKVMFPGAType string `json:"__support_kvm_fpga_type"` 92 SupportKVMNVMEHIGHIO string `json:"__support_nvme_highio"` 93 SupportLargeMemory string `json:"__support_largememory"` 94 SupportDiskIntensive string `json:"__support_diskintensive"` 95 SupportHighPerformance string `json:"__support_highperformance"` 96 SupportXENGPUType string `json:"__support_xen_gpu_type"` 97 SupportKVMGPUType string `json:"__support_kvm_gpu_type"` 98 SupportGPUT4 string `json:"__support_gpu_t4"` 99 SupportKVMAscend310 string `json:"__support_kvm_ascend_310"` 100 SupportArm string `json:"__support_arm"` 101 } 102 103 func (self *SImage) GetMinRamSizeMb() int { 104 return self.MinRamMB 105 } 106 107 func (self *SImage) GetId() string { 108 return self.Id 109 } 110 111 func (self *SImage) GetName() string { 112 return self.Name 113 } 114 115 func (self *SImage) GetGlobalId() string { 116 return self.Id 117 } 118 119 func (self *SImage) GetStatus() string { 120 switch self.Status { 121 case ImageStatusQueued: 122 return api.CACHED_IMAGE_STATUS_CACHING 123 case ImageStatusActive: 124 return api.CACHED_IMAGE_STATUS_ACTIVE 125 case ImageStatusKilled: 126 return api.CACHED_IMAGE_STATUS_CACHE_FAILED 127 default: 128 return api.CACHED_IMAGE_STATUS_CACHE_FAILED 129 } 130 } 131 132 func (self *SImage) GetImageStatus() string { 133 switch self.Status { 134 case ImageStatusQueued: 135 return cloudprovider.IMAGE_STATUS_QUEUED 136 case ImageStatusActive: 137 return cloudprovider.IMAGE_STATUS_ACTIVE 138 case ImageStatusKilled: 139 return cloudprovider.IMAGE_STATUS_KILLED 140 default: 141 return cloudprovider.IMAGE_STATUS_KILLED 142 } 143 } 144 145 func (self *SImage) Refresh() error { 146 ret, err := self.cache.region.GetImage(self.GetId()) 147 if err != nil { 148 return err 149 } 150 return jsonutils.Update(self, ret) 151 } 152 153 func (self *SImage) GetImageType() cloudprovider.TImageType { 154 switch self.Imagetype { 155 case "gold": 156 return cloudprovider.ImageTypeSystem 157 case "private": 158 return cloudprovider.ImageTypeCustomized 159 case "shared": 160 return cloudprovider.ImageTypeShared 161 default: 162 return cloudprovider.ImageTypeCustomized 163 } 164 } 165 166 func (self *SImage) GetSizeByte() int64 { 167 return int64(self.MinDiskGB) * 1024 * 1024 * 1024 168 } 169 170 func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo { 171 if self.imgInfo == nil { 172 imgInfo := imagetools.NormalizeImageInfo(self.ImageSourceType, self.Architecture, self.OSType, self.Platform, "") 173 self.imgInfo = &imgInfo 174 } 175 176 return self.imgInfo 177 } 178 179 func (self *SImage) GetFullOsName() string { 180 return self.ImageSourceType 181 } 182 183 func (self *SImage) GetOsType() cloudprovider.TOsType { 184 return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType) 185 } 186 187 func (self *SImage) GetOsDist() string { 188 return self.getNormalizedImageInfo().OsDistro 189 } 190 191 func (self *SImage) GetOsVersion() string { 192 return self.getNormalizedImageInfo().OsVersion 193 } 194 195 func (self *SImage) GetOsLang() string { 196 return self.getNormalizedImageInfo().OsLang 197 } 198 199 func (self *SImage) GetOsArch() string { 200 return self.getNormalizedImageInfo().OsArch 201 } 202 203 func (self *SImage) GetBios() cloudprovider.TBiosType { 204 return cloudprovider.ToBiosType(self.getNormalizedImageInfo().OsBios) 205 } 206 207 func (self *SImage) GetMinOsDiskSizeGb() int { 208 return int(self.MinDiskGB) 209 } 210 211 func (self *SImage) GetImageFormat() string { 212 return self.DiskFormat 213 } 214 215 func (self *SImage) GetCreatedAt() time.Time { 216 return self.CreatedAt 217 } 218 219 func (self *SImage) IsEmulated() bool { 220 return false 221 } 222 223 func (self *SImage) Delete(ctx context.Context) error { 224 return self.cache.region.DeleteImage(self.GetId()) 225 } 226 227 func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache { 228 return self.cache 229 } 230 231 func (self *SRegion) GetImage(imageId string) (*SImage, error) { 232 image := &SImage{cache: self.getStoragecache()} 233 res := fmt.Sprintf("images/%s", imageId) 234 return image, self.imsGet(res, image) 235 } 236 237 func excludeImage(image SImage) bool { 238 if image.VirtualEnvType == "Ironic" { 239 return true 240 } 241 242 if len(image.SupportDiskIntensive) > 0 { 243 return true 244 } 245 246 if len(image.SupportKVMFPGAType) > 0 || len(image.SupportKVMAscend310) > 0 { 247 return true 248 } 249 250 if len(image.SupportKVMGPUType) > 0 { 251 return true 252 } 253 254 if len(image.SupportKVMNVMEHIGHIO) > 0 { 255 return true 256 } 257 258 if len(image.SupportGPUT4) > 0 { 259 return true 260 } 261 262 if len(image.SupportXENGPUType) > 0 { 263 return true 264 } 265 266 if len(image.SupportHighPerformance) > 0 { 267 return true 268 } 269 270 if len(image.SupportArm) > 0 { 271 return true 272 } 273 274 return false 275 } 276 277 // https://support.huaweicloud.com/api-ims/zh-cn_topic_0060804959.html 278 func (self *SRegion) GetImages(status string, imagetype TImageOwnerType, name string, envType string) ([]SImage, error) { 279 params := url.Values{} 280 if len(status) > 0 { 281 params.Set("status", status) 282 } 283 284 if len(imagetype) > 0 { 285 params.Set("__imagetype", string(imagetype)) 286 if imagetype == ImageOwnerPublic { 287 params.Set("protected", "True") 288 } 289 } 290 if len(envType) > 0 { 291 params.Set("virtual_env_type", envType) 292 } 293 294 if len(name) > 0 { 295 params.Set("name", name) 296 } 297 298 images := []SImage{} 299 err := self.imsList("images", params, &images) 300 if err != nil { 301 return nil, err 302 } 303 304 // 排除掉需要特定镜像才能创建的实例类型 305 // https://support.huaweicloud.com/eu-west-0-api-ims/zh-cn_topic_0031617666.html#ZH-CN_TOPIC_0031617666__table48545918250 306 // https://support.huaweicloud.com/productdesc-ecs/zh-cn_topic_0088142947.html 307 filtedImages := make([]SImage, 0) 308 for i := range images { 309 if !excludeImage(images[i]) { 310 filtedImages = append(filtedImages, images[i]) 311 } 312 } 313 return filtedImages, err 314 } 315 316 func (self *SRegion) DeleteImage(imageId string) error { 317 res := fmt.Sprintf("images/%s", imageId) 318 return self.imsDelete(res) 319 } 320 321 func (self *SRegion) GetImageByName(name string) (*SImage, error) { 322 if len(name) == 0 { 323 return nil, fmt.Errorf("image name should not be empty") 324 } 325 images, err := self.GetImages("", TImageOwnerType(""), name, "") 326 if err != nil { 327 return nil, err 328 } 329 if len(images) == 0 { 330 return nil, cloudprovider.ErrNotFound 331 } 332 log.Debugf("%d image found match name %s", len(images), name) 333 return &images[0], nil 334 } 335 336 /* 337 https://support.huaweicloud.com/api-ims/zh-cn_topic_0020092109.html 338 339 os version 取值范围: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html 340 用于创建私有镜像的源云服务器系统盘大小大于等于40GB且不超过1024GB。 341 目前支持vhd,zvhd、raw,qcow2 342 todo: 考虑使用镜像快速导入。 https://support.huaweicloud.com/api-ims/zh-cn_topic_0133188204.html 343 使用OBS文件创建镜像 344 345 * openstack原生接口支持的格式:https://support.huaweicloud.com/api-ims/zh-cn_topic_0031615566.html 346 */ 347 func (self *SRegion) ImportImageJob(name string, osDist string, osVersion string, osArch string, bucket string, key string, minDiskGB int64) error { 348 os_version, err := stdVersion(osDist, osVersion, osArch) 349 log.Debugf("%s %s %s: %s.min_disk %d GB", osDist, osVersion, osArch, os_version, minDiskGB) 350 if err != nil { 351 log.Debugln(err) 352 } 353 354 image_url := fmt.Sprintf("%s:%s", bucket, key) 355 params := map[string]interface{}{ 356 "name": name, 357 "image_url": image_url, 358 "os_version": os_version, 359 "is_config_init": true, 360 "is_config": true, 361 "min_disk": minDiskGB, 362 } 363 return self.imsPerform("cloudimage", "action", params, nil) 364 } 365 366 func formatVersion(osDist string, osVersion string) (string, error) { 367 err := fmt.Errorf("unsupport version %s.reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", osVersion) 368 dist := strings.ToLower(osDist) 369 if dist == "ubuntu" || dist == "redhat" || dist == "centos" || dist == "oracle" || dist == "euleros" { 370 parts := strings.Split(osVersion, ".") 371 if len(parts) < 2 { 372 return "", err 373 } 374 375 return parts[0] + "." + parts[1], nil 376 } 377 378 if dist == "debian" { 379 parts := strings.Split(osVersion, ".") 380 if len(parts) < 3 { 381 return "", err 382 } 383 384 return parts[0] + "." + parts[1] + "." + parts[2], nil 385 } 386 387 if dist == "fedora" || dist == "windows" || dist == "suse" { 388 parts := strings.Split(osVersion, ".") 389 if len(parts) < 1 { 390 return "", err 391 } 392 393 return parts[0], nil 394 } 395 396 if dist == "opensuse" { 397 parts := strings.Split(osVersion, ".") 398 if len(parts) == 0 { 399 return "", err 400 } 401 402 if len(parts) == 1 { 403 return parts[0], nil 404 } 405 406 if len(parts) >= 2 { 407 return parts[0] + "." + parts[1], nil 408 } 409 } 410 411 return "", err 412 } 413 414 // todo: 如何保持同步更新 415 // https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html 416 func stdVersion(osDist string, osVersion string, osArch string) (string, error) { 417 // 架构 418 arch := "" 419 switch osArch { 420 case "64", apis.OS_ARCH_X86_64: 421 arch = "64bit" 422 case "32", apis.OS_ARCH_X86_32: 423 arch = "32bit" 424 default: 425 return "", fmt.Errorf("unsupported arch %s.reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", osArch) 426 } 427 428 _dist := strings.Split(strings.TrimSpace(osDist), " ")[0] 429 _dist = strings.ToLower(_dist) 430 // 版本 431 ver, err := formatVersion(_dist, osVersion) 432 if err != nil { 433 return "", err 434 } 435 436 // 操作系统 437 dist := "" 438 439 switch _dist { 440 case "ubuntu": 441 return fmt.Sprintf("Ubuntu %s server %s", ver, arch), nil 442 case "redhat": 443 dist = "Redhat Linux Enterprise" 444 case "centos": 445 dist = "CentOS" 446 case "fedora": 447 dist = "Fedora" 448 case "debian": 449 dist = "Debian GNU/Linux" 450 case "windows": 451 dist = "Windows Server" 452 case "oracle": 453 dist = "Oracle Linux Server release" 454 case "suse": 455 dist = "SUSE Linux Enterprise Server" 456 case "opensuse": 457 dist = "OpenSUSE" 458 case "euleros": 459 dist = "EulerOS" 460 default: 461 return "", fmt.Errorf("unsupported os %s. reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", dist) 462 } 463 464 return fmt.Sprintf("%s %s %s", dist, ver, arch), nil 465 } 466 467 func (self *SImage) UEFI() bool { 468 return false 469 }