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  }