image.go 11 KB

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