index.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. <template>
  2. <div>
  3. <page-header :title="$t('compute.text_709', [$t('compute.text_100')])" :tabs="cloudEnvOptions" :current-tab.sync="cloudEnv" />
  4. <a-form
  5. class="mt-3"
  6. :form="form.fc"
  7. hideRequiredMark>
  8. <a-form-item :label="$t('compute.text_297', [$t('dictionary.project')])" v-bind="formItemLayout">
  9. <domain-project :fc="form.fc" :decorators="{ project: decorators.project, domain: decorators.domain }" />
  10. </a-form-item>
  11. <area-selects
  12. class="mb-0"
  13. ref="areaSelects"
  14. :wrapperCol="formItemLayout.wrapperCol"
  15. :labelCol="formItemLayout.labelCol"
  16. :names="areaselectsName"
  17. :cloudregionParams="param.region"
  18. :zoneParams="param.zone"
  19. :providerParams="param.provider"
  20. :isRequired="true"
  21. :region.sync="regionList"
  22. filterBrandResource="compute_engine"
  23. :zone.sync="zoneList" />
  24. <a-form-item :label="$t('compute.text_228')" v-bind="formItemLayout">
  25. <a-input v-decorator="decorators.name" :placeholder="$t('validator.resourceCreateName')" />
  26. </a-form-item>
  27. <a-form-item :label="$t('common.description')" v-bind="formItemLayout">
  28. <a-textarea :auto-size="{ minRows: 1, maxRows: 3 }" v-decorator="decorators.description" :placeholder="$t('common_367')" />
  29. </a-form-item>
  30. <a-form-item :label="$t('compute.text_176')" :extra="$t('compute.hypervisor_extra')" v-bind="formItemLayout">
  31. <hypervisor-radio
  32. :decorator="decorators.hypervisor"
  33. :type="form.fi.createType"
  34. :hypervisors="hypervisors"
  35. :disabledHypervisorMap="disabledHypervisorMap"
  36. @change="changeHandle" />
  37. </a-form-item>
  38. <a-form-item :label="$t('compute.text_100')" v-bind="formItemLayout">
  39. <a-row>
  40. <a-col :span="6" class="mr-2">
  41. <a-select v-decorator="decorators.backend" @change="__newStorageChange">
  42. <a-select-option v-for="item in storageOpts" :key="item.value">
  43. <div class="d-flex">
  44. <span class="text-truncate flex-fill mr-2" :title="item.label">{{ item.label }}</span>
  45. </div>
  46. </a-select-option>
  47. </a-select>
  48. </a-col>
  49. <a-col :span="3">
  50. <a-form-item>
  51. <a-tooltip :title="tooltip" placement="top">
  52. <a-input-number :min="minDiskData" :max="maxDiskData" :step="step" v-decorator="decorators.size" /> GB
  53. </a-tooltip>
  54. </a-form-item>
  55. </a-col>
  56. <a-col v-if="isShowStorageSelect" :span="5">
  57. <div class="d-flex">
  58. <disk-storage-select
  59. v-if="showStorage"
  60. style="min-width: 480px; max-width: 500px;"
  61. :decorators="decorators"
  62. :form="form"
  63. :storageParams="storageParams" />
  64. <a-button class="mt-1" type="link" @click="showStorage = !showStorage">{{ showStorage ? $t('compute.text_135') : $t('compute.text_1350') }}</a-button>
  65. </div>
  66. </a-col>
  67. <a-col v-if="isShowIops" :span="5">
  68. <div class="d-flex">
  69. <a-tooltip :title="iopsTooltip" placement="top">
  70. <a-input-number
  71. v-if="showIops"
  72. v-decorator="decorators.iops"
  73. placeholder="IOPS"
  74. :min="iopsLimit.min"
  75. :max="iopsLimit.max"
  76. :precision="0" />
  77. </a-tooltip>
  78. <a-button class="mt-1" type="link" @click="() => showIops = !showIops">{{ showIops ? $t('compute.text_135') : $t('compute.set_iops') }}</a-button>
  79. </div>
  80. </a-col>
  81. <a-col v-if="isShowThroughput" :span="5">
  82. <div class="d-flex">
  83. <a-tooltip title="125 ~ 1000MiB/s" placement="top">
  84. <a-input-number
  85. v-if="showThroughput"
  86. v-decorator="decorators.throughput"
  87. :placeholder="$t('compute.throughput')"
  88. :min="125"
  89. :max="1000"
  90. :precision="0" />
  91. </a-tooltip>
  92. <a-button class="mt-1" type="link" @click="() => showThroughput = !showThroughput">{{ showThroughput ? $t('compute.text_135') : $t('compute.set_throughput') }}</a-button>
  93. </div>
  94. </a-col>
  95. </a-row>
  96. </a-form-item>
  97. <a-form-item v-if="enableEncryption && !isPublic" v-bind="formItemLayout" :label="$t('compute.disk.encryption')" :extra="$t('compute.disk.encryption.extra')">
  98. <encrypt-keys :decorators="decorators.encrypt_keys" />
  99. </a-form-item>
  100. <a-form-item :label="$t('compute.text_1154')" class="mb-0" v-bind="formItemLayout">
  101. <tag
  102. v-decorator="decorators.__meta__" :allowNoValue="false" />
  103. </a-form-item>
  104. <a-collapse :bordered="false" v-if="cloudEnv === 'public' || isHCSO || isHCS">
  105. <a-collapse-panel :header="$t('compute.text_309')" key="1">
  106. <a-form-item :label="$t('compute.text_15')" v-bind="formItemLayout">
  107. <base-select
  108. class="w-50"
  109. v-decorator="decorators.manager_id"
  110. resource="cloudproviders"
  111. :params="cloudproviderParams"
  112. :isDefaultSelect="true"
  113. :showSync="true"
  114. :select-props="{ placeholder: $t('compute.text_149') }"
  115. :resList.sync="cloudproviderData" />
  116. </a-form-item>
  117. </a-collapse-panel>
  118. </a-collapse>
  119. </a-form>
  120. <bottom-bar
  121. :current-cloudregion="currentCloudregion"
  122. :current-cloudzone="currentCloudzone"
  123. :current-storage="storageItem"
  124. :storage-types="storageTypes"
  125. :provider="provider"
  126. :size="form.fd.size" />
  127. </div>
  128. </template>
  129. <script>
  130. import * as R from 'ramda'
  131. import { mapGetters } from 'vuex'
  132. import { MEDIUM_MAP, CUSTOM_STORAGE_TYPES, STORAGE_TYPES } from '@Compute/constants'
  133. import EncryptKeys from '@Compute/sections/encryptkeys'
  134. import DiskStorageSelect from '@Compute/sections/Disk/components/Storage'
  135. import { HYPERVISORS_MAP, CLOUD_ENVS } from '@/constants'
  136. import AreaSelects from '@/sections/AreaSelects'
  137. import DialogMixin from '@/mixins/dialog'
  138. import WindowsMixin from '@/mixins/windows'
  139. import validateForm, { isRequired } from '@/utils/validate'
  140. import i18n from '@/locales'
  141. import DomainProject from '@/sections/DomainProject'
  142. import HypervisorRadio from '@/sections/HypervisorRadio'
  143. import { getCloudEnvOptions } from '@/utils/common/hypervisor'
  144. import Tag from '@/sections/Tag'
  145. import BottomBar from './components/BottomBar'
  146. export default {
  147. name: 'DiskCreate',
  148. components: {
  149. AreaSelects,
  150. DomainProject,
  151. BottomBar,
  152. Tag,
  153. EncryptKeys,
  154. DiskStorageSelect,
  155. HypervisorRadio,
  156. },
  157. mixins: [DialogMixin, WindowsMixin],
  158. data () {
  159. const cloudEnvOptions = getCloudEnvOptions('compute_engine_brands', true)
  160. const queryType = this.$route.query.type
  161. let cloudEnv = queryType === 'idc' ? 'onpremise' : this.$route.query.type
  162. let routerQuery = this.$route.query.type
  163. if (!cloudEnvOptions.find(val => val.key === cloudEnv)) {
  164. cloudEnv = cloudEnvOptions[0].key
  165. routerQuery = cloudEnv === 'onpremise' ? 'idc' : cloudEnv
  166. }
  167. return {
  168. loading: false,
  169. cloudEnvOptions,
  170. cloudEnv,
  171. routerQuery,
  172. form: {
  173. fc: this.$form.createForm(this, {
  174. onValuesChange: (props, values) => {
  175. Object.keys(values).forEach((key) => {
  176. switch (key) {
  177. case 'domain':
  178. this.$set(this.form.fd, key, values[key]?.key)
  179. break
  180. case 'project':
  181. this.$set(this.form.fd, key, values[key]?.key)
  182. break
  183. default:
  184. this.$set(this.form.fd, key, values[key])
  185. }
  186. })
  187. if (values.hasOwnProperty('zone')) {
  188. if (values.zone) {
  189. this.fetchStorageList(values.zone)
  190. } else {
  191. this.storageOpts = []
  192. this.form.fc.resetFields(['backend'])
  193. }
  194. }
  195. if (values.hasOwnProperty('size')) {
  196. this.form.fd.size = values.size
  197. }
  198. },
  199. }),
  200. fd: {
  201. size: 10,
  202. },
  203. fi: {
  204. createType: 'idc',
  205. },
  206. },
  207. decorators: {
  208. domain: [
  209. 'domain',
  210. {
  211. rules: [
  212. { validator: isRequired(), message: i18n.t('rules.domain'), trigger: 'change' },
  213. ],
  214. },
  215. ],
  216. project: [
  217. 'project',
  218. {
  219. rules: [
  220. { validator: isRequired(), message: i18n.t('rules.project'), trigger: 'change' },
  221. ],
  222. },
  223. ],
  224. name: [
  225. 'name',
  226. {
  227. validateFirst: true,
  228. rules: [
  229. { required: true, message: this.$t('compute.text_210') },
  230. { validator: this.$validate('resourceCreateName') },
  231. ],
  232. },
  233. ],
  234. description: ['description'],
  235. backend: [
  236. 'backend',
  237. {
  238. rules: [
  239. { required: true, message: this.$t('compute.text_411') },
  240. ],
  241. },
  242. ],
  243. storage_id: [
  244. 'storage_id',
  245. {
  246. rules: [
  247. { required: true, message: this.$t('compute.text_411') },
  248. ],
  249. },
  250. ],
  251. size: [
  252. 'size',
  253. {
  254. initialValue: 10,
  255. rules: [
  256. { required: true, type: 'number', message: this.$t('compute.disk.size.required_message') },
  257. ],
  258. },
  259. ],
  260. manager_id: [
  261. 'manager_id',
  262. ],
  263. __meta__: [
  264. '__meta__',
  265. {
  266. rules: [
  267. { validator: validateForm('tagName') },
  268. ],
  269. },
  270. ],
  271. encrypt_keys: {
  272. encryptEnable: [
  273. 'encryptEnable',
  274. {
  275. initialValue: '',
  276. },
  277. ],
  278. encrypt_key_alg: [
  279. 'encrypt_key_alg',
  280. {
  281. initialValue: '',
  282. },
  283. ],
  284. encrypt_key_id: [
  285. 'encrypt_key_id',
  286. ],
  287. },
  288. host: ['host'],
  289. server: ['server'],
  290. storage: [
  291. 'storage',
  292. {
  293. rules: [{
  294. required: true,
  295. message: i18n.t('compute.text_1351'),
  296. }],
  297. },
  298. ],
  299. iops: [
  300. 'iops',
  301. {
  302. rules: [{
  303. required: true,
  304. message: i18n.t('compute.iops_input_tip'),
  305. }],
  306. },
  307. ],
  308. throughput: [
  309. 'throughput',
  310. {
  311. rules: [{
  312. required: true,
  313. message: i18n.t('compute.throughput_input_tip'),
  314. }],
  315. },
  316. ],
  317. hypervisor: [
  318. 'hypervisor',
  319. {
  320. rules: [
  321. { required: true, message: i18n.t('compute.text_215') },
  322. ],
  323. },
  324. ],
  325. },
  326. formItemLayout: {
  327. wrapperCol: {
  328. md: { span: 18 },
  329. xl: { span: 20 },
  330. xxl: { span: 22 },
  331. },
  332. labelCol: {
  333. md: { span: 6 },
  334. xl: { span: 4 },
  335. xxl: { span: 2 },
  336. },
  337. },
  338. storageOpts: [],
  339. storageItem: {},
  340. storageTypes: [],
  341. maxDiskData: 2048,
  342. minDiskData: 1,
  343. step: 10,
  344. cloudproviderData: [],
  345. regionList: {},
  346. zoneList: {},
  347. instance_capabilities: [],
  348. showStorage: false,
  349. showIops: false,
  350. showThroughput: false,
  351. hypervisors: [],
  352. capbilityData: {},
  353. disabledHypervisorMap: {
  354. esxi: this.$t('compute.hypervisor_disabled_tips', ['VMware']),
  355. pod: this.$t('compute.hypervisor_disabled_tips', [this.$t('compute.vminstance-container')]),
  356. },
  357. }
  358. },
  359. computed: {
  360. enableEncryption () {
  361. return this.$appConfig.isPrivate && !this.$store.getters.isSysCE
  362. },
  363. tooltip () {
  364. return this.$t('compute.text_137', [this.minDiskData, this.maxDiskData])
  365. },
  366. ...mapGetters(['isAdminMode', 'scope', 'userInfo']),
  367. currentCloudregion () {
  368. return this.regionList[this.form.fd.cloudregion]
  369. },
  370. currentCloudzone () {
  371. return this.zoneList[this.form.fd.zone]
  372. },
  373. isHCSO () {
  374. if (this.currentCloudregion) {
  375. return this.currentCloudregion.provider === HYPERVISORS_MAP.hcso.provider
  376. }
  377. return false
  378. },
  379. isHCS () {
  380. if (this.currentCloudregion) {
  381. return this.currentCloudregion.provider === HYPERVISORS_MAP.hcs.provider
  382. }
  383. return false
  384. },
  385. isZettaKit () {
  386. if (this.currentCloudregion) {
  387. return this.currentCloudregion.provider === HYPERVISORS_MAP.zettakit.provider
  388. }
  389. return false
  390. },
  391. isUIS () {
  392. if (this.currentCloudregion) {
  393. return this.currentCloudregion.provider === HYPERVISORS_MAP.uis.provider
  394. }
  395. return false
  396. },
  397. isAws () {
  398. return this.currentCloudregion?.provider === HYPERVISORS_MAP.aws.provider
  399. },
  400. isKVM () {
  401. return true
  402. },
  403. provider () { // 向外提供的,通过 refs 获取
  404. if (this.currentCloudregion && this.currentCloudregion.provider) {
  405. return this.currentCloudregion.provider.toLowerCase()
  406. }
  407. return ['kvm', 'esxi'] // 没有 provider 肯定是 kvm 或者 esxi 的cloudregion
  408. },
  409. diskType () {
  410. return this.cloudEnv
  411. },
  412. storageLabel () {
  413. if (['idc', 'private'].includes(this.diskType)) {
  414. return this.$t('compute.text_380')
  415. }
  416. return this.$t('compute.text_396')
  417. },
  418. param () {
  419. const project_domain = { project_domain: this.form.fd.domain || this.userInfo.projectDomainId || this.userInfo.domain.id }
  420. if (this.diskType === 'private') {
  421. const params = {
  422. zone: {
  423. usable: true,
  424. show_emulated: true,
  425. order_by: 'created_at',
  426. order: 'asc',
  427. ...project_domain,
  428. },
  429. region: {
  430. usable: true,
  431. cloud_env: 'private',
  432. show_emulated: true,
  433. filter: `provider.notin(${HYPERVISORS_MAP.nutanix.provider}, ${HYPERVISORS_MAP.proxmox.provider})`,
  434. ...project_domain,
  435. },
  436. provider: {},
  437. }
  438. return params
  439. } else if (this.diskType === 'public') {
  440. return {
  441. zone: {
  442. usable: true,
  443. show_emulated: true,
  444. order_by: 'created_at',
  445. order: 'asc',
  446. ...project_domain,
  447. },
  448. region: {
  449. usable: true,
  450. cloud_env: 'public',
  451. ...project_domain,
  452. },
  453. provider: {
  454. cloud_env: 'public',
  455. ...project_domain,
  456. },
  457. }
  458. }
  459. if (this.isAdminMode) {
  460. return {
  461. zone: {
  462. ...project_domain,
  463. },
  464. region: {
  465. usable: true,
  466. cloud_env: 'onpremise',
  467. ...project_domain,
  468. },
  469. }
  470. }
  471. return {
  472. zone: {
  473. ...project_domain,
  474. },
  475. region: {
  476. usable: true,
  477. cloud_env: 'onpremise',
  478. scope: this.scope,
  479. },
  480. }
  481. },
  482. areaselectsName () {
  483. if (this.diskType === 'private' || this.diskType === 'onpremise') {
  484. return ['cloudregion', 'zone']
  485. }
  486. return ['provider', 'cloudregion', 'zone']
  487. },
  488. cloudproviderParams () {
  489. const { cloudregion, domain: project_domain, zone } = this.form.fd
  490. const params = {
  491. limit: 0,
  492. enabled: true,
  493. read_only: false,
  494. 'filter.0': 'status.equals("connected")',
  495. 'filter.1': 'health_status.equals("normal")',
  496. cloudregion,
  497. project_domain,
  498. zone,
  499. }
  500. return params
  501. },
  502. project_domain () {
  503. return this.form.fd.domain ? this.form.fd.domain : this.userInfo.projectDomainId
  504. },
  505. instanceCapabilitieStorage () {
  506. if (R.isEmpty(this.instance_capabilities)) return {}
  507. return this.instance_capabilities?.[0]?.storages
  508. },
  509. instanceCapabilitieDataDisk () {
  510. if (R.isEmpty(this.instanceCapabilitieStorage)) return []
  511. return this.instanceCapabilitieStorage?.data_disk
  512. },
  513. isIDC () {
  514. return this.cloudEnv === 'onpremise'
  515. },
  516. isShowStorageSelect () {
  517. return this.isIDC || this.isZettaKit || this.isUIS
  518. },
  519. isShowIops () {
  520. return this.isAws && (this.storageItem?.value?.startsWith('gp3') || this.storageItem?.value?.startsWith('io1'))
  521. },
  522. isShowThroughput () {
  523. return this.isAws && this.storageItem?.value?.startsWith('gp3')
  524. },
  525. iopsTooltip () {
  526. if (this.iopsLimit.min && this.iopsLimit.max) {
  527. return `${this.iopsLimit.min} ~ ${this.iopsLimit.max}`
  528. }
  529. return ''
  530. },
  531. isLocalDisk () {
  532. if (this.storageItem && this.storageItem.value && this.storageItem.value.toLowerCase().startsWith('local_')) {
  533. return true
  534. }
  535. return false
  536. },
  537. isPublic () {
  538. return this.cloudEnv === CLOUD_ENVS.public
  539. },
  540. storageParams () {
  541. const params = {
  542. project_domain: this.form.fd.domain,
  543. zone: this.cloudproviderParams?.zone,
  544. }
  545. const storageVal = this.storageItem.value?.toLowerCase()
  546. if (storageVal) {
  547. // 磁盘区分介质
  548. const storage_type = storageVal?.split('__')[0]
  549. const medium_type = storageVal?.split('__')[1]
  550. params.filter = [
  551. `storage_type.contains("${storage_type}")`,
  552. `medium_type.contains("${medium_type}")`,
  553. ]
  554. }
  555. return params
  556. },
  557. dataStorageTypes () {
  558. return this.capbilityData.data_storage_types2
  559. },
  560. dataStorageProviderTypes () {
  561. return this.dataStorageTypes[(this.currentCloudregion?.provider || '').toLowerCase()]
  562. },
  563. iopsLimit () {
  564. let ret = { min: 0 }
  565. if (this.isAws) {
  566. // gp3 iops 不能超过磁盘500倍
  567. if ((this.storageItem?.value || '').startsWith('gp3')) {
  568. ret = { min: 3000, max: 16000 }
  569. const { size } = this.form.fd
  570. if (size) {
  571. ret.max = size * 500 < ret.max ? size * 500 : ret.max
  572. }
  573. }
  574. // io1 iops 不能超过磁盘50倍
  575. if ((this.storageItem?.value || '').startsWith('io1')) {
  576. ret = { min: 100, max: 64000 }
  577. const { size } = this.form.fd
  578. if (size) {
  579. ret.max = size * 50 < ret.max ? size * 50 : ret.max
  580. }
  581. }
  582. }
  583. return ret
  584. },
  585. },
  586. watch: {
  587. cloudEnv (val) {
  588. this.$nextTick(() => {
  589. const { query, path } = this.$router.history.current
  590. const newQuery = JSON.parse(JSON.stringify(query))
  591. newQuery.type = val === 'onpremise' ? 'idc' : val
  592. this.form.fi.createType = newQuery.type
  593. this.$router.push({ path, query: newQuery })
  594. })
  595. this.$refs.areaSelects.fetchs(['provider', 'cloudregion', 'zone'])
  596. this.storageItem = {}
  597. },
  598. 'form.fd.domain' (newValue, oldValue) {
  599. if (newValue !== oldValue) {
  600. this.$refs.areaSelects.fetchs(this.areaselectsName)
  601. }
  602. },
  603. },
  604. provide () {
  605. return {
  606. form: this.form,
  607. }
  608. },
  609. methods: {
  610. fetchStorageList (zoneId) {
  611. const params = { show_emulated: true }
  612. if (this.isAdminMode) {
  613. params.project_domain = this.project_domain
  614. } else {
  615. params.scope = this.scope
  616. }
  617. this.storageOpts = []
  618. new this.$Manager('capability').list({ ctx: [['zones', zoneId]], params })
  619. .then(({ data }) => {
  620. try {
  621. const provider = Array.isArray(this.provider) ? this.provider[0] : this.provider
  622. this.capbilityData = data
  623. this.instance_capabilities = data.instance_capabilities
  624. const hypervisors = Object.keys(this.dataStorageTypes || {})
  625. let data_storage_types = []
  626. this.hypervisors = hypervisors
  627. if (hypervisors && hypervisors.length > 0) {
  628. const supportHypervisors = hypervisors.filter(item => ![HYPERVISORS_MAP.esxi.key, HYPERVISORS_MAP.pod.key].includes(item))
  629. const firstHypervisor = supportHypervisors[0]
  630. this.$nextTick(() => {
  631. this.form.fc.setFieldsValue({
  632. hypervisor: firstHypervisor,
  633. })
  634. })
  635. data_storage_types = this.dataStorageTypes[firstHypervisor]
  636. }
  637. this.getStorageOpts(data_storage_types, provider)
  638. } catch (error) {
  639. throw new Error(this.$t('common_589') + error)
  640. }
  641. })
  642. },
  643. getStorageOpts (data_storage_types, provider) {
  644. this.storageOpts = data_storage_types.map((item) => {
  645. const types = item.split('/')
  646. const backend = types[0]
  647. const medium = types[1]
  648. let opt = STORAGE_TYPES[provider][backend]
  649. if (!this.isPublic && opt) {
  650. opt = {
  651. ...opt,
  652. label: `${opt.label}(${MEDIUM_MAP[medium]})`,
  653. }
  654. }
  655. const getLabel = (backend) => { return backend.includes('rbd') ? `Ceph(${MEDIUM_MAP[medium]})` : `${backend}(${MEDIUM_MAP[medium]})` }
  656. const backends = data_storage_types.filter(v => v.includes(backend))
  657. return {
  658. value: `${backend}__${medium}`,
  659. label: opt ? opt.label : getLabel(backend),
  660. medium: MEDIUM_MAP[medium] || medium,
  661. multiple: backends.length > 1,
  662. }
  663. })
  664. if (this.diskType === 'private') {
  665. this.storageOpts = this.storageOpts.filter((item) => {
  666. return !item.value.includes('nova')
  667. })
  668. } else if (this.diskType === 'public') {
  669. // 公有云隐藏带local关键字的硬盘类型
  670. this.storageOpts = this.storageOpts.filter(({ value }) => {
  671. if (value.includes('local')) return false
  672. return true
  673. })
  674. }
  675. if (provider === 'qcloud' || provider === 'ucloud') {
  676. this.storageOpts = this.storageOpts.filter((item) => {
  677. return !(item.value && item.value.toLowerCase().startsWith('local_'))
  678. })
  679. }
  680. this.form.fc.setFieldsValue({ backend: '' })
  681. if (this.storageOpts.length > 0) {
  682. this.form.fc.setFieldsValue({ backend: this.storageOpts[0].value })
  683. this.__newStorageChange(this.storageOpts[0].value)
  684. }
  685. },
  686. _translateStorageOps (data) {
  687. const findStorageProvider = optItem => {
  688. const storageName = optItem.name ? `(${optItem.name})` : ''
  689. let storageType = optItem.storage_type
  690. let storageProvider = {}
  691. if (R.is(Array, this.provider)) {
  692. this.provider.forEach(hypervisor => { // 将 kvm 和 esxi 的存储类型合一
  693. Object.assign(storageProvider, STORAGE_TYPES[hypervisor])
  694. })
  695. } else {
  696. storageProvider = STORAGE_TYPES[this.provider]
  697. }
  698. if (storageProvider[storageType.toLowerCase()]) {
  699. storageType = storageType.toLowerCase()
  700. } else if (storageProvider[storageType.toUpperCase()]) {
  701. storageType = storageType.toUpperCase()
  702. }
  703. return {
  704. storageName,
  705. storageType,
  706. storageProvider,
  707. }
  708. }
  709. // 过滤掉不支持创建的云硬盘的存储类型
  710. const conCreateCloud = data.filter(v => {
  711. const { storageProvider, storageType } = findStorageProvider(v)
  712. if (storageType === 'nova') return false
  713. if (storageType && storageProvider && !R.isEmpty(storageProvider) && storageProvider[storageType]) {
  714. return !storageProvider[storageType].unCreateCloud
  715. }
  716. // 说明支持自定义
  717. if (CUSTOM_STORAGE_TYPES.includes(this.provider)) {
  718. return true
  719. }
  720. return false
  721. })
  722. return conCreateCloud.map(v => {
  723. const { storageName, storageType, storageProvider } = findStorageProvider(v)
  724. let label = v.name
  725. try {
  726. if (storageProvider[storageType]) {
  727. label = storageProvider[storageType].label
  728. } else {
  729. label = storageProvider[storageType.toLowerCase()].label
  730. }
  731. } catch (error) {
  732. console.warn(this.$t('compute.text_413', [this.provider, storageType]))
  733. }
  734. return {
  735. label: `${label}${storageName}`,
  736. value: v.id,
  737. ...v,
  738. }
  739. })
  740. },
  741. __newStorageChange (val) {
  742. const item = this.storageOpts.find(v => v.value === val)
  743. this.storageItem = item
  744. try {
  745. this.minDiskData = this.getDataDiskMin(val)
  746. this.maxDiskData = this.getDataDiskMax(val)
  747. this.step = this.getDataDiskStep(val)
  748. } catch (error) {
  749. console.warn(this.$t('compute.text_413', [STORAGE_TYPES[this.provider], item.storage_type]))
  750. }
  751. this.form.fc.setFieldsValue({ size: 10 })
  752. const size = this.form.fc.getFieldValue('size')
  753. if (size > this.maxDiskData) { // 如果当前容量大于当前集群的最大值,那么取最大值
  754. this.form.fc.setFieldsValue({ size: this.maxDiskData })
  755. } else if (size < this.minDiskData) { // 如果当前容量小于当前集群的最大值,那么取最小值
  756. this.form.fc.setFieldsValue({ size: this.minDiskData })
  757. }
  758. },
  759. getDataDiskMin (val) {
  760. const curDisk = this.instanceCapabilitieDataDisk.find(v => val.startsWith(v.storage_type))
  761. if (curDisk) {
  762. return curDisk.min_size_gb
  763. }
  764. return STORAGE_TYPES[this.provider][val].min
  765. },
  766. getDataDiskMax (val) {
  767. const curDisk = this.instanceCapabilitieDataDisk.find(v => val.startsWith(v.storage_type))
  768. if (curDisk) {
  769. return curDisk.max_size_gb
  770. }
  771. return STORAGE_TYPES[this.provider][val].max
  772. },
  773. getDataDiskStep (val) {
  774. const curDisk = this.instanceCapabilitieDataDisk.find(v => val.startsWith(v.storage_type))
  775. if (curDisk) {
  776. return curDisk.step_size_gb
  777. }
  778. return 10
  779. },
  780. changeHandle (v) {
  781. const data_storage_types = this.dataStorageTypes[v]
  782. const provider = Array.isArray(this.provider) ? this.provider[0] : this.provider
  783. this.getStorageOpts(data_storage_types, provider)
  784. },
  785. },
  786. }
  787. </script>