image.go 6.5 KB

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