image.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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 huawei
  15. import (
  16. "context"
  17. "fmt"
  18. "net/url"
  19. "strings"
  20. "time"
  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. const (
  31. ImageStatusQueued = "queued" // queued:表示镜像元数据已经创建成功,等待上传镜像文件。
  32. ImageStatusSaving = "saving" // saving:表示镜像正在上传文件到后端存储。
  33. ImageStatusDeleted = "deleted" // deleted:表示镜像已经删除。
  34. ImageStatusKilled = "killed" // killed:表示镜像上传错误。
  35. ImageStatusActive = "active" // active:表示镜像可以正常使用
  36. )
  37. type SImage struct {
  38. multicloud.SImageBase
  39. HuaweiTags
  40. storageCache *SStoragecache
  41. // normalized image info
  42. imgInfo *imagetools.ImageInfo
  43. Schema string `json:"schema"`
  44. MinDiskGB int64 `json:"min_disk"`
  45. CreatedAt time.Time `json:"created_at"`
  46. ImageSourceType string `json:"__image_source_type"`
  47. ContainerFormat string `json:"container_format"`
  48. File string `json:"file"`
  49. UpdatedAt time.Time `json:"updated_at"`
  50. Protected bool `json:"protected"`
  51. Checksum string `json:"checksum"`
  52. ID string `json:"id"`
  53. Isregistered string `json:"__isregistered"`
  54. MinRamMB int `json:"min_ram"`
  55. Lazyloading string `json:"__lazyloading"`
  56. Owner string `json:"owner"`
  57. OSType string `json:"__os_type"`
  58. Imagetype string `json:"__imagetype"`
  59. Visibility string `json:"visibility"`
  60. VirtualEnvType string `json:"virtual_env_type"`
  61. Platform string `json:"__platform"`
  62. SizeGB int `json:"size"`
  63. ImageSize int64 `json:"__image_size"`
  64. OSBit string `json:"__os_bit"`
  65. OSVersion string `json:"__os_version"`
  66. Name string `json:"name"`
  67. Self string `json:"self"`
  68. DiskFormat string `json:"disk_format"`
  69. Status string `json:"status"`
  70. SupportKVMFPGAType string `json:"__support_kvm_fpga_type"`
  71. SupportKVMNVMEHIGHIO string `json:"__support_nvme_highio"`
  72. SupportLargeMemory string `json:"__support_largememory"`
  73. SupportDiskIntensive string `json:"__support_diskintensive"`
  74. SupportHighPerformance string `json:"__support_highperformance"`
  75. SupportXENGPUType string `json:"__support_xen_gpu_type"`
  76. SupportKVMGPUType string `json:"__support_kvm_gpu_type"`
  77. SupportGPUT4 string `json:"__support_gpu_t4"`
  78. SupportKVMAscend310 string `json:"__support_kvm_ascend_310"`
  79. SupportArm string `json:"__support_arm"`
  80. }
  81. func (self *SImage) GetMinRamSizeMb() int {
  82. return self.MinRamMB
  83. }
  84. func (self *SImage) GetId() string {
  85. return self.ID
  86. }
  87. func (self *SImage) GetName() string {
  88. return self.Name
  89. }
  90. func (self *SImage) GetGlobalId() string {
  91. return self.ID
  92. }
  93. func (self *SImage) GetStatus() string {
  94. switch self.Status {
  95. case ImageStatusQueued:
  96. return api.CACHED_IMAGE_STATUS_CACHING
  97. case ImageStatusActive:
  98. return api.CACHED_IMAGE_STATUS_ACTIVE
  99. case ImageStatusKilled:
  100. return api.CACHED_IMAGE_STATUS_CACHE_FAILED
  101. default:
  102. return api.CACHED_IMAGE_STATUS_CACHE_FAILED
  103. }
  104. }
  105. func (self *SImage) GetImageStatus() string {
  106. switch self.Status {
  107. case ImageStatusQueued:
  108. return cloudprovider.IMAGE_STATUS_QUEUED
  109. case ImageStatusActive:
  110. return cloudprovider.IMAGE_STATUS_ACTIVE
  111. case ImageStatusKilled:
  112. return cloudprovider.IMAGE_STATUS_KILLED
  113. default:
  114. return cloudprovider.IMAGE_STATUS_KILLED
  115. }
  116. }
  117. func (self *SImage) Refresh() error {
  118. image, err := self.storageCache.region.GetImage(self.GetId())
  119. if err != nil {
  120. return err
  121. }
  122. return jsonutils.Update(self, image)
  123. }
  124. func (self *SImage) GetImageType() cloudprovider.TImageType {
  125. switch self.Imagetype {
  126. case "gold":
  127. return cloudprovider.ImageTypeSystem
  128. case "private":
  129. return cloudprovider.ImageTypeCustomized
  130. case "shared":
  131. return cloudprovider.ImageTypeShared
  132. default:
  133. return cloudprovider.ImageTypeCustomized
  134. }
  135. }
  136. func (self *SImage) GetSizeByte() int64 {
  137. return int64(self.MinDiskGB) * 1024 * 1024 * 1024
  138. }
  139. func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
  140. if self.imgInfo == nil {
  141. arch := "x86"
  142. if strings.ToLower(self.SupportArm) == "true" {
  143. arch = "arm"
  144. }
  145. imgInfo := imagetools.NormalizeImageInfo(self.ImageSourceType, arch, self.OSType, self.Platform, "")
  146. self.imgInfo = &imgInfo
  147. }
  148. return self.imgInfo
  149. }
  150. func (self *SImage) GetFullOsName() string {
  151. return self.ImageSourceType
  152. }
  153. func (self *SImage) GetOsType() cloudprovider.TOsType {
  154. return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType)
  155. }
  156. func (self *SImage) GetOsDist() string {
  157. return self.getNormalizedImageInfo().OsDistro
  158. }
  159. func (self *SImage) GetOsVersion() string {
  160. return self.getNormalizedImageInfo().OsVersion
  161. }
  162. func (self *SImage) GetOsLang() string {
  163. return self.getNormalizedImageInfo().OsLang
  164. }
  165. func (self *SImage) GetOsArch() string {
  166. return self.getNormalizedImageInfo().OsArch
  167. }
  168. func (self *SImage) GetBios() cloudprovider.TBiosType {
  169. return cloudprovider.ToBiosType(self.getNormalizedImageInfo().OsBios)
  170. }
  171. func (self *SImage) GetMinOsDiskSizeGb() int {
  172. return int(self.MinDiskGB)
  173. }
  174. func (self *SImage) GetImageFormat() string {
  175. return self.DiskFormat
  176. }
  177. func (self *SImage) GetCreatedAt() time.Time {
  178. return self.CreatedAt
  179. }
  180. func (self *SImage) Delete(ctx context.Context) error {
  181. return self.storageCache.region.DeleteImage(self.GetId())
  182. }
  183. func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
  184. return self.storageCache
  185. }
  186. func (self *SRegion) GetImage(imageId string) (*SImage, error) {
  187. images, err := self.GetImages(imageId, "", "", "")
  188. if err != nil {
  189. return nil, err
  190. }
  191. for i := range images {
  192. if images[i].ID == imageId {
  193. return &images[i], nil
  194. }
  195. }
  196. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", imageId)
  197. }
  198. // https://console.huaweicloud.com/apiexplorer/#/openapi/IMS/doc?api=ListImages
  199. func (self *SRegion) GetImages(id, status string, imagetype string, name string) ([]SImage, error) {
  200. query := url.Values{}
  201. query.Set("virtual_env_type", "FusionCompute")
  202. if len(status) > 0 {
  203. query.Set("status", status)
  204. }
  205. if len(id) > 0 {
  206. query.Set("id", id)
  207. }
  208. if len(imagetype) > 0 {
  209. query.Set("__imagetype", string(imagetype))
  210. if imagetype == "gold" {
  211. query.Set("protected", "True")
  212. }
  213. }
  214. if len(name) > 0 {
  215. query.Set("name", name)
  216. }
  217. ret := []SImage{}
  218. for {
  219. resp, err := self.list(SERVICE_IMS, "cloudimages", query)
  220. if err != nil {
  221. return nil, err
  222. }
  223. part := struct {
  224. Images []SImage
  225. }{}
  226. err = resp.Unmarshal(&part)
  227. if err != nil {
  228. return nil, errors.Wrapf(err, "Unmarshal")
  229. }
  230. ret = append(ret, part.Images...)
  231. if len(part.Images) == 0 {
  232. break
  233. }
  234. query.Set("marker", part.Images[len(part.Images)-1].ID)
  235. }
  236. return ret, nil
  237. }
  238. func (self *SRegion) DeleteImage(imageId string) error {
  239. return cloudprovider.ErrNotImplemented
  240. }
  241. func (self *SRegion) GetImageByName(name string) (*SImage, error) {
  242. images, err := self.GetImages("", "", "", name)
  243. if err != nil {
  244. return nil, err
  245. }
  246. for i := range images {
  247. if images[i].Name == name {
  248. return &images[i], nil
  249. }
  250. }
  251. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", name)
  252. }
  253. func (self *SRegion) ImportImageJob(name string, osDist string, osVersion string, osArch string, bucket string, key string, minDiskGB int64) (string, error) {
  254. osVersion, err := stdVersion(osDist, osVersion, osArch)
  255. log.Debugf("%s %s %s: %s.min_disk %d GB", osDist, osVersion, osArch, osVersion, minDiskGB)
  256. imageUrl := fmt.Sprintf("%s:%s", bucket, key)
  257. params := map[string]interface{}{
  258. "name": name,
  259. "image_url": imageUrl,
  260. "is_config_init": true,
  261. "is_config": true,
  262. "min_disk": minDiskGB,
  263. }
  264. if len(osVersion) > 0 {
  265. params["os_version"] = osVersion
  266. }
  267. resp, err := self.post(SERVICE_IMS, "cloudimages/quickimport/action", params)
  268. if err != nil {
  269. return "", errors.Wrapf(err, "import image")
  270. }
  271. return resp.GetString("job_id")
  272. }
  273. func formatVersion(osDist string, osVersion string) (string, error) {
  274. err := fmt.Errorf("unsupport version %s.reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", osVersion)
  275. dist := strings.ToLower(osDist)
  276. if dist == "ubuntu" || dist == "redhat" || dist == "centos" || dist == "oracle" || dist == "euleros" {
  277. parts := strings.Split(osVersion, ".")
  278. if len(parts) < 2 {
  279. return "", err
  280. }
  281. return parts[0] + "." + parts[1], nil
  282. }
  283. if dist == "debian" {
  284. parts := strings.Split(osVersion, ".")
  285. if len(parts) < 3 {
  286. return "", err
  287. }
  288. return parts[0] + "." + parts[1] + "." + parts[2], nil
  289. }
  290. if dist == "fedora" || dist == "windows" || dist == "suse" {
  291. parts := strings.Split(osVersion, ".")
  292. if len(parts) < 1 {
  293. return "", err
  294. }
  295. return parts[0], nil
  296. }
  297. if dist == "opensuse" {
  298. parts := strings.Split(osVersion, ".")
  299. if len(parts) == 0 {
  300. return "", err
  301. }
  302. if len(parts) == 1 {
  303. return parts[0], nil
  304. }
  305. if len(parts) >= 2 {
  306. return parts[0] + "." + parts[1], nil
  307. }
  308. }
  309. return "", err
  310. }
  311. // todo: 如何保持同步更新
  312. // https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html
  313. func stdVersion(osDist string, osVersion string, osArch string) (string, error) {
  314. // 架构
  315. arch := ""
  316. switch osArch {
  317. case "64", apis.OS_ARCH_X86_64:
  318. arch = "64bit"
  319. case "32", apis.OS_ARCH_X86_32:
  320. arch = "32bit"
  321. default:
  322. return "", fmt.Errorf("unsupported arch %s.reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", osArch)
  323. }
  324. _dist := strings.Split(strings.TrimSpace(osDist), " ")[0]
  325. _dist = strings.ToLower(_dist)
  326. // 版本
  327. ver, err := formatVersion(_dist, osVersion)
  328. if err != nil {
  329. return "", err
  330. }
  331. // 操作系统
  332. dist := ""
  333. switch _dist {
  334. case "ubuntu":
  335. return fmt.Sprintf("Ubuntu %s server %s", ver, arch), nil
  336. case "redhat":
  337. dist = "Redhat Linux Enterprise"
  338. case "centos":
  339. dist = "CentOS"
  340. case "fedora":
  341. dist = "Fedora"
  342. case "debian":
  343. dist = "Debian GNU/Linux"
  344. case "windows":
  345. dist = "Windows Server"
  346. case "oracle":
  347. dist = "Oracle Linux Server release"
  348. case "suse":
  349. dist = "SUSE Linux Enterprise Server"
  350. case "opensuse":
  351. dist = "OpenSUSE"
  352. case "euleros":
  353. dist = "EulerOS"
  354. default:
  355. return "", fmt.Errorf("unsupported os %s. reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", dist)
  356. }
  357. return fmt.Sprintf("%s %s %s", dist, ver, arch), nil
  358. }