yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/openstack/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 openstack
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io"
    21  	"net/url"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  
    27  	"yunion.io/x/jsonutils"
    28  
    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  	"yunion.io/x/onecloud/pkg/util/qemuimg"
    34  	"yunion.io/x/onecloud/pkg/util/rbacutils"
    35  )
    36  
    37  const (
    38  	QUEUED         = "queued"         //	The Image service reserved an image ID for the image in the catalog but did not yet upload any image data.
    39  	SAVING         = "saving"         //	The Image service is in the process of saving the raw data for the image into the backing store.
    40  	ACTIVE         = "active"         //	The image is active and ready for consumption in the Image service.
    41  	KILLED         = "killed"         // An image data upload error occurred.
    42  	DELETED        = "deleted"        //	The Image service retains information about the image but the image is no longer available for use.
    43  	PENDING_DELETE = "pending_delete" // Similar to the deleted status. An image in this state is not recoverable.
    44  	DEACTIVATED    = "deactivated"    //	The image data is not available for use.
    45  	UPLOADING      = "uploading"      // Data has been staged as part of the interoperable image import process. It is not yet available for use. (Since Image API 2.6)
    46  	IMPORTING      = "importing"      //	The image data is being processed as part of the interoperable image import process, but is not yet available for use. (Since Image API 2.6)
    47  )
    48  
    49  type SImage struct {
    50  	multicloud.SImageBase
    51  	storageCache *SStoragecache
    52  	OpenStackTags
    53  
    54  	// normalized image info
    55  	imgInfo *imagetools.ImageInfo
    56  
    57  	Status          string
    58  	Name            string
    59  	Tags            []string
    60  	ContainerFormat string
    61  	CreatedAt       time.Time
    62  	DiskFormat      string
    63  	UpdatedAt       time.Time
    64  	Visibility      string
    65  	Self            string
    66  	MinDisk         int
    67  	Protected       bool
    68  	Id              string
    69  	File            string
    70  	Checksum        string
    71  	OsHashAlgo      string
    72  	OsHashValue     string
    73  	OsHidden        bool
    74  	OsDistro        string
    75  	OsType          string
    76  	Owner           string
    77  	Size            int
    78  	MinRAM          int
    79  	Schema          string
    80  	VirtualSize     int
    81  	visibility      string
    82  }
    83  
    84  func (image *SImage) GetMinRamSizeMb() int {
    85  	return image.MinRAM
    86  }
    87  
    88  func (region *SRegion) GetImages(name string, status string, imageId string) ([]SImage, error) {
    89  	query := url.Values{}
    90  	if len(status) > 0 {
    91  		query.Set("status", status)
    92  	}
    93  	if len(name) > 0 {
    94  		query.Set("name", name)
    95  	}
    96  	if len(imageId) > 0 {
    97  		query.Set("id", imageId)
    98  	}
    99  	images := []SImage{}
   100  	resource := "/v2/images"
   101  	marker := ""
   102  	for {
   103  		if len(marker) > 0 {
   104  			query.Set("marker", marker)
   105  		}
   106  		resp, err := region.imageList(resource, query)
   107  		if err != nil {
   108  			return nil, errors.Wrap(err, "imageList")
   109  		}
   110  		part := struct {
   111  			Images []SImage
   112  			Next   string
   113  		}{}
   114  		err = resp.Unmarshal(&part)
   115  		if err != nil {
   116  			return nil, errors.Wrap(err, "resp.Unmarshal")
   117  		}
   118  		images = append(images, part.Images...)
   119  		if len(part.Next) == 0 {
   120  			break
   121  		}
   122  		if len(part.Next) > 0 {
   123  			href, err := url.Parse(part.Next)
   124  			if err != nil {
   125  				marker = ""
   126  			} else {
   127  				marker = href.Query().Get("marker")
   128  			}
   129  		}
   130  		if len(marker) == 0 {
   131  			break
   132  		}
   133  	}
   134  	return images, nil
   135  }
   136  
   137  func (image *SImage) GetId() string {
   138  	return image.Id
   139  }
   140  
   141  func (image *SImage) GetName() string {
   142  	return image.Name
   143  }
   144  
   145  func (image *SImage) IsEmulated() bool {
   146  	return false
   147  }
   148  
   149  func (image *SImage) GetGlobalId() string {
   150  	return image.Id
   151  }
   152  
   153  func (image *SImage) Delete(ctx context.Context) error {
   154  	return image.storageCache.region.DeleteImage(image.Id)
   155  }
   156  
   157  func (image *SImage) GetStatus() string {
   158  	switch image.Status {
   159  	case QUEUED, SAVING, UPLOADING, IMPORTING:
   160  		return api.CACHED_IMAGE_STATUS_CACHING
   161  	case ACTIVE:
   162  		return api.CACHED_IMAGE_STATUS_ACTIVE
   163  	case DELETED, DEACTIVATED, PENDING_DELETE, KILLED:
   164  		return api.CACHED_IMAGE_STATUS_CACHE_FAILED
   165  	default:
   166  		return api.CACHED_IMAGE_STATUS_CACHE_FAILED
   167  	}
   168  }
   169  
   170  func (image *SImage) GetImageStatus() string {
   171  	switch image.Status {
   172  	case QUEUED, SAVING, UPLOADING, IMPORTING:
   173  		return cloudprovider.IMAGE_STATUS_SAVING
   174  	case ACTIVE:
   175  		return cloudprovider.IMAGE_STATUS_ACTIVE
   176  	case DELETED, DEACTIVATED, PENDING_DELETE:
   177  		return cloudprovider.IMAGE_STATUS_DELETED
   178  	case KILLED:
   179  		return cloudprovider.IMAGE_STATUS_KILLED
   180  	default:
   181  		return cloudprovider.IMAGE_STATUS_DELETED
   182  	}
   183  }
   184  
   185  func (image *SImage) Refresh() error {
   186  	_image, err := image.storageCache.region.GetImage(image.Id)
   187  	if err != nil {
   188  		return errors.Wrap(err, "GetImage")
   189  	}
   190  	return jsonutils.Update(image, _image)
   191  }
   192  
   193  func (image *SImage) GetImageType() cloudprovider.TImageType {
   194  	return cloudprovider.ImageTypeSystem
   195  }
   196  
   197  func (image *SImage) GetPublicScope() rbacutils.TRbacScope {
   198  	switch image.Visibility {
   199  	case "private":
   200  		return rbacutils.ScopeNone
   201  	default:
   202  		return rbacutils.ScopeSystem
   203  	}
   204  }
   205  
   206  func (image *SImage) GetProjectId() string {
   207  	return image.Owner
   208  }
   209  
   210  func (image *SImage) GetSizeByte() int64 {
   211  	return int64(image.Size)
   212  }
   213  
   214  func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
   215  	if self.imgInfo == nil {
   216  		imgInfo := imagetools.NormalizeImageInfo(self.Name, "", self.OsType, "", "")
   217  		self.imgInfo = &imgInfo
   218  	}
   219  
   220  	return self.imgInfo
   221  }
   222  
   223  func (image *SImage) GetFullOsName() string {
   224  	return image.Name
   225  }
   226  
   227  func (image *SImage) GetOsType() cloudprovider.TOsType {
   228  	return cloudprovider.TOsType(image.getNormalizedImageInfo().OsType)
   229  }
   230  
   231  func (image *SImage) GetOsDist() string {
   232  	return image.getNormalizedImageInfo().OsDistro
   233  }
   234  
   235  func (image *SImage) GetOsVersion() string {
   236  	return image.getNormalizedImageInfo().OsVersion
   237  }
   238  
   239  func (image *SImage) GetOsArch() string {
   240  	return image.getNormalizedImageInfo().OsArch
   241  }
   242  
   243  func (image *SImage) GetOsLang() string {
   244  	return image.getNormalizedImageInfo().OsLang
   245  }
   246  
   247  func (image *SImage) GetBios() cloudprovider.TBiosType {
   248  	return cloudprovider.ToBiosType(image.getNormalizedImageInfo().OsBios)
   249  }
   250  
   251  func (image *SImage) GetMinOsDiskSizeGb() int {
   252  	if image.MinDisk > 0 {
   253  		return image.MinDisk
   254  	}
   255  	return 30
   256  }
   257  
   258  func (image *SImage) GetImageFormat() string {
   259  	return image.DiskFormat
   260  }
   261  
   262  func (image *SImage) GetCreatedAt() time.Time {
   263  	return image.CreatedAt
   264  }
   265  
   266  func (region *SRegion) GetImage(imageId string) (*SImage, error) {
   267  	resource := "/v2/images/" + imageId
   268  	resp, err := region.imageGet(resource)
   269  	if err != nil {
   270  		return nil, errors.Wrapf(err, "imageGet(%s)", resource)
   271  	}
   272  	image := &SImage{}
   273  	err = resp.Unmarshal(image)
   274  	if err != nil {
   275  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   276  	}
   277  	return image, nil
   278  }
   279  
   280  func (image *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
   281  	return image.storageCache
   282  }
   283  
   284  func (region *SRegion) DeleteImage(imageId string) error {
   285  	_, err := region.imageDelete("/v2/images/" + imageId)
   286  	return err
   287  }
   288  
   289  func (region *SRegion) GetImageStatus(imageId string) (string, error) {
   290  	image, err := region.GetImage(imageId)
   291  	if err != nil {
   292  		return "", err
   293  	}
   294  	return image.Status, nil
   295  }
   296  
   297  func (region *SRegion) GetImageByName(name string) (*SImage, error) {
   298  	images, err := region.GetImages(name, "", "")
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  	if len(images) == 0 {
   303  		return nil, cloudprovider.ErrNotFound
   304  	}
   305  	return &images[0], nil
   306  }
   307  
   308  func (region *SRegion) CreateImage(imageName string, osType string, osDist string, minDiskGb int, minRam int, size int64, body io.Reader, callback func(progress float32)) (*SImage, error) {
   309  	params := map[string]interface{}{
   310  		"container_format":    "bare",
   311  		"disk_format":         string(qemuimg.QCOW2),
   312  		"name":                imageName,
   313  		"min_disk":            minDiskGb,
   314  		"min_ram":             minRam,
   315  		"os_type":             strings.ToLower(osType),
   316  		"os_distro":           osDist,
   317  		"hw_qemu_guest_agent": "yes",
   318  	}
   319  
   320  	resp, err := region.imagePost("/v2/images", params)
   321  	if err != nil {
   322  		return nil, errors.Wrap(err, "imagePost")
   323  	}
   324  	image := &SImage{}
   325  	err = resp.Unmarshal(image)
   326  	if err != nil {
   327  		return nil, errors.Wrap(err, "resp.Unmarshal")
   328  	}
   329  	url := fmt.Sprintf("/v2/images/%s/file", image.Id)
   330  	err = region.imageUpload(url, size, body, callback)
   331  	if err != nil {
   332  		return nil, errors.Wrap(err, "imageUpload")
   333  	}
   334  	return image, nil
   335  }