image.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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 qcloud
  15. import (
  16. "context"
  17. "fmt"
  18. "strconv"
  19. "strings"
  20. "time"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/util/imagetools"
  24. "yunion.io/x/pkg/utils"
  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. ImageStatusNormal ImageStatusType = "NORMAL"
  34. ImageStatusSycing ImageStatusType = "SYNCING"
  35. ImageStatusImporting ImageStatusType = "IMPORTING"
  36. ImageStatusUsing ImageStatusType = "USING"
  37. ImageStatusDeleting ImageStatusType = "DELETING"
  38. )
  39. type SImage struct {
  40. multicloud.SImageBase
  41. QcloudTags
  42. storageCache *SStoragecache
  43. // normalized image info
  44. imgInfo *imagetools.ImageInfo
  45. ImageId string // 镜像ID
  46. OsName string // 镜像操作系统
  47. ImageType string // 镜像类型
  48. CreatedTime time.Time // 镜像创建时间
  49. ImageName string // 镜像名称
  50. ImageDescription string // 镜像描述
  51. ImageSize int // 镜像大小
  52. Architecture string // 镜像架构
  53. ImageState ImageStatusType // 镜像状态
  54. Platform string // 镜像来源平台
  55. ImageCreator string // 镜像创建者
  56. ImageSource string // 镜像来源
  57. SyncPercent int // 同步百分比
  58. IsSupportCloudinit bool // 镜像是否支持cloud-init
  59. OsType string
  60. OsVersion string
  61. }
  62. func (self *SImage) GetMinRamSizeMb() int {
  63. return 0
  64. }
  65. func (self *SRegion) GetImages(status string, owner string, imageIds []string, name string, offset int, limit int) ([]SImage, int, error) {
  66. if limit > 50 || limit <= 0 {
  67. limit = 50
  68. }
  69. params := make(map[string]string)
  70. params["Limit"] = fmt.Sprintf("%d", limit)
  71. params["Offset"] = fmt.Sprintf("%d", offset)
  72. for index, imageId := range imageIds {
  73. params[fmt.Sprintf("ImageIds.%d", index)] = imageId
  74. }
  75. if len(imageIds) == 0 { // imageIds 不能和Filter同时查询
  76. filter := 0
  77. if len(status) > 0 {
  78. params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-state"
  79. params[fmt.Sprintf("Filters.%d.Values.0", filter)] = status
  80. filter++
  81. }
  82. if len(owner) > 0 {
  83. params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-type"
  84. params[fmt.Sprintf("Filters.%d.Values.0", filter)] = owner
  85. filter++
  86. }
  87. if len(name) > 0 {
  88. params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-name"
  89. params[fmt.Sprintf("Filters.%d.Values.0", filter)] = name
  90. filter++
  91. }
  92. }
  93. images := make([]SImage, 0)
  94. body, err := self.cvmRequest("DescribeImages", params, true)
  95. if err != nil {
  96. return nil, 0, err
  97. }
  98. err = body.Unmarshal(&images, "ImageSet")
  99. if err != nil {
  100. return nil, 0, err
  101. }
  102. for i := 0; i < len(images); i++ {
  103. images[i].storageCache = self.getStoragecache()
  104. }
  105. total, _ := body.Float("TotalCount")
  106. return images, int(total), nil
  107. }
  108. func (self *SImage) GetId() string {
  109. return self.ImageId
  110. }
  111. func (self *SImage) GetName() string {
  112. return self.ImageName
  113. }
  114. func (self *SImage) IsEmulated() bool {
  115. return false
  116. }
  117. func (self *SImage) GetGlobalId() string {
  118. return self.ImageId
  119. }
  120. func (self *SImage) Delete(ctx context.Context) error {
  121. return self.storageCache.region.DeleteImage(self.ImageId)
  122. }
  123. func (self *SImage) GetStatus() string {
  124. switch self.ImageState {
  125. case ImageStatusCreating, ImageStatusSycing, ImageStatusImporting:
  126. return api.CACHED_IMAGE_STATUS_CACHING
  127. case ImageStatusNormal, ImageStatusUsing:
  128. return api.CACHED_IMAGE_STATUS_ACTIVE
  129. default:
  130. return api.CACHED_IMAGE_STATUS_CACHE_FAILED
  131. }
  132. }
  133. func (self *SImage) GetImageStatus() string {
  134. switch self.ImageState {
  135. case ImageStatusCreating, ImageStatusSycing, ImageStatusImporting:
  136. return cloudprovider.IMAGE_STATUS_SAVING
  137. case ImageStatusNormal, ImageStatusUsing:
  138. return cloudprovider.IMAGE_STATUS_ACTIVE
  139. case ImageStatusDeleting:
  140. return cloudprovider.IMAGE_STATUS_DELETED
  141. default:
  142. return cloudprovider.IMAGE_STATUS_DELETED
  143. }
  144. }
  145. func (self *SImage) Refresh() error {
  146. new, err := self.storageCache.region.GetImage(self.ImageId)
  147. if err != nil {
  148. return err
  149. }
  150. return jsonutils.Update(self, new)
  151. }
  152. func (self *SImage) GetImageType() cloudprovider.TImageType {
  153. switch self.ImageType {
  154. case "PUBLIC_IMAGE":
  155. return cloudprovider.ImageTypeSystem
  156. case "PRIVATE_IMAGE":
  157. return cloudprovider.ImageTypeCustomized
  158. default:
  159. return cloudprovider.ImageTypeCustomized
  160. }
  161. }
  162. func (self *SImage) GetSizeByte() int64 {
  163. return int64(self.ImageSize) * 1024 * 1024 * 1024
  164. }
  165. func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
  166. if self.imgInfo == nil {
  167. imgInfo := imagetools.NormalizeImageInfo(self.OsName, self.Architecture, self.OsType, self.Platform, self.OsVersion)
  168. self.imgInfo = &imgInfo
  169. }
  170. return self.imgInfo
  171. }
  172. func (self *SImage) GetOsType() cloudprovider.TOsType {
  173. return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType)
  174. }
  175. func (self *SImage) GetOsDist() string {
  176. return self.getNormalizedImageInfo().OsDistro
  177. }
  178. func (self *SImage) GetOsVersion() string {
  179. return self.getNormalizedImageInfo().OsVersion
  180. }
  181. func (self *SImage) GetOsArch() string {
  182. return self.getNormalizedImageInfo().OsArch
  183. }
  184. func (img *SImage) GetBios() cloudprovider.TBiosType {
  185. return cloudprovider.ToBiosType(img.getNormalizedImageInfo().OsBios)
  186. }
  187. func (img *SImage) GetFullOsName() string {
  188. return img.OsName
  189. }
  190. func (img *SImage) GetOsLang() string {
  191. return img.getNormalizedImageInfo().OsLang
  192. }
  193. func (self *SImage) GetMinOsDiskSizeGb() int {
  194. return 50
  195. }
  196. func (self *SImage) GetImageFormat() string {
  197. return "qcow2"
  198. }
  199. func (self *SImage) GetCreatedAt() time.Time {
  200. return self.CreatedTime
  201. }
  202. func (self *SRegion) GetImage(imageId string) (*SImage, error) {
  203. images, _, err := self.GetImages("", "", []string{imageId}, "", 0, 1)
  204. if err != nil {
  205. return nil, err
  206. }
  207. if len(images) == 0 {
  208. return nil, fmt.Errorf("image %s not found", imageId)
  209. }
  210. return &images[0], nil
  211. }
  212. func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
  213. return self.storageCache
  214. }
  215. func (self *SRegion) DeleteImage(imageId string) error {
  216. params := make(map[string]string)
  217. params["ImageIds.0"] = imageId
  218. _, err := self.cvmRequest("DeleteImages", params, true)
  219. return err
  220. }
  221. func (self *SRegion) GetImageStatus(imageId string) (ImageStatusType, error) {
  222. image, err := self.GetImage(imageId)
  223. if err != nil {
  224. return "", err
  225. }
  226. return image.ImageState, nil
  227. }
  228. func (self *SRegion) GetImageByName(name string) (*SImage, error) {
  229. images, _, err := self.GetImages("", "", nil, name, 0, 1)
  230. if err != nil {
  231. return nil, err
  232. }
  233. if len(images) == 0 {
  234. return nil, cloudprovider.ErrNotFound
  235. }
  236. return &images[0], nil
  237. }
  238. type ImportImageOsListSupported struct {
  239. Linux []string //"CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux"
  240. Windows []string //"Windows Server 2008|Windows Server 2012|Windows Server 2016"
  241. }
  242. type ImportImageOsVersionSet struct {
  243. Architecture []string
  244. OsName string //"CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux|Windows Server 2008|Windows Server 2012|Windows Server 2016"
  245. OsVersions []string
  246. }
  247. type SupportImageSet struct {
  248. ImportImageOsListSupported ImportImageOsListSupported
  249. ImportImageOsVersionSet []ImportImageOsVersionSet
  250. }
  251. func (self *SRegion) GetSupportImageSet() (*SupportImageSet, error) {
  252. body, err := self.cvmRequest("DescribeImportImageOs", map[string]string{}, true)
  253. if err != nil {
  254. return nil, err
  255. }
  256. imageSet := SupportImageSet{}
  257. return &imageSet, body.Unmarshal(&imageSet)
  258. }
  259. func (self *SRegion) GetImportImageParams(name string, osArch, osDist, osVersion string, imageUrl string) (map[string]string, error) {
  260. params := map[string]string{}
  261. imageSet, err := self.GetSupportImageSet()
  262. if err != nil {
  263. return nil, err
  264. }
  265. osType := ""
  266. for _, _imageSet := range imageSet.ImportImageOsVersionSet {
  267. if strings.ToLower(osDist) == strings.ToLower(_imageSet.OsName) { //Linux一般可正常匹配
  268. osType = _imageSet.OsName
  269. } else if strings.Contains(strings.ToLower(_imageSet.OsName), "windows") && strings.Contains(strings.ToLower(osDist), "windows") {
  270. info := strings.Split(_imageSet.OsName, " ")
  271. _osVersion := "2008"
  272. for _, version := range info {
  273. if _, err := strconv.Atoi(version); err == nil {
  274. _osVersion = version
  275. break
  276. }
  277. }
  278. if strings.Contains(osDist+osVersion, _osVersion) {
  279. osType = _imageSet.OsName
  280. }
  281. }
  282. if len(osType) == 0 {
  283. continue
  284. }
  285. if !utils.IsInStringArray(osArch, _imageSet.Architecture) {
  286. osArch = apis.OS_ARCH_X86_64
  287. }
  288. for _, _osVersion := range _imageSet.OsVersions {
  289. if strings.HasPrefix(osVersion, _osVersion) {
  290. osVersion = _osVersion
  291. break
  292. }
  293. }
  294. if !utils.IsInStringArray(osVersion, _imageSet.OsVersions) {
  295. osVersion = "-"
  296. if len(_imageSet.OsVersions) > 0 {
  297. osVersion = _imageSet.OsVersions[0]
  298. }
  299. }
  300. break
  301. }
  302. if len(osType) == 0 {
  303. osType = "Other Linux"
  304. osArch = apis.OS_ARCH_X86_64
  305. osVersion = "-"
  306. }
  307. params["ImageName"] = name
  308. params["OsType"] = osType
  309. params["OsVersion"] = osVersion
  310. params["Architecture"] = osArch // "x86_64|i386"
  311. params["ImageUrl"] = imageUrl
  312. params["Force"] = "true"
  313. return params, nil
  314. }
  315. func (self *SRegion) ImportImage(name string, osArch, osDist, osVersion string, imageUrl string) (*SImage, error) {
  316. params, err := self.GetImportImageParams(name, osArch, osDist, osVersion, imageUrl)
  317. if err != nil {
  318. return nil, err
  319. }
  320. log.Debugf("Upload image with params %#v", params)
  321. if _, err := self.cvmRequest("ImportImage", params, true); err != nil {
  322. return nil, err
  323. }
  324. for i := 0; i < 8; i++ {
  325. image, err := self.GetImageByName(name)
  326. if err == nil {
  327. return image, nil
  328. }
  329. time.Sleep(time.Minute * time.Duration(i))
  330. }
  331. return nil, cloudprovider.ErrNotFound
  332. }