ImageSelect.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. <template>
  2. <div class="os-image-select-wrapper">
  3. <a-row :gutter="8">
  4. <a-col :span="8" v-if="showCloudaccount">
  5. <a-form-item :wrapperCol="{ span: 24 }" class="mb-0">
  6. <base-select
  7. v-decorator="decorator.prefer_manager"
  8. resource="cloudproviders"
  9. @change="cloudproviderChange"
  10. :params="cloudproviderParams"
  11. :isDefaultSelect="true"
  12. :resList.sync="cloudproviderList"
  13. :select-props="{ placeholder: $t('compute.text_149'), disabled: imageCloudproviderDisabled }" />
  14. </a-form-item>
  15. </a-col>
  16. <a-col :span="showCloudaccount ? 5 : 6">
  17. <a-form-item :wrapperCol="{ span: 24 }" class="mb-0">
  18. <a-select v-decorator="decorator.os" :loading="loading" @change="osChange" :placeholder="$t('compute.text_153')">
  19. <a-select-option v-for="item in imagesInfo.osOpts" :key="item.key">
  20. <div :key="item.key" class="d-flex align-items-center">
  21. <image-icon v-show="item.key !== 'all'" :image="item.key" />
  22. <span :class="{ 'ml-2': item.key !== 'all' }">{{ item.label }}</span>
  23. </div>
  24. </a-select-option>
  25. </a-select>
  26. </a-form-item>
  27. </a-col>
  28. <a-col :span="showCloudaccount ? 11 : 18">
  29. <a-form-item :wrapperCol="{ span: 24 }" class="mb-0">
  30. <image-select-template v-decorator="decorator.image" :imageOpts="imageOptions" @imageChange="imageChange" :loading="loading" :imageType="imageType" />
  31. </a-form-item>
  32. </a-col>
  33. </a-row>
  34. <span v-if="isShowErrorInfo" class="error-color">{{$t('compute.text_150')}}</span>
  35. </div>
  36. </template>
  37. <script>
  38. import * as R from 'ramda'
  39. import _ from 'lodash'
  40. import { SELECT_IMAGE_KEY_SUFFIX } from '@Compute/constants'
  41. import { Manager } from '@/utils/manager'
  42. import { HYPERVISORS_MAP } from '@/constants'
  43. import { IMAGES_TYPE_MAP, OS_TYPE_OPTION_MAP } from '@/constants/compute'
  44. import storage from '@/utils/storage'
  45. import { uuid } from '@/utils/utils'
  46. import ImageSelectTemplate from './ImageSelectTemplate'
  47. const initData = undefined
  48. export default {
  49. name: 'ImageSelect',
  50. components: {
  51. ImageSelectTemplate,
  52. },
  53. props: {
  54. imageType: {
  55. type: String,
  56. required: true,
  57. validator: val => !R.isNil(IMAGES_TYPE_MAP[val]),
  58. },
  59. cloudType: {
  60. type: String,
  61. default: 'idc',
  62. validator: val => ['public', 'private', 'idc', 'baremetal'].includes(val),
  63. },
  64. decorator: {
  65. type: Object,
  66. required: true,
  67. validator: val => R.is(Array, val.os) && R.is(Array, val.image),
  68. },
  69. imageParams: {
  70. type: Object,
  71. },
  72. form: {
  73. type: Object,
  74. required: true,
  75. validator: val => !R.isNil(val.fc),
  76. },
  77. cacheImageParams: {
  78. type: Object,
  79. },
  80. osType: {
  81. type: String,
  82. },
  83. osArch: {
  84. type: String,
  85. },
  86. uefi: {
  87. type: Boolean,
  88. },
  89. vgaPci: {
  90. type: Boolean,
  91. },
  92. cloudproviderParamsExtra: {
  93. type: Object,
  94. default: () => ({}),
  95. },
  96. imageCloudproviderDisabled: {
  97. type: Boolean,
  98. default: false,
  99. },
  100. sysDiskSize: {
  101. type: Number,
  102. },
  103. edit: {
  104. type: Boolean,
  105. default: false,
  106. },
  107. hypervisor: {
  108. type: String,
  109. },
  110. },
  111. data () {
  112. return {
  113. images: {
  114. list: [], // 公共镜像、iso、自定义镜像 list
  115. cacheimagesList: [], // idc: 镜像缓存list,用于对比哪些镜像已缓存,public|private: image-list
  116. hostimagesList: [], // 主机镜像 list
  117. instanceSnapshotsList: [], // 主机快照 list
  118. instanceBackupsList: [], // 主机备份 list
  119. },
  120. imagesInfo: {
  121. osOpts: [],
  122. imageOptsMap: {},
  123. },
  124. loading: false,
  125. imageOpts: [],
  126. cloudprovider: _.get(this.decorator, 'prefer_manager[1].initialValue') || '',
  127. cloudproviderList: [],
  128. }
  129. },
  130. computed: {
  131. apiCacheImgParams () {
  132. return IMAGES_TYPE_MAP[this.imageType].cacheImgParams || {}
  133. },
  134. apiImgParams () {
  135. return IMAGES_TYPE_MAP[this.imageType].imgParams || {}
  136. },
  137. isPublic () {
  138. return this.cloudType === 'public'
  139. },
  140. isPrivate () {
  141. return this.cloudType === 'private'
  142. },
  143. // 选择的镜像类型是否为公有云镜像
  144. isPublicImage () {
  145. return this.imageType === IMAGES_TYPE_MAP.public.key || this.imageType === IMAGES_TYPE_MAP.public_customize.key
  146. },
  147. // 选择的镜像类型是否为私有云镜像
  148. isPrivateImage () {
  149. return this.imageType === IMAGES_TYPE_MAP.private.key || this.imageType === IMAGES_TYPE_MAP.private_iso.key
  150. },
  151. isVMwareImage () {
  152. return this.imageType === IMAGES_TYPE_MAP.vmware.key
  153. },
  154. // 选择的镜像类型是否为主机镜像
  155. isHostImage () {
  156. return this.imageType === IMAGES_TYPE_MAP.host.key
  157. },
  158. // 选择的镜像类型是否为主机快照
  159. isShapshotImage () {
  160. return this.imageType === IMAGES_TYPE_MAP.snapshot.key
  161. },
  162. // 选择的镜像类型是否为主机备份
  163. isBackupImage () {
  164. return this.imageType === IMAGES_TYPE_MAP.backup.key
  165. },
  166. cacheimageIds () {
  167. return this.images.cacheimagesList.map(item => item.id)
  168. },
  169. storageSelectImage () { // public__select_image: {os: OS_TYPE_OPTION_MAP.Windows.value, image: {id: xxx, name: xxx}}
  170. return storage.get(`${this.cloudType}${SELECT_IMAGE_KEY_SUFFIX}`)
  171. },
  172. showCloudaccount () {
  173. if (!this.decorator.prefer_manager) return false
  174. const imageMsg = IMAGES_TYPE_MAP[this.imageType]
  175. return imageMsg && imageMsg.enable_cloudaccount
  176. },
  177. cloudproviderParams () {
  178. const params = {
  179. limit: 0,
  180. enabled: true,
  181. 'filter.0': 'status.equals("connected")',
  182. 'filter.1': 'health_status.equals("normal")',
  183. ...this.cloudproviderParamsExtra,
  184. }
  185. if (!params.scope && !params.project_domain) {
  186. params.scope = this.$store.getters.scope
  187. }
  188. if (this.showCloudaccount && this.form.fd.zone) {
  189. params.zone_id = this.form.fd.zone.key
  190. }
  191. return params
  192. },
  193. storageImage () {
  194. const initImageOs = this.decorator.os[1].initialValue
  195. const initImage = this.decorator.image[1].initialValue
  196. if (initImageOs && initImage) {
  197. return {
  198. os: initImageOs,
  199. image: initImage,
  200. }
  201. }
  202. if (this.storageSelectImage) {
  203. const [os, image] = this.storageSelectImage.split(':')
  204. if (os && image) {
  205. return {
  206. os,
  207. image,
  208. }
  209. }
  210. }
  211. return false
  212. },
  213. imageOptions () {
  214. const { os } = this.form.fc.getFieldsValue(['os'])
  215. let imageOptions = this.imageOpts
  216. // 所选镜像容量最小磁盘要求需小于虚拟机系统盘大小
  217. if (this.sysDiskSize) {
  218. imageOptions = this.imageOpts.filter((item) => {
  219. const minDisk = item.min_disk || (item.info && item.info.min_disk)
  220. if (minDisk) {
  221. return minDisk <= this.sysDiskSize
  222. }
  223. return true
  224. })
  225. }
  226. if (this.uefi) {
  227. const imageOpts = imageOptions.map((item) => {
  228. if (!item.properties?.uefi_support || item.properties?.uefi_support === 'false') {
  229. return {
  230. ...item,
  231. hidden: true,
  232. }
  233. }
  234. return item
  235. })
  236. const arr = imageOpts.filter((item) => { return !item.hidden })
  237. if (arr.length === 0 && os === OS_TYPE_OPTION_MAP.Windows.value) {
  238. this.form.fc.setFieldsValue({ [this.decorator.image[0]]: initData })
  239. }
  240. return imageOpts
  241. }
  242. return imageOptions
  243. },
  244. isVMware () {
  245. return this.imageType === IMAGES_TYPE_MAP.vmware.key
  246. },
  247. isShowErrorInfo () {
  248. return this.vgaPci
  249. },
  250. isCloudpods () {
  251. return this.hypervisor === HYPERVISORS_MAP.cloudpods.key
  252. },
  253. },
  254. watch: {
  255. imageType (val, oldVal) {
  256. if (R.equals(val, oldVal)) return
  257. this.fetchData()
  258. this.fetchCacheimages()
  259. },
  260. cacheImageParams (val, oldVal) {
  261. if (R.equals(val, oldVal)) return
  262. this.fetchCacheimages()
  263. },
  264. imageParams (val, oldVal) {
  265. if (R.equals(val, oldVal)) return
  266. this.fetchData()
  267. },
  268. showCloudaccount (val) {
  269. if (!val) { // 如果不显示云账号,清空 fd 中的 prefer_manager
  270. if (this.form && this.form.fd && this.form.fd.prefer_manager) {
  271. this.form.fd.prefer_manager = ''
  272. }
  273. } else { // 如果显示查看是否有初始化云订阅,如果有则请求 cacheImage-list
  274. this.$nextTick(() => {
  275. const { prefer_manager: preferManager } = this.form.fc.getFieldsValue([this.decorator.prefer_manager[0]])
  276. this.cloudproviderChange(preferManager)
  277. })
  278. }
  279. },
  280. 'form.fd.vmem' (val) {
  281. if (this.imagesInfo.osOpts && this.imagesInfo.osOpts.length) {
  282. const { os, image } = this.form.fc.getFieldsValue([this.decorator.os[0], this.decorator.image[0]])
  283. this.defaultSelect(os, image)
  284. }
  285. },
  286. cloudproviderList (val) {
  287. if (this.showCloudaccount && (!val || !val.length)) {
  288. this.images.cacheimagesList = []
  289. this.getImagesInfo()
  290. }
  291. },
  292. osArch (val, oldVal) {
  293. if (R.equals(val, oldVal)) return
  294. this.getImagesInfo()
  295. },
  296. vgaPci (val, oldVal) {
  297. if (R.equals(val, oldVal)) return
  298. this.getImagesInfo()
  299. },
  300. },
  301. created () {
  302. this.imagesM = new Manager('images', 'v1')
  303. this.cachedimagesM = new Manager('cachedimages', 'v2')
  304. this.guestimagesM = new Manager('guestimages', 'v1')
  305. this.instanceSnapshots = new Manager('instance_snapshots', 'v2')
  306. this.instanceBackups = new Manager('instancebackups', 'v2')
  307. this.fetchData()
  308. this.fetchCacheimages = _.debounce(this._fetchCacheimages, 500)
  309. if (this.isPublicImage || this.isPrivateImage || this.isVMware) {
  310. this.fetchCacheimages()
  311. }
  312. },
  313. methods: {
  314. fetchData () {
  315. this.images.list = []
  316. let params = {}
  317. switch (this.imageType) { // 自定义镜像
  318. case IMAGES_TYPE_MAP.host.key: // 主机镜像
  319. // eslint-disable-next-line no-case-declarations
  320. params = { ...this.imageParams, status: 'active' }
  321. this.fetchHostImages(params)
  322. break
  323. case IMAGES_TYPE_MAP.snapshot.key: // 主机快照
  324. params = { ...this.imageParams, status: 'ready', provider: this.cloudproviderParamsExtra?.provider }
  325. this.fetchSnapshotImages(params)
  326. break
  327. case IMAGES_TYPE_MAP.backup.key: // 主机备份
  328. params = { ...this.imageParams, status: 'ready', provider: this.cloudproviderParamsExtra?.provider }
  329. this.fetchBackupImages(params)
  330. break
  331. default: // image list
  332. this.fetchImages()
  333. break
  334. }
  335. },
  336. imageChange (imageObj) {
  337. let imageMsg = {}
  338. if (imageObj && R.is(Object, imageObj)) {
  339. const list = this.imageOptions
  340. imageMsg = list.find(image => image.id === imageObj.key)
  341. }
  342. this.$emit('updateImageMsg', { imageMsg })
  343. },
  344. osChange (osValue, imageValue) {
  345. this.defaultSelect(osValue, imageValue)
  346. },
  347. _resetImage () {
  348. const { os, image } = this.form.fc.getFieldsValue(['os', 'image'])
  349. if (os && image) {
  350. this.form.fc.setFieldsValue({ os: undefined })
  351. this.form.fc.setFieldsValue({ image: initData })
  352. }
  353. },
  354. async fetchImages () {
  355. if (this.isPublicImage || this.isPrivateImage || this.isVMware) return // 阻止不必要的请求
  356. let params = {
  357. limit: 0,
  358. details: true,
  359. status: 'active',
  360. is_guest_image: false,
  361. scope: this.$store.getters.scope,
  362. ...this.imageParams,
  363. }
  364. if (params.project_domain) {
  365. delete params.scope
  366. }
  367. if (this.cloudType === 'baremetal') {
  368. params = {
  369. limit: 0,
  370. details: true,
  371. status: 'active',
  372. scope: this.$store.getters.scope,
  373. ...this.imageParams,
  374. }
  375. }
  376. if (this.imageType === IMAGES_TYPE_MAP.iso.key) {
  377. params.disk_formats = 'iso'
  378. if (params['filter.0'] && params['filter.0'] === 'disk_format.notequals(iso)') Reflect.deleteProperty(params, 'filter.0')
  379. Reflect.deleteProperty(params, 'is_standard')
  380. } else if (this.imageType === IMAGES_TYPE_MAP.standard.key || this.imageType === IMAGES_TYPE_MAP.customize.key || this.imageType === IMAGES_TYPE_MAP.host.key) {
  381. // Cloudpods 支持选择iso
  382. const target = (this.cloudproviderList || []).filter(item => item.id === this.cloudprovider)
  383. if (target.length && target[0].provider === 'Cloudpods') {
  384. if (params['filter.0'] && params['filter.0'] === 'disk_format.notequals(iso)') Reflect.deleteProperty(params, 'filter.0')
  385. } else {
  386. params['filter.0'] = 'disk_format.notequals(iso)'
  387. }
  388. }
  389. if (this.imageType === IMAGES_TYPE_MAP.customize.key) {
  390. params.owner = this.$store.getters.userInfo.projectId
  391. params.is_standard = false
  392. }
  393. if (this.imageType === IMAGES_TYPE_MAP.standard.key) {
  394. params.is_standard = true
  395. }
  396. this.loading = true
  397. this._resetImage()
  398. try {
  399. const { data: { data = [] } } = await this.imagesM.list({ params })
  400. this.loading = false
  401. this.images.list = data
  402. if (this.cloudType === 'baremetal') {
  403. this.images.list = data.filter(item => { return item.properties && (item.properties.os_type === 'Linux' || item.properties.os_type === 'VMWare') })
  404. }
  405. this.getImagesInfo()
  406. } catch (error) {
  407. this.loading = false
  408. throw error
  409. }
  410. },
  411. async fetchHostImages (params) {
  412. this.loading = true
  413. this._resetImage()
  414. try {
  415. const { data: { data = [] } } = await this.guestimagesM.list({ params })
  416. this.loading = false
  417. this.images.list = data
  418. this.getImagesInfo()
  419. } catch (error) {
  420. this.loading = false
  421. throw error
  422. }
  423. },
  424. async fetchSnapshotImages (params) {
  425. this.loading = true
  426. this._resetImage()
  427. try {
  428. const { data: { data = [] } } = await this.instanceSnapshots.list({ params })
  429. this.loading = false
  430. this.images.list = data
  431. this.getImagesInfo()
  432. } catch (error) {
  433. this.loading = false
  434. throw error
  435. }
  436. },
  437. async fetchBackupImages (params) {
  438. this.loading = true
  439. this._resetImage()
  440. try {
  441. const { data: { data = [] } } = await this.instanceBackups.list({ params })
  442. this.loading = false
  443. this.images.list = data
  444. this.getImagesInfo()
  445. } catch (error) {
  446. this.loading = false
  447. throw error
  448. }
  449. },
  450. async _fetchCacheimages () {
  451. if (R.isNil(this.cacheImageParams) || R.isEmpty(this.cacheImageParams)) return
  452. if (!this.isPublicImage && !this.isPrivateImage && !this.isVMware) return // 阻止不必要的请求,仅这三种情况需要渲染的是cacheimage,而且现在没有[需要标出哪些已缓存]的功能了
  453. const params = {
  454. details: false,
  455. order_by: 'ref_count',
  456. order: 'desc',
  457. $t: uuid(),
  458. valid: true,
  459. ...this.cacheImageParams,
  460. }
  461. if (this.showCloudaccount) {
  462. if (this.cloudprovider) {
  463. params.cloudprovider = this.cloudprovider
  464. } else {
  465. return
  466. }
  467. }
  468. if (this.imageType === IMAGES_TYPE_MAP.public.key) {
  469. params.image_type = 'system'
  470. }
  471. if (this.imageType === IMAGES_TYPE_MAP.public_customize.key) {
  472. params.image_type = 'customized'
  473. }
  474. if (this.isVMware) {
  475. params.image_type = 'system'
  476. }
  477. if ((this.isPrivate && !this.isCloudpods) || this.isVMware) {
  478. params.project_domain = this.form.fd.domain?.key || this.$store.getters.userInfo.projectDomainId
  479. }
  480. this.loading = true
  481. this.images.cacheimagesList = []
  482. try {
  483. const { data: { data = [] } } = await this.cachedimagesM.list({ params })
  484. this.loading = false
  485. this.images.cacheimagesList = data
  486. this.getImagesInfo()
  487. } catch (error) {
  488. this.loading = false
  489. throw error
  490. }
  491. },
  492. cloudproviderChange (val) {
  493. this.cloudprovider = val
  494. this.fetchCacheimages()
  495. },
  496. getProperties (img) {
  497. if (!img) return null
  498. if (this.isPublicImage || this.isPrivateImage || this.isVMwareImage) {
  499. return img.info?.properties
  500. }
  501. if (this.isShapshotImage || this.isBackupImage) {
  502. return img.server_metadata
  503. }
  504. return img.properties
  505. },
  506. getOsDistribution (osDistribution) {
  507. if (osDistribution.indexOf(OS_TYPE_OPTION_MAP.Windows.value) !== -1) {
  508. return OS_TYPE_OPTION_MAP.Windows.value
  509. }
  510. return osDistribution
  511. },
  512. genImageName (img) {
  513. let name = ''
  514. if (img.properties && img.properties.os_distribution) {
  515. name = img.properties.os_distribution
  516. if (img.properties.os_version) {
  517. name += ' ' + img.properties.os_version
  518. }
  519. if (img.properties.os_codename) {
  520. name += ' ' + img.properties.os_codename
  521. }
  522. name += ' (' + img.name + ')'
  523. } else {
  524. name = img.name
  525. }
  526. return name
  527. },
  528. getImageType (img) {
  529. let imageType = null
  530. const properties = this.getProperties(img)
  531. if (properties) {
  532. if (properties.os_distribution) {
  533. imageType = this.getOsDistribution(properties.os_distribution)
  534. } else {
  535. imageType = properties.os_type
  536. }
  537. }
  538. return imageType
  539. },
  540. getImageCached (img) {
  541. return this.cacheimageIds.includes(img.id)
  542. },
  543. getImagesInfo () {
  544. let images = this.images.list
  545. // 如果选择的是公有云镜像类型,则取cache image list
  546. // 其他类型再进行过滤一次
  547. if (this.isPublicImage || this.isPrivateImage || this.isVMware) {
  548. images = this.images.cacheimagesList
  549. if (this.osArch) {
  550. images = images.filter((image) => {
  551. const arch = _.get(image, 'info.properties.os_arch', '')
  552. if (arch === this.osArch || arch.indexOf(this.osArch) >= 0) {
  553. return true
  554. }
  555. return false
  556. })
  557. }
  558. } else {
  559. if (this.vgaPci) {
  560. images = images.filter(item => {
  561. const osType = (item.properties?.os_type || '').toLowerCase()
  562. if (osType && osType.includes('windows')) {
  563. return item.properties?.uefi_support && item.properties?.uefi_support !== 'false'
  564. }
  565. return true
  566. })
  567. }
  568. if (this.imageType !== IMAGES_TYPE_MAP.host.key && this.imageType !== IMAGES_TYPE_MAP.snapshot.key && this.imageType !== IMAGES_TYPE_MAP.backup.key) {
  569. images = images.filter(item => {
  570. let diskFormat = item.disk_format
  571. if (!diskFormat && item.info && item.info.disk_format) {
  572. diskFormat = item.info.disk_format
  573. }
  574. return diskFormat && diskFormat !== 'docker'
  575. })
  576. }
  577. }
  578. let osOpts = [{ label: '全部', key: 'all' }]
  579. const imageOptsMap = {
  580. all: [],
  581. }
  582. let isOther = false
  583. for (let i = 0, len = images.length; i < len; i++) {
  584. const item = images[i]
  585. // 默认将 os 设置为 其他
  586. let osVal = 'other'
  587. let osLabel = ''
  588. // 如果有 os_type 标识则赋值给 osVal
  589. const properties = this.getProperties(item)
  590. if (properties && properties.os_distribution) {
  591. osVal = properties.os_distribution
  592. } else if (properties && properties.os_type) {
  593. osVal = properties.os_type
  594. } else {
  595. isOther = true
  596. }
  597. if (osVal.toLowerCase().includes('windows')) {
  598. osVal = OS_TYPE_OPTION_MAP.Windows.value
  599. }
  600. if (osVal.toLowerCase().includes('linux')) {
  601. if (osVal.toLowerCase().includes('amazon linux')) {
  602. osVal = 'Amazon Linux'
  603. } else if (osVal.includes('RedHat Enterprise Linux')) {
  604. osVal = 'RHEL'
  605. } else if (osVal.toLowerCase().includes('almalinux')) {
  606. osVal = 'AlmaLinux'
  607. } else if (osVal.toLowerCase().includes('rocky')) {
  608. osVal = 'Rocky'
  609. } else {
  610. osVal = 'Linux'
  611. }
  612. }
  613. if (osVal.toLowerCase().includes('ubuntu')) {
  614. osVal = 'Ubuntu'
  615. } else if (osVal.toLowerCase().includes('suse')) {
  616. osVal = 'SUSE'
  617. } else if (osVal.toLowerCase() === 'kylin') {
  618. osLabel = this.$t('compute.os.kylin')
  619. osVal = 'Kylin'
  620. } else if (osVal.toLowerCase() === 'neokylin') {
  621. osLabel = this.$t('compute.os.neokylin')
  622. osVal = 'NeoKylin'
  623. } else if (osVal.toLowerCase().includes('nfs')) {
  624. osLabel = this.$t('compute.os.nfs')
  625. osVal = 'nfs'
  626. }
  627. // const osDistribution = osVal && osVal.toLowerCase()
  628. const osDistribution = osVal
  629. // 如果 Map 中没有此 key,则创建,同时对options进行push
  630. if (!imageOptsMap[osDistribution]) {
  631. imageOptsMap[osDistribution] = []
  632. if (osVal !== 'other') {
  633. osOpts.push({
  634. label: osLabel || osVal,
  635. key: osVal,
  636. })
  637. }
  638. }
  639. const newItem = {
  640. ...item,
  641. feData: {
  642. name: this.genImageName(item),
  643. imageType: this.getImageType(item),
  644. },
  645. }
  646. // !!! 暂时去掉镜像缓存功能,原因是镜像缓存在宿主机01上,但是可能调度到宿主机02上,那么该条镜像就不是已缓存
  647. // 选择的镜像类型为公有云/私有云平台的标准及自定义镜像才去做缓存标识
  648. // if ((this.isPublic && !this.isPublicImage) || (this.isPrivate && !this.isPrivateImage)) {
  649. // newItem.feData.cached = this.getImageCached(item)
  650. // }
  651. imageOptsMap.all.push(newItem)
  652. imageOptsMap[osDistribution].push(newItem)
  653. }
  654. // make other os the last option
  655. if (isOther) {
  656. osOpts.push({ label: this.$t('compute.text_151'), key: 'other' })
  657. }
  658. osOpts = osOpts.filter((item) => {
  659. if (this.osType) {
  660. if (this.osType === OS_TYPE_OPTION_MAP.Windows.value) {
  661. return item.key === OS_TYPE_OPTION_MAP.Windows.value
  662. }
  663. return item.key !== OS_TYPE_OPTION_MAP.Windows.value
  664. }
  665. return true
  666. })
  667. this.imagesInfo = {
  668. osOpts,
  669. imageOptsMap,
  670. }
  671. this.fillImageOpts()
  672. },
  673. /*
  674. * @params {String} osValue
  675. * @params {Object} imageValue { key: <id>, label: <name> }
  676. */
  677. defaultSelect (osValue, imageValue) {
  678. this.imageOpts = []
  679. const { osOpts, imageOptsMap } = this.imagesInfo
  680. if (osOpts && osOpts.length) {
  681. let os = imageOptsMap[osValue] ? osValue : osOpts[0].key
  682. let imageOpts = this.getImageOpts(imageOptsMap[os])
  683. if (!imageOpts || !imageOpts.length) {
  684. this.form.fc.setFieldsValue({ image: initData })
  685. } else {
  686. const isEsxit = imageValue && !!imageOpts.find(val => val.id === imageValue.key)
  687. let image = isEsxit ? imageValue : { key: imageOpts[0].id, label: imageOpts[0].name }
  688. if (!this.edit && !osValue && this.storageImage) { // 采用上次选择的镜像(storage)
  689. const { os: storageOs, image: storageImage } = this.storageImage
  690. const tempImageOpts = imageOptsMap[storageOs] || []
  691. const storageImageObj = tempImageOpts.find(val => val.id === storageImage)
  692. if (R.is(Object, storageImageObj)) {
  693. imageOpts = this.getImageOpts(tempImageOpts)
  694. os = storageOs
  695. image = { key: storageImageObj.id, label: storageImageObj.name }
  696. }
  697. }
  698. this.imageOpts = imageOpts
  699. if (this.imageOptions.find(val => val.id === image.key)) {
  700. this.form.fc.setFieldsValue({ image })
  701. } else {
  702. this.form.fc.setFieldsValue({ image: initData })
  703. }
  704. const currentImage = this.form.fc.getFieldValue(this.decorator.image[0])
  705. if (currentImage && imageValue && currentImage.key === imageValue.key) {
  706. } else {
  707. this.imageChange(image)
  708. }
  709. }
  710. this.form.fc.setFieldsValue({ os })
  711. } else {
  712. this.form.fc.setFieldsValue({ os: undefined, image: initData })
  713. }
  714. },
  715. getImageOpts (imageOpts = []) {
  716. let images = imageOpts.slice()
  717. if (images && images.length > 0) {
  718. images = images.filter((item) => {
  719. const minRam = (item.info && item.info.min_ram) || item.min_ram
  720. if (this.form && this.form.fd) {
  721. const vmem = this.form.fd.vmem || this.form.fd.sku?.memory_size_mb
  722. if (minRam > 0 && R.is(Number, vmem)) {
  723. return minRam <= vmem
  724. }
  725. }
  726. return true
  727. })
  728. }
  729. if (images && images.length > 0) {
  730. images.sort((a, b) => {
  731. const aVersion = a.info && a.info.properties && a.info.properties.os_version
  732. const bVersion = b.info && b.info.properties && b.info.properties.os_version
  733. if (aVersion && bVersion) {
  734. return aVersion.localeCompare(bVersion)
  735. }
  736. return 0
  737. })
  738. }
  739. return images || []
  740. },
  741. fillImageOpts () {
  742. let lastSelectedImageInfo = storage.get('oc_selected_image') || {}
  743. // 默认值
  744. if (this.decorator.os[1].initialValue && this.decorator.image[1].initialValue) {
  745. lastSelectedImageInfo = { ...lastSelectedImageInfo, imageOs: this.decorator.os[1].initialValue, imageId: this.decorator.image[1].initialValue.key }
  746. }
  747. const { imageOs = lastSelectedImageInfo.imageOs, imageId = lastSelectedImageInfo.imageId } = this.$route.query
  748. if (imageOs) {
  749. const os = imageOs.replace(imageOs[0], imageOs[0].toUpperCase())
  750. const images = this.imagesInfo.imageOptsMap[os] || []
  751. if (images?.length > 0) {
  752. this.form.fc.setFieldsValue({ os })
  753. }
  754. let image = images.find((item) => { return item.id === imageId })
  755. this.defaultSelect(os === 'Nfs' ? 'nfs' : os)
  756. if (image) {
  757. image = { key: image.id, label: image.name }
  758. if (this.imageOptions.length === 0) {
  759. this.form.fc.setFieldsValue({ image: initData })
  760. } else {
  761. this.form.fc.setFieldsValue({ image })
  762. }
  763. this.imageChange(image)
  764. }
  765. } else {
  766. this.defaultSelect()
  767. }
  768. },
  769. },
  770. }
  771. </script>