yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/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 qcloud
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strconv"
    21  	"strings"
    22  	"time"
    23  
    24  	"yunion.io/x/jsonutils"
    25  	"yunion.io/x/log"
    26  	"yunion.io/x/pkg/utils"
    27  
    28  	"yunion.io/x/cloudmux/pkg/apis"
    29  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    30  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    31  	"yunion.io/x/cloudmux/pkg/multicloud"
    32  	"yunion.io/x/onecloud/pkg/util/imagetools"
    33  )
    34  
    35  type ImageStatusType string
    36  
    37  const (
    38  	ImageStatusCreating  ImageStatusType = "CREATING"
    39  	ImageStatusNormal    ImageStatusType = "NORMAL"
    40  	ImageStatusSycing    ImageStatusType = "SYNCING"
    41  	ImageStatusImporting ImageStatusType = "IMPORTING"
    42  	ImageStatusUsing     ImageStatusType = "USING"
    43  	ImageStatusDeleting  ImageStatusType = "DELETING"
    44  )
    45  
    46  type SImage struct {
    47  	multicloud.SImageBase
    48  	QcloudTags
    49  	storageCache *SStoragecache
    50  
    51  	// normalized image info
    52  	imgInfo *imagetools.ImageInfo
    53  
    54  	ImageId            string          //	镜像ID
    55  	OsName             string          //	镜像操作系统
    56  	ImageType          string          //	镜像类型
    57  	CreatedTime        time.Time       //	镜像创建时间
    58  	ImageName          string          //	镜像名称
    59  	ImageDescription   string          //	镜像描述
    60  	ImageSize          int             //	镜像大小
    61  	Architecture       string          //	镜像架构
    62  	ImageState         ImageStatusType //	镜像状态
    63  	Platform           string          //	镜像来源平台
    64  	ImageCreator       string          //	镜像创建者
    65  	ImageSource        string          //	镜像来源
    66  	SyncPercent        int             //	同步百分比
    67  	IsSupportCloudinit bool            //	镜像是否支持cloud-init
    68  }
    69  
    70  func (self *SImage) GetMinRamSizeMb() int {
    71  	return 0
    72  }
    73  
    74  func (self *SRegion) GetImages(status string, owner string, imageIds []string, name string, offset int, limit int) ([]SImage, int, error) {
    75  	if limit > 50 || limit <= 0 {
    76  		limit = 50
    77  	}
    78  	params := make(map[string]string)
    79  	params["Limit"] = fmt.Sprintf("%d", limit)
    80  	params["Offset"] = fmt.Sprintf("%d", offset)
    81  
    82  	for index, imageId := range imageIds {
    83  		params[fmt.Sprintf("ImageIds.%d", index)] = imageId
    84  	}
    85  
    86  	if len(imageIds) == 0 { // imageIds 不能和Filter同时查询
    87  		filter := 0
    88  		if len(status) > 0 {
    89  			params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-state"
    90  			params[fmt.Sprintf("Filters.%d.Values.0", filter)] = status
    91  			filter++
    92  		}
    93  
    94  		if len(owner) > 0 {
    95  			params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-type"
    96  			params[fmt.Sprintf("Filters.%d.Values.0", filter)] = owner
    97  			filter++
    98  		}
    99  
   100  		if len(name) > 0 {
   101  			params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-name"
   102  			params[fmt.Sprintf("Filters.%d.Values.0", filter)] = name
   103  			filter++
   104  		}
   105  	}
   106  
   107  	images := make([]SImage, 0)
   108  	body, err := self.cvmRequest("DescribeImages", params, true)
   109  	if err != nil {
   110  		return nil, 0, err
   111  	}
   112  	err = body.Unmarshal(&images, "ImageSet")
   113  	if err != nil {
   114  		return nil, 0, err
   115  	}
   116  	for i := 0; i < len(images); i++ {
   117  		images[i].storageCache = self.getStoragecache()
   118  	}
   119  	total, _ := body.Float("TotalCount")
   120  	return images, int(total), nil
   121  }
   122  
   123  func (self *SImage) GetId() string {
   124  	return self.ImageId
   125  }
   126  
   127  func (self *SImage) GetName() string {
   128  	return self.ImageName
   129  }
   130  
   131  func (self *SImage) IsEmulated() bool {
   132  	return false
   133  }
   134  
   135  func (self *SImage) GetGlobalId() string {
   136  	return self.ImageId
   137  }
   138  
   139  func (self *SImage) Delete(ctx context.Context) error {
   140  	return self.storageCache.region.DeleteImage(self.ImageId)
   141  }
   142  
   143  func (self *SImage) GetStatus() string {
   144  	switch self.ImageState {
   145  	case ImageStatusCreating, ImageStatusSycing, ImageStatusImporting:
   146  		return api.CACHED_IMAGE_STATUS_CACHING
   147  	case ImageStatusNormal, ImageStatusUsing:
   148  		return api.CACHED_IMAGE_STATUS_ACTIVE
   149  	default:
   150  		return api.CACHED_IMAGE_STATUS_CACHE_FAILED
   151  	}
   152  }
   153  
   154  func (self *SImage) GetImageStatus() string {
   155  	switch self.ImageState {
   156  	case ImageStatusCreating, ImageStatusSycing, ImageStatusImporting:
   157  		return cloudprovider.IMAGE_STATUS_SAVING
   158  	case ImageStatusNormal, ImageStatusUsing:
   159  		return cloudprovider.IMAGE_STATUS_ACTIVE
   160  	case ImageStatusDeleting:
   161  		return cloudprovider.IMAGE_STATUS_DELETED
   162  	default:
   163  		return cloudprovider.IMAGE_STATUS_DELETED
   164  	}
   165  }
   166  
   167  func (self *SImage) Refresh() error {
   168  	new, err := self.storageCache.region.GetImage(self.ImageId)
   169  	if err != nil {
   170  		return err
   171  	}
   172  	return jsonutils.Update(self, new)
   173  }
   174  
   175  func (self *SImage) GetImageType() cloudprovider.TImageType {
   176  	switch self.ImageType {
   177  	case "PUBLIC_IMAGE":
   178  		return cloudprovider.ImageTypeSystem
   179  	case "PRIVATE_IMAGE":
   180  		return cloudprovider.ImageTypeCustomized
   181  	default:
   182  		return cloudprovider.ImageTypeCustomized
   183  	}
   184  }
   185  
   186  func (self *SImage) GetSizeByte() int64 {
   187  	return int64(self.ImageSize) * 1024 * 1024 * 1024
   188  }
   189  
   190  func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
   191  	if self.imgInfo == nil {
   192  		imgInfo := imagetools.NormalizeImageInfo(self.OsName, self.Architecture, self.Platform, self.Platform, "")
   193  		self.imgInfo = &imgInfo
   194  	}
   195  
   196  	return self.imgInfo
   197  }
   198  
   199  func (self *SImage) GetOsType() cloudprovider.TOsType {
   200  	return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType)
   201  }
   202  
   203  func (self *SImage) GetOsDist() string {
   204  	return self.getNormalizedImageInfo().OsDistro
   205  }
   206  
   207  func (self *SImage) GetOsVersion() string {
   208  	return self.getNormalizedImageInfo().OsVersion
   209  }
   210  
   211  func (self *SImage) GetOsArch() string {
   212  	return self.getNormalizedImageInfo().OsArch
   213  }
   214  
   215  func (img *SImage) GetBios() cloudprovider.TBiosType {
   216  	return cloudprovider.ToBiosType(img.getNormalizedImageInfo().OsBios)
   217  }
   218  
   219  func (img *SImage) GetFullOsName() string {
   220  	return img.OsName
   221  }
   222  
   223  func (img *SImage) GetOsLang() string {
   224  	return img.getNormalizedImageInfo().OsLang
   225  }
   226  
   227  func (self *SImage) GetMinOsDiskSizeGb() int {
   228  	return 50
   229  }
   230  
   231  func (self *SImage) GetImageFormat() string {
   232  	return "qcow2"
   233  }
   234  
   235  func (self *SImage) GetCreatedAt() time.Time {
   236  	return self.CreatedTime
   237  }
   238  
   239  func (self *SRegion) GetImage(imageId string) (*SImage, error) {
   240  	images, _, err := self.GetImages("", "", []string{imageId}, "", 0, 1)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	if len(images) == 0 {
   245  		return nil, fmt.Errorf("image %s not found", imageId)
   246  	}
   247  	return &images[0], nil
   248  }
   249  
   250  func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
   251  	return self.storageCache
   252  }
   253  
   254  func (self *SRegion) DeleteImage(imageId string) error {
   255  	params := make(map[string]string)
   256  	params["ImageIds.0"] = imageId
   257  
   258  	_, err := self.cvmRequest("DeleteImages", params, true)
   259  	return err
   260  }
   261  
   262  func (self *SRegion) GetImageStatus(imageId string) (ImageStatusType, error) {
   263  	image, err := self.GetImage(imageId)
   264  	if err != nil {
   265  		return "", err
   266  	}
   267  	return image.ImageState, nil
   268  }
   269  
   270  func (self *SRegion) GetImageByName(name string) (*SImage, error) {
   271  	images, _, err := self.GetImages("", "", nil, name, 0, 1)
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  	if len(images) == 0 {
   276  		return nil, cloudprovider.ErrNotFound
   277  	}
   278  	return &images[0], nil
   279  }
   280  
   281  type ImportImageOsListSupported struct {
   282  	Linux   []string //"CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux"
   283  	Windows []string //"Windows Server 2008|Windows Server 2012|Windows Server 2016"
   284  }
   285  
   286  type ImportImageOsVersionSet struct {
   287  	Architecture []string
   288  	OsName       string //"CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux|Windows Server 2008|Windows Server 2012|Windows Server 2016"
   289  	OsVersions   []string
   290  }
   291  
   292  type SupportImageSet struct {
   293  	ImportImageOsListSupported ImportImageOsListSupported
   294  	ImportImageOsVersionSet    []ImportImageOsVersionSet
   295  }
   296  
   297  func (self *SRegion) GetSupportImageSet() (*SupportImageSet, error) {
   298  	body, err := self.cvmRequest("DescribeImportImageOs", map[string]string{}, true)
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  	imageSet := SupportImageSet{}
   303  	return &imageSet, body.Unmarshal(&imageSet)
   304  }
   305  
   306  func (self *SRegion) GetImportImageParams(name string, osArch, osDist, osVersion string, imageUrl string) (map[string]string, error) {
   307  	params := map[string]string{}
   308  	imageSet, err := self.GetSupportImageSet()
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  	osType := ""
   313  	for _, _imageSet := range imageSet.ImportImageOsVersionSet {
   314  		if strings.ToLower(osDist) == strings.ToLower(_imageSet.OsName) { //Linux一般可正常匹配
   315  			osType = _imageSet.OsName
   316  		} else if strings.Contains(strings.ToLower(_imageSet.OsName), "windows") && strings.Contains(strings.ToLower(osDist), "windows") {
   317  			info := strings.Split(_imageSet.OsName, " ")
   318  			_osVersion := "2008"
   319  			for _, version := range info {
   320  				if _, err := strconv.Atoi(version); err == nil {
   321  					_osVersion = version
   322  					break
   323  				}
   324  			}
   325  			if strings.Contains(osDist+osVersion, _osVersion) {
   326  				osType = _imageSet.OsName
   327  			}
   328  		}
   329  		if len(osType) == 0 {
   330  			continue
   331  		}
   332  		if !utils.IsInStringArray(osArch, _imageSet.Architecture) {
   333  			osArch = apis.OS_ARCH_X86_64
   334  		}
   335  		for _, _osVersion := range _imageSet.OsVersions {
   336  			if strings.HasPrefix(osVersion, _osVersion) {
   337  				osVersion = _osVersion
   338  				break
   339  			}
   340  		}
   341  		if !utils.IsInStringArray(osVersion, _imageSet.OsVersions) {
   342  			osVersion = "-"
   343  			if len(_imageSet.OsVersions) > 0 {
   344  				osVersion = _imageSet.OsVersions[0]
   345  			}
   346  		}
   347  		break
   348  	}
   349  	if len(osType) == 0 {
   350  		osType = "Other Linux"
   351  		osArch = apis.OS_ARCH_X86_64
   352  		osVersion = "-"
   353  	}
   354  
   355  	params["ImageName"] = name
   356  	params["OsType"] = osType
   357  	params["OsVersion"] = osVersion
   358  	params["Architecture"] = osArch // "x86_64|i386"
   359  	params["ImageUrl"] = imageUrl
   360  	params["Force"] = "true"
   361  	return params, nil
   362  }
   363  
   364  func (self *SRegion) ImportImage(name string, osArch, osDist, osVersion string, imageUrl string) (*SImage, error) {
   365  	params, err := self.GetImportImageParams(name, osArch, osDist, osVersion, imageUrl)
   366  	if err != nil {
   367  		return nil, err
   368  	}
   369  
   370  	log.Debugf("Upload image with params %#v", params)
   371  
   372  	if _, err := self.cvmRequest("ImportImage", params, true); err != nil {
   373  		return nil, err
   374  	}
   375  	for i := 0; i < 8; i++ {
   376  		image, err := self.GetImageByName(name)
   377  		if err == nil {
   378  			return image, nil
   379  		}
   380  		time.Sleep(time.Minute * time.Duration(i))
   381  	}
   382  	return nil, cloudprovider.ErrNotFound
   383  }