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  }