yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/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 aliyun
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/aliyun/aliyun-oss-go-sdk/oss"
    24  
    25  	"yunion.io/x/jsonutils"
    26  	"yunion.io/x/log"
    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  	ImageStatusAvailable    ImageStatusType = "Available"
    40  	ImageStatusUnAvailable  ImageStatusType = "UnAvailable"
    41  	ImageStatusCreateFailed ImageStatusType = "CreateFailed"
    42  )
    43  
    44  type ImageOwnerType string
    45  
    46  const (
    47  	ImageOwnerSystem      ImageOwnerType = "system"
    48  	ImageOwnerSelf        ImageOwnerType = "self"
    49  	ImageOwnerOthers      ImageOwnerType = "others"
    50  	ImageOwnerMarketplace ImageOwnerType = "marketplace"
    51  )
    52  
    53  type ImageUsageType string
    54  
    55  const (
    56  	ImageUsageInstance ImageUsageType = "instance"
    57  	ImageUsageNone     ImageUsageType = "none"
    58  )
    59  
    60  type SImage struct {
    61  	multicloud.SImageBase
    62  	AliyunTags
    63  	storageCache *SStoragecache
    64  
    65  	// normalized image info
    66  	imgInfo *imagetools.ImageInfo
    67  
    68  	Architecture         string
    69  	CreationTime         time.Time
    70  	Description          string
    71  	ImageId              string
    72  	ImageName            string
    73  	OSName               string
    74  	OSType               string
    75  	ImageOwnerAlias      ImageOwnerType
    76  	IsSupportCloudinit   bool
    77  	IsSupportIoOptimized bool
    78  	Platform             string
    79  	Size                 int
    80  	Status               ImageStatusType
    81  	Usage                string
    82  }
    83  
    84  func (self *SImage) GetMinRamSizeMb() int {
    85  	return 0
    86  }
    87  
    88  func (self *SImage) GetId() string {
    89  	return self.ImageId
    90  }
    91  
    92  func (self *SImage) GetName() string {
    93  	if self.ImageOwnerAlias == ImageOwnerSystem {
    94  		return self.OSName
    95  	} else {
    96  		return self.ImageName
    97  	}
    98  }
    99  
   100  func (self *SImage) IsEmulated() bool {
   101  	return false
   102  }
   103  
   104  func (self *SImage) Delete(ctx context.Context) error {
   105  	return self.storageCache.region.DeleteImage(self.ImageId)
   106  }
   107  
   108  func (self *SImage) GetGlobalId() string {
   109  	return self.ImageId
   110  }
   111  
   112  func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
   113  	return self.storageCache
   114  }
   115  
   116  func (self *SImage) GetStatus() string {
   117  	switch self.Status {
   118  	case ImageStatusCreating:
   119  		return api.CACHED_IMAGE_STATUS_SAVING
   120  	case ImageStatusAvailable:
   121  		return api.CACHED_IMAGE_STATUS_ACTIVE
   122  	case ImageStatusUnAvailable:
   123  		return api.CACHED_IMAGE_STATUS_CACHE_FAILED
   124  	case ImageStatusCreateFailed:
   125  		return api.CACHED_IMAGE_STATUS_CACHE_FAILED
   126  	default:
   127  		return api.CACHED_IMAGE_STATUS_CACHE_FAILED
   128  	}
   129  }
   130  
   131  func (self *SImage) GetImageStatus() string {
   132  	switch self.Status {
   133  	case ImageStatusCreating:
   134  		return cloudprovider.IMAGE_STATUS_QUEUED
   135  	case ImageStatusAvailable:
   136  		return cloudprovider.IMAGE_STATUS_ACTIVE
   137  	case ImageStatusUnAvailable:
   138  		return cloudprovider.IMAGE_STATUS_DELETED
   139  	case ImageStatusCreateFailed:
   140  		return cloudprovider.IMAGE_STATUS_KILLED
   141  	default:
   142  		return cloudprovider.IMAGE_STATUS_KILLED
   143  	}
   144  }
   145  
   146  func (self *SImage) Refresh() error {
   147  	new, err := self.storageCache.region.GetImage(self.ImageId)
   148  	if err != nil {
   149  		return err
   150  	}
   151  	return jsonutils.Update(self, new)
   152  }
   153  
   154  func (self *SImage) GetImageType() cloudprovider.TImageType {
   155  	switch self.ImageOwnerAlias {
   156  	case ImageOwnerSystem:
   157  		return cloudprovider.ImageTypeSystem
   158  	case ImageOwnerSelf:
   159  		return cloudprovider.ImageTypeCustomized
   160  	case ImageOwnerMarketplace:
   161  		return cloudprovider.ImageTypeMarket
   162  	case ImageOwnerOthers:
   163  		return cloudprovider.ImageTypeShared
   164  	default:
   165  		return cloudprovider.ImageTypeCustomized
   166  	}
   167  }
   168  
   169  func (self *SImage) GetSizeByte() int64 {
   170  	return int64(self.Size) * 1024 * 1024 * 1024
   171  }
   172  
   173  func (self *SImage) GetOsType() cloudprovider.TOsType {
   174  	return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType)
   175  }
   176  
   177  func (self *SImage) GetOsDist() string {
   178  	return self.getNormalizedImageInfo().OsDistro
   179  }
   180  
   181  func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
   182  	if self.imgInfo == nil {
   183  		imgInfo := imagetools.NormalizeImageInfo(self.OSName, self.Architecture, self.OSType, self.Platform, "")
   184  		self.imgInfo = &imgInfo
   185  	}
   186  
   187  	return self.imgInfo
   188  }
   189  
   190  func (self *SImage) GetFullOsName() string {
   191  	return self.OSName
   192  }
   193  
   194  func (self *SImage) GetOsVersion() string {
   195  	return self.getNormalizedImageInfo().OsVersion
   196  }
   197  
   198  func (self *SImage) GetOsLang() string {
   199  	return self.getNormalizedImageInfo().OsLang
   200  }
   201  
   202  func (self *SImage) GetOsArch() string {
   203  	return self.getNormalizedImageInfo().OsArch
   204  }
   205  
   206  func (self *SImage) GetBios() cloudprovider.TBiosType {
   207  	return cloudprovider.ToBiosType(self.getNormalizedImageInfo().OsBios)
   208  }
   209  
   210  func (self *SImage) GetMinOsDiskSizeGb() int {
   211  	return 40
   212  }
   213  
   214  func (self *SImage) GetImageFormat() string {
   215  	return "vhd"
   216  }
   217  
   218  func (self *SImage) GetCreatedAt() time.Time {
   219  	return self.CreationTime
   220  }
   221  
   222  type ImageExportTask struct {
   223  	ImageId  string
   224  	RegionId string
   225  	// RequestId string
   226  	TaskId string
   227  }
   228  
   229  func (self *SRegion) ExportImage(imageId string, bucket *oss.Bucket) (*ImageExportTask, error) {
   230  	params := make(map[string]string)
   231  	params["RegionId"] = self.RegionId
   232  	params["ImageId"] = imageId
   233  	params["OssBucket"] = bucket.BucketName
   234  	params["OssPrefix"] = fmt.Sprintf("%sexport", strings.Replace(imageId, "-", "", -1))
   235  
   236  	if body, err := self.ecsRequest("ExportImage", params); err != nil {
   237  		return nil, err
   238  	} else {
   239  		result := ImageExportTask{}
   240  		if err := body.Unmarshal(&result); err != nil {
   241  			log.Errorf("unmarshal result error %s", err)
   242  			return nil, err
   243  		}
   244  		return &result, nil
   245  	}
   246  }
   247  
   248  // {"ImageId":"m-j6c1qlpa7oebbg1n2k60","RegionId":"cn-hongkong","RequestId":"F8B2F6A1-F6AA-4C92-A54C-C4A309CF811F","TaskId":"t-j6c1qlpa7oebbg1rcl9t"}
   249  
   250  type ImageImportTask struct {
   251  	ImageId  string
   252  	RegionId string
   253  	// RequestId string
   254  	TaskId string
   255  }
   256  
   257  func (self *SRegion) ImportImage(name string, osArch string, osType string, osDist string, bucket string, key string) (*ImageImportTask, error) {
   258  	params := make(map[string]string)
   259  	params["RegionId"] = self.RegionId
   260  	params["ImageName"] = name
   261  	if osDist == "RHEL" {
   262  		osDist = "CentOS"
   263  	}
   264  	params["Platform"] = osDist // "Others Linux"
   265  	params["OSType"] = osType   // "linux"
   266  	switch osArch {
   267  	case apis.OS_ARCH_I386, apis.OS_ARCH_X86_32:
   268  		params["Architecture"] = "i386"
   269  	case apis.OS_ARCH_X86, apis.OS_ARCH_X86_64:
   270  		params["Architecture"] = "x86_64"
   271  	case apis.OS_ARCH_ARM, apis.OS_ARCH_AARCH32, apis.OS_ARCH_AARCH64:
   272  		params["Architecture"] = "arm64"
   273  	default:
   274  		params["Architecture"] = osArch // "x86_64"
   275  	}
   276  	params["DiskDeviceMapping.1.OSSBucket"] = bucket
   277  	params["DiskDeviceMapping.1.OSSObject"] = key
   278  
   279  	log.Debugf("Upload image with params %#v", params)
   280  
   281  	body, err := self.ecsRequest("ImportImage", params)
   282  	if err != nil {
   283  		log.Errorf("ImportImage fail %s", err)
   284  		return nil, err
   285  	}
   286  
   287  	log.Infof("%s", body)
   288  	result := ImageImportTask{}
   289  	err = body.Unmarshal(&result)
   290  	if err != nil {
   291  		log.Errorf("unmarshal result error %s", err)
   292  		return nil, err
   293  	}
   294  
   295  	return &result, nil
   296  }
   297  
   298  func (self *SRegion) GetImage(imageId string) (*SImage, error) {
   299  	images, _, err := self.GetImages("", "", []string{imageId}, "", 0, 1)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  	if len(images) == 0 {
   304  		return nil, cloudprovider.ErrNotFound
   305  	}
   306  	return &images[0], nil
   307  }
   308  
   309  func (self *SRegion) GetImageByName(name string) (*SImage, error) {
   310  	images, _, err := self.GetImages("", "", nil, name, 0, 1)
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	if len(images) == 0 {
   315  		return nil, cloudprovider.ErrNotFound
   316  	}
   317  	return &images[0], nil
   318  }
   319  
   320  func (self *SRegion) GetImagesBySnapshot(snapshotId string, offset int, limit int) ([]SImage, int, error) {
   321  	if limit > 50 || limit <= 0 {
   322  		limit = 50
   323  	}
   324  	params := make(map[string]string)
   325  	params["RegionId"] = self.RegionId
   326  	params["PageSize"] = fmt.Sprintf("%d", limit)
   327  	params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1)
   328  	params["SnapshotId"] = snapshotId
   329  
   330  	return self.getImages(params)
   331  }
   332  
   333  func (self *SRegion) GetImageStatus(imageId string) (ImageStatusType, error) {
   334  	image, err := self.GetImage(imageId)
   335  	if err != nil {
   336  		return "", err
   337  	}
   338  	return image.Status, nil
   339  }
   340  
   341  func (self *SRegion) GetImages(status ImageStatusType, owner ImageOwnerType, imageId []string, name string, offset int, limit int) ([]SImage, int, error) {
   342  	if limit > 50 || limit <= 0 {
   343  		limit = 50
   344  	}
   345  	params := make(map[string]string)
   346  	params["RegionId"] = self.RegionId
   347  	params["PageSize"] = fmt.Sprintf("%d", limit)
   348  	params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1)
   349  
   350  	if len(status) > 0 {
   351  		params["Status"] = string(status)
   352  	} else {
   353  		params["Status"] = "Creating,Available,UnAvailable,CreateFailed"
   354  	}
   355  	if imageId != nil && len(imageId) > 0 {
   356  		params["ImageId"] = strings.Join(imageId, ",")
   357  	}
   358  	if len(owner) > 0 {
   359  		params["ImageOwnerAlias"] = string(owner)
   360  	}
   361  
   362  	if len(name) > 0 {
   363  		params["ImageName"] = name
   364  	}
   365  
   366  	return self.getImages(params)
   367  }
   368  
   369  func (self *SRegion) getImages(params map[string]string) ([]SImage, int, error) {
   370  	body, err := self.ecsRequest("DescribeImages", params)
   371  	if err != nil {
   372  		log.Errorf("DescribeImages fail %s", err)
   373  		return nil, 0, err
   374  	}
   375  
   376  	images := make([]SImage, 0)
   377  	err = body.Unmarshal(&images, "Images", "Image")
   378  	if err != nil {
   379  		log.Errorf("unmarshal images fail %s", err)
   380  		return nil, 0, nil
   381  	}
   382  	total, _ := body.Int("TotalCount")
   383  	return images, int(total), nil
   384  }
   385  
   386  func (self *SRegion) DeleteImage(imageId string) error {
   387  	params := make(map[string]string)
   388  	params["RegionId"] = self.RegionId
   389  	params["ImageId"] = imageId
   390  	params["Force"] = "true"
   391  
   392  	_, err := self.ecsRequest("DeleteImage", params)
   393  	if err != nil {
   394  		log.Errorf("DeleteImage fail %s", err)
   395  		return err
   396  	}
   397  	return nil
   398  }