image.go 8.6 KB

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