image.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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 apsara
  15. import (
  16. "context"
  17. "fmt"
  18. "strings"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/util/imagetools"
  23. api "yunion.io/x/cloudmux/pkg/apis/compute"
  24. "yunion.io/x/cloudmux/pkg/cloudprovider"
  25. "yunion.io/x/cloudmux/pkg/multicloud"
  26. )
  27. type ImageStatusType string
  28. const (
  29. ImageStatusCreating ImageStatusType = "Creating"
  30. ImageStatusAvailable ImageStatusType = "Available"
  31. ImageStatusUnAvailable ImageStatusType = "UnAvailable"
  32. ImageStatusCreateFailed ImageStatusType = "CreateFailed"
  33. )
  34. type ImageOwnerType string
  35. const (
  36. ImageOwnerSystem ImageOwnerType = "system"
  37. ImageOwnerSelf ImageOwnerType = "self"
  38. ImageOwnerOthers ImageOwnerType = "others"
  39. ImageOwnerMarketplace ImageOwnerType = "marketplace"
  40. )
  41. type ImageUsageType string
  42. const (
  43. ImageUsageInstance ImageUsageType = "instance"
  44. ImageUsageNone ImageUsageType = "none"
  45. )
  46. type SImage struct {
  47. multicloud.SImageBase
  48. ApsaraTags
  49. storageCache *SStoragecache
  50. // normalized image info
  51. imgInfo *imagetools.ImageInfo
  52. Architecture string
  53. CreationTime time.Time
  54. Description string
  55. ImageId string
  56. ImageName string
  57. OSName string
  58. OSType string
  59. ImageOwnerAlias ImageOwnerType
  60. IsSupportCloudinit bool
  61. IsSupportIoOptimized bool
  62. Platform string
  63. Size int
  64. Status ImageStatusType
  65. Usage string
  66. }
  67. func (self *SImage) GetMinRamSizeMb() int {
  68. return 0
  69. }
  70. func (self *SImage) GetId() string {
  71. return self.ImageId
  72. }
  73. func (self *SImage) GetName() string {
  74. if self.ImageOwnerAlias == ImageOwnerSystem {
  75. return self.OSName
  76. } else {
  77. return self.ImageName
  78. }
  79. }
  80. func (self *SImage) IsEmulated() bool {
  81. return false
  82. }
  83. func (self *SImage) Delete(ctx context.Context) error {
  84. return self.storageCache.region.DeleteImage(self.ImageId)
  85. }
  86. func (self *SImage) GetGlobalId() string {
  87. return self.ImageId
  88. }
  89. func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
  90. return self.storageCache
  91. }
  92. func (self *SImage) GetStatus() string {
  93. switch self.Status {
  94. case ImageStatusCreating:
  95. return api.CACHED_IMAGE_STATUS_SAVING
  96. case ImageStatusAvailable:
  97. return api.CACHED_IMAGE_STATUS_ACTIVE
  98. case ImageStatusUnAvailable:
  99. return api.CACHED_IMAGE_STATUS_CACHE_FAILED
  100. case ImageStatusCreateFailed:
  101. return api.CACHED_IMAGE_STATUS_CACHE_FAILED
  102. default:
  103. return api.CACHED_IMAGE_STATUS_CACHE_FAILED
  104. }
  105. }
  106. func (self *SImage) GetImageStatus() string {
  107. switch self.Status {
  108. case ImageStatusCreating:
  109. return cloudprovider.IMAGE_STATUS_QUEUED
  110. case ImageStatusAvailable:
  111. return cloudprovider.IMAGE_STATUS_ACTIVE
  112. case ImageStatusUnAvailable:
  113. return cloudprovider.IMAGE_STATUS_DELETED
  114. case ImageStatusCreateFailed:
  115. return cloudprovider.IMAGE_STATUS_KILLED
  116. default:
  117. return cloudprovider.IMAGE_STATUS_KILLED
  118. }
  119. }
  120. func (self *SImage) Refresh() error {
  121. new, err := self.storageCache.region.GetImage(self.ImageId)
  122. if err != nil {
  123. return err
  124. }
  125. return jsonutils.Update(self, new)
  126. }
  127. func (self *SImage) GetImageType() cloudprovider.TImageType {
  128. switch self.ImageOwnerAlias {
  129. case ImageOwnerSystem:
  130. return cloudprovider.ImageTypeSystem
  131. case ImageOwnerSelf:
  132. return cloudprovider.ImageTypeCustomized
  133. case ImageOwnerMarketplace:
  134. return cloudprovider.ImageTypeMarket
  135. case ImageOwnerOthers:
  136. return cloudprovider.ImageTypeShared
  137. default:
  138. return cloudprovider.ImageTypeCustomized
  139. }
  140. }
  141. func (self *SImage) GetSizeByte() int64 {
  142. return int64(self.Size) * 1024 * 1024 * 1024
  143. }
  144. func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
  145. if self.imgInfo == nil {
  146. imgInfo := imagetools.NormalizeImageInfo(self.OSName, self.Architecture, self.OSType, self.Platform, "")
  147. self.imgInfo = &imgInfo
  148. }
  149. return self.imgInfo
  150. }
  151. func (self *SImage) GetFullOsName() string {
  152. return self.OSName
  153. }
  154. func (self *SImage) GetOsType() cloudprovider.TOsType {
  155. return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType)
  156. }
  157. func (self *SImage) GetOsDist() string {
  158. return self.getNormalizedImageInfo().OsDistro
  159. }
  160. func (self *SImage) GetOsVersion() string {
  161. return self.getNormalizedImageInfo().OsVersion
  162. }
  163. func (self *SImage) GetOsLang() string {
  164. return self.getNormalizedImageInfo().OsLang
  165. }
  166. func (self *SImage) GetOsArch() string {
  167. return self.getNormalizedImageInfo().OsArch
  168. }
  169. func (self *SImage) GetBios() cloudprovider.TBiosType {
  170. return cloudprovider.ToBiosType(self.getNormalizedImageInfo().OsBios)
  171. }
  172. func (self *SImage) GetMinOsDiskSizeGb() int {
  173. return 40
  174. }
  175. func (self *SImage) GetImageFormat() string {
  176. return "vhd"
  177. }
  178. func (self *SImage) GetCreatedAt() time.Time {
  179. return self.CreationTime
  180. }
  181. type ImageExportTask struct {
  182. ImageId string
  183. RegionId string
  184. // RequestId string
  185. TaskId string
  186. }
  187. func (self *SRegion) ExportImage(imageId string, bucketName string) (*ImageExportTask, error) {
  188. params := make(map[string]string)
  189. params["RegionId"] = self.RegionId
  190. params["ImageId"] = imageId
  191. params["OssBucket"] = bucketName
  192. params["OssPrefix"] = fmt.Sprintf("%sexport", strings.Replace(imageId, "-", "", -1))
  193. if body, err := self.ecsRequest("ExportImage", params); err != nil {
  194. return nil, err
  195. } else {
  196. result := ImageExportTask{}
  197. if err := body.Unmarshal(&result); err != nil {
  198. log.Errorf("unmarshal result error %s", err)
  199. return nil, err
  200. }
  201. return &result, nil
  202. }
  203. }
  204. // {"ImageId":"m-j6c1qlpa7oebbg1n2k60","RegionId":"cn-hongkong","RequestId":"F8B2F6A1-F6AA-4C92-A54C-C4A309CF811F","TaskId":"t-j6c1qlpa7oebbg1rcl9t"}
  205. type ImageImportTask struct {
  206. ImageId string
  207. RegionId string
  208. // RequestId string
  209. TaskId string
  210. }
  211. func (self *SRegion) ImportImage(name string, osArch string, osType string, osDist string, bucket string, key string) (*ImageImportTask, error) {
  212. params := make(map[string]string)
  213. params["RegionId"] = self.RegionId
  214. params["ImageName"] = name
  215. if osDist == "RHEL" {
  216. osDist = "CentOS"
  217. }
  218. params["Platform"] = osDist // "Others Linux"
  219. params["OSType"] = osType // "linux"
  220. params["Architecture"] = osArch // "x86_64"
  221. params["DiskDeviceMapping.1.OSSBucket"] = bucket
  222. params["DiskDeviceMapping.1.OSSObject"] = key
  223. log.Debugf("Upload image with params %#v", params)
  224. body, err := self.ecsRequest("ImportImage", params)
  225. if err != nil {
  226. log.Errorf("ImportImage fail %s", err)
  227. return nil, err
  228. }
  229. log.Infof("%s", body)
  230. result := ImageImportTask{}
  231. err = body.Unmarshal(&result)
  232. if err != nil {
  233. log.Errorf("unmarshal result error %s", err)
  234. return nil, err
  235. }
  236. return &result, nil
  237. }
  238. func (self *SRegion) GetImage(imageId string) (*SImage, error) {
  239. for _, owner := range []ImageOwnerType{ImageOwnerSelf, ImageOwnerSystem} {
  240. images, _, err := self.GetImages("", owner, []string{imageId}, "", 0, 1)
  241. if err != nil {
  242. return nil, err
  243. }
  244. if len(images) == 1 {
  245. return &images[0], nil
  246. }
  247. }
  248. return nil, cloudprovider.ErrNotFound
  249. }
  250. func (self *SRegion) GetImageByName(name string) (*SImage, error) {
  251. for _, owner := range []ImageOwnerType{ImageOwnerSelf, ImageOwnerSystem} {
  252. images, _, err := self.GetImages("", owner, nil, name, 0, 1)
  253. if err != nil {
  254. return nil, err
  255. }
  256. if len(images) == 1 {
  257. return &images[0], nil
  258. }
  259. }
  260. return nil, cloudprovider.ErrNotFound
  261. }
  262. func (self *SRegion) GetImagesBySnapshot(snapshotId string, offset int, limit int) ([]SImage, int, error) {
  263. if limit > 50 || limit <= 0 {
  264. limit = 50
  265. }
  266. params := make(map[string]string)
  267. params["RegionId"] = self.RegionId
  268. params["PageSize"] = fmt.Sprintf("%d", limit)
  269. params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1)
  270. params["SnapshotId"] = snapshotId
  271. return self.getImages(params)
  272. }
  273. func (self *SRegion) GetImageStatus(imageId string) (ImageStatusType, error) {
  274. image, err := self.GetImage(imageId)
  275. if err != nil {
  276. return "", err
  277. }
  278. return image.Status, nil
  279. }
  280. func (self *SRegion) GetImages(status ImageStatusType, owner ImageOwnerType, imageId []string, name string, offset int, limit int) ([]SImage, int, error) {
  281. if limit > 50 || limit <= 0 {
  282. limit = 50
  283. }
  284. params := make(map[string]string)
  285. params["RegionId"] = self.RegionId
  286. params["PageSize"] = fmt.Sprintf("%d", limit)
  287. params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1)
  288. if len(status) > 0 {
  289. params["Status"] = string(status)
  290. } else {
  291. params["Status"] = "Creating,Available,UnAvailable,CreateFailed"
  292. }
  293. if imageId != nil && len(imageId) > 0 {
  294. params["ImageId"] = strings.Join(imageId, ",")
  295. }
  296. if len(owner) > 0 {
  297. params["ImageOwnerAlias"] = string(owner)
  298. }
  299. if len(name) > 0 {
  300. params["ImageName"] = name
  301. }
  302. return self.getImages(params)
  303. }
  304. func (self *SRegion) getImages(params map[string]string) ([]SImage, int, error) {
  305. body, err := self.ecsRequest("DescribeImages", params)
  306. if err != nil {
  307. log.Errorf("DescribeImages fail %s", err)
  308. return nil, 0, err
  309. }
  310. images := make([]SImage, 0)
  311. err = body.Unmarshal(&images, "Images", "Image")
  312. if err != nil {
  313. log.Errorf("unmarshal images fail %s", err)
  314. return nil, 0, nil
  315. }
  316. total, _ := body.Int("TotalCount")
  317. return images, int(total), nil
  318. }
  319. func (self *SRegion) DeleteImage(imageId string) error {
  320. params := make(map[string]string)
  321. params["RegionId"] = self.RegionId
  322. params["ImageId"] = imageId
  323. params["Force"] = "true"
  324. _, err := self.ecsRequest("DeleteImage", params)
  325. if err != nil {
  326. log.Errorf("DeleteImage fail %s", err)
  327. return err
  328. }
  329. return nil
  330. }