yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/nutanix/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 nutanix 16 17 import ( 18 "context" 19 "fmt" 20 "io" 21 "net/http" 22 "net/url" 23 "time" 24 25 "yunion.io/x/jsonutils" 26 "yunion.io/x/log" 27 "yunion.io/x/pkg/errors" 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 ) 34 35 type SImage struct { 36 multicloud.STagBase 37 multicloud.SImageBase 38 39 cache *SStoragecache 40 41 imageInfo *imagetools.ImageInfo 42 43 UUID string `json:"uuid"` 44 Name string `json:"name"` 45 Deleted bool `json:"deleted"` 46 StorageContainerID int `json:"storage_container_id"` 47 StorageContainerUUID string `json:"storage_container_uuid"` 48 LogicalTimestamp int `json:"logical_timestamp"` 49 ImageType string `json:"image_type"` 50 VMDiskID string `json:"vm_disk_id"` 51 ImageState string `json:"image_state"` 52 CreatedTimeInUsecs int64 `json:"created_time_in_usecs"` 53 UpdatedTimeInUsecs int64 `json:"updated_time_in_usecs"` 54 VMDiskSize int64 `json:"vm_disk_size"` 55 } 56 57 func (self *SImage) GetName() string { 58 return self.Name 59 } 60 61 func (self *SImage) GetId() string { 62 return self.UUID 63 } 64 65 func (self *SImage) GetGlobalId() string { 66 return self.UUID 67 } 68 69 func (self *SImage) Refresh() error { 70 image, err := self.cache.region.GetImage(self.GetGlobalId()) 71 if err != nil { 72 return err 73 } 74 return jsonutils.Update(self, image) 75 } 76 77 func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache { 78 return self.cache 79 } 80 81 func (self *SImage) GetImageFormat() string { 82 if self.ImageType == "ISO_IMAGE" { 83 return "iso" 84 } 85 return "raw" 86 } 87 88 func (self *SImage) GetStatus() string { 89 switch self.ImageState { 90 case "ACTIVE": 91 return api.CACHED_IMAGE_STATUS_ACTIVE 92 case "INACTIVE": 93 return api.CACHED_IMAGE_STATUS_SAVING 94 } 95 return self.ImageState 96 } 97 98 func (self *SImage) GetImageStatus() string { 99 switch self.ImageState { 100 case "ACTIVE": 101 return cloudprovider.IMAGE_STATUS_ACTIVE 102 case "INACTIVE": 103 return cloudprovider.IMAGE_STATUS_QUEUED 104 } 105 return cloudprovider.IMAGE_STATUS_KILLED 106 } 107 108 func (self *SImage) GetImageType() cloudprovider.TImageType { 109 return cloudprovider.ImageTypeSystem 110 } 111 112 func (self *SImage) GetCreatedAt() time.Time { 113 return time.Unix(self.CreatedTimeInUsecs/1000, self.CreatedTimeInUsecs%1000) 114 } 115 116 func (self *SImage) Delete(ctx context.Context) error { 117 return cloudprovider.ErrNotImplemented 118 } 119 120 func (self *SImage) GetMinOsDiskSizeGb() int { 121 return int(self.VMDiskSize / 1024 / 1024 / 1024) 122 } 123 124 func (self *SImage) GetSizeByte() int64 { 125 return self.VMDiskSize 126 } 127 128 func (img *SImage) getNormalizedImageInfo() *imagetools.ImageInfo { 129 if img.imageInfo == nil { 130 imgInfo := imagetools.NormalizeImageInfo(img.Name, "", "", "", "") 131 img.imageInfo = &imgInfo 132 } 133 return img.imageInfo 134 } 135 136 func (img *SImage) GetFullOsName() string { 137 return img.Name 138 } 139 140 func (ins *SImage) GetOsType() cloudprovider.TOsType { 141 return cloudprovider.TOsType(ins.getNormalizedImageInfo().OsType) 142 } 143 144 func (ins *SImage) GetOsDist() string { 145 return ins.getNormalizedImageInfo().OsDistro 146 } 147 148 func (ins *SImage) GetOsVersion() string { 149 return ins.getNormalizedImageInfo().OsVersion 150 } 151 152 func (ins *SImage) GetOsLang() string { 153 return ins.getNormalizedImageInfo().OsLang 154 } 155 156 func (ins *SImage) GetBios() cloudprovider.TBiosType { 157 return cloudprovider.ToBiosType(ins.getNormalizedImageInfo().OsBios) 158 } 159 160 func (ins *SImage) GetOsArch() string { 161 return ins.getNormalizedImageInfo().OsArch 162 } 163 164 func (self *SImage) GetMinRamSizeMb() int { 165 return 0 166 } 167 168 func (self *SRegion) GetImages() ([]SImage, error) { 169 images := []SImage{} 170 params := url.Values{} 171 params.Set("include_vm_disk_sizes", "true") 172 params.Set("include_vm_disk_paths", "true") 173 return images, self.listAll("images", nil, &images) 174 } 175 176 func (self *SRegion) GetImage(id string) (*SImage, error) { 177 image := &SImage{} 178 params := url.Values{} 179 params.Set("include_vm_disk_sizes", "true") 180 params.Set("include_vm_disk_paths", "true") 181 return image, self.get("images", id, params, image) 182 } 183 184 func (self *SRegion) CreateImage(storageId string, opts *cloudprovider.SImageCreateOption, sizeBytes int64, body io.Reader, callback func(float32)) (*SImage, error) { 185 params := map[string]interface{}{ 186 "image_type": "DISK_IMAGE", 187 "name": opts.ImageName, 188 "annotation": opts.OsDistribution, 189 } 190 ret := struct { 191 TaskUUID string 192 }{} 193 err := self.post("images", jsonutils.Marshal(params), &ret) 194 if err != nil { 195 return nil, errors.Wrapf(err, "create image") 196 } 197 imageId := "" 198 err = cloudprovider.Wait(time.Second*5, time.Minute*3, func() (bool, error) { 199 task, err := self.GetTask(ret.TaskUUID) 200 if err != nil { 201 return false, err 202 } 203 for _, entity := range task.EntityList { 204 imageId = entity.EntityID 205 } 206 log.Debugf("task %s %s status: %s", task.OperationType, task.UUID, task.ProgressStatus) 207 if task.ProgressStatus == "Succeeded" { 208 for _, entity := range task.EntityList { 209 imageId = entity.EntityID 210 } 211 return true, nil 212 } 213 return false, nil 214 }) 215 if err != nil { 216 return nil, err 217 } 218 header := http.Header{} 219 header.Set("X-Nutanix-Destination-Container", storageId) 220 header.Set("Content-Type", "application/octet-stream") 221 header.Set("Content-Length", fmt.Sprintf("%d", sizeBytes)) 222 reader := multicloud.NewProgress(sizeBytes, 90, body, callback) 223 resp, err := self.upload("images", fmt.Sprintf("%s/upload", imageId), header, reader) 224 if err != nil { 225 return nil, errors.Wrapf(err, "upload") 226 } 227 resp.Unmarshal(&ret) 228 err = cloudprovider.Wait(time.Second*10, time.Hour*2, func() (bool, error) { 229 task, err := self.GetTask(ret.TaskUUID) 230 if err != nil { 231 return false, err 232 } 233 if callback != nil { 234 callback(90 + float32(task.PercentageComplete)*0.1) 235 } 236 log.Debugf("task %s %s status: %s", task.OperationType, task.UUID, task.ProgressStatus) 237 if task.ProgressStatus == "Succeeded" { 238 for _, entity := range task.EntityList { 239 imageId = entity.EntityID 240 } 241 return true, nil 242 } 243 return false, nil 244 }) 245 if err != nil { 246 return nil, errors.Wrapf(err, "wait image ready") 247 } 248 return self.GetImage(imageId) 249 }