createServer.js 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420
  1. import * as R from 'ramda'
  2. import _ from 'lodash'
  3. import ipaddr from 'ipaddr.js'
  4. import {
  5. NETWORK_OPTIONS_MAP,
  6. SERVER_TYPE,
  7. EIP_TYPES_MAP,
  8. EIP_CHARGE_TYPES_MAP,
  9. BILL_TYPES_MAP,
  10. SCHED_POLICY_OPTIONS_MAP,
  11. STORAGE_AUTO,
  12. SECGROUP_OPTIONS_MAP,
  13. FORECAST_FILTERS_MAP,
  14. RESOURCE_TYPES_MAP,
  15. } from '@Compute/constants'
  16. import { IMAGES_TYPE_MAP, HOST_CPU_ARCHS } from '@/constants/compute'
  17. import { HYPERVISORS_MAP } from '@/constants'
  18. import validateForm, { isRequired, isWithinRange } from '@/utils/validate'
  19. import store from '@/store'
  20. import i18n from '@/locales'
  21. import { removeHttp } from '@/utils/url'
  22. import { diskSupportTypeMedium, getOriginDiskKey } from '@/utils/common/hypervisor'
  23. export function getIpv6Start (ipv6) {
  24. try {
  25. const IPv6 = ipaddr.IPv6.parse(ipv6)
  26. return IPv6.toNormalizedString().split(':').slice(0, 4).join(':') + ':'
  27. } catch (err) {
  28. console.error('IPv6 address is error')
  29. return ''
  30. }
  31. }
  32. export function ipv6ToHex (ipv6) {
  33. return ipv6.parts.map(part => part.toString(16).padStart(4, '0')).join('')
  34. }
  35. export function checkIpV6 (i, networkData) {
  36. return (rule, value, cb) => {
  37. const ipv6First = getIpv6Start(networkData.guest_ip6_start)
  38. try {
  39. const ipv6 = ipv6First + value
  40. const ipAddr = ipaddr.IPv6.parse(ipv6)
  41. const subnet1Addr = ipaddr.IPv6.parse(networkData.guest_ip6_start)
  42. const subnet2Addr = ipaddr.IPv6.parse(networkData.guest_ip6_end)
  43. if (ipAddr.kind() !== 'ipv6') {
  44. cb(new Error(i18n.t('compute.error_ipv6')))
  45. }
  46. const target = ipv6ToHex(ipAddr)
  47. const start = ipv6ToHex(subnet1Addr)
  48. const end = ipv6ToHex(subnet2Addr)
  49. // 检查IP是否在两个子网之间
  50. if (!((target >= start && target <= end))) {
  51. cb(new Error(i18n.t('compute.ipv6_within_range')))
  52. }
  53. cb()
  54. } catch (err) {
  55. cb(new Error(i18n.t('compute.error_ipv6')))
  56. }
  57. }
  58. }
  59. export function checkIpInSegment (i, networkData) {
  60. return (rule, value, cb) => {
  61. const isIn = isWithinRange(value, networkData.guest_ip_start, networkData.guest_ip_end)
  62. if (isIn) {
  63. cb()
  64. } else {
  65. cb(new Error(i18n.t('compute.text_205')))
  66. }
  67. }
  68. }
  69. export function diskValidator (rule, value, callback) {
  70. if (R.isNil(value) || R.isEmpty(value)) {
  71. return callback(new Error(i18n.t('compute.text_206')))
  72. }
  73. if (!value.startsWith('/')) {
  74. return callback(new Error(i18n.t('compute.text_207')))
  75. }
  76. if (value === '/') {
  77. return callback(new Error(i18n.t('compute.text_208')))
  78. }
  79. callback()
  80. }
  81. const validateValidPath = (rule, value, callback) => {
  82. if (value.startsWith('/')) {
  83. if (value === '/') {
  84. callback(new Error(i18n.t('compute.repo.mount_point.check_content')))
  85. } else {
  86. callback()
  87. }
  88. } else {
  89. callback(new Error(i18n.t('compute.repo.mount_point.check_start')))
  90. }
  91. }
  92. const validateMountNames = (rule, value, callback) => {
  93. if (value === undefined) {
  94. callback(new Error(i18n.t('common.tips.select', [i18n.t('compute.repo.storage_statement')])))
  95. }
  96. callback()
  97. }
  98. export const createVmDecorators = () => {
  99. const imageTypeInitValue = IMAGES_TYPE_MAP.standard.key
  100. return {
  101. domain: [
  102. 'domain',
  103. {
  104. rules: [
  105. { validator: isRequired(), message: i18n.t('rules.domain'), trigger: 'change' },
  106. ],
  107. },
  108. ],
  109. project: [
  110. 'project',
  111. {
  112. rules: [
  113. { validator: isRequired(), message: i18n.t('rules.project'), trigger: 'change' },
  114. ],
  115. },
  116. ],
  117. name: [
  118. 'name',
  119. {
  120. initialValue: '',
  121. validateTrigger: 'blur',
  122. validateFirst: true,
  123. rules: [
  124. { required: true, message: i18n.t('compute.text_210') },
  125. ],
  126. },
  127. ],
  128. description: [
  129. 'description',
  130. {
  131. initialValue: '',
  132. },
  133. ],
  134. count: [
  135. 'count',
  136. {
  137. initialValue: 1,
  138. rules: [
  139. { required: true, message: i18n.t('compute.text_211') },
  140. ],
  141. },
  142. ],
  143. cloudregionZone: {
  144. cloudregion: [
  145. 'cloudregion',
  146. {
  147. initialValue: { key: '', label: '' },
  148. rules: [
  149. { validator: isRequired(), message: i18n.t('compute.text_212') },
  150. ],
  151. },
  152. ],
  153. zone: [
  154. 'zone',
  155. {
  156. initialValue: { key: '', label: '' },
  157. rules: [
  158. { validator: isRequired(), message: i18n.t('compute.text_213') },
  159. ],
  160. },
  161. ],
  162. },
  163. imageOS: {
  164. prefer_manager: [
  165. 'prefer_manager',
  166. {
  167. rules: [
  168. { required: true, message: i18n.t('compute.text_149') },
  169. ],
  170. },
  171. ],
  172. os: [
  173. 'os',
  174. {
  175. initialValue: '',
  176. rules: [
  177. { required: true, message: i18n.t('compute.text_153') },
  178. ],
  179. },
  180. ],
  181. image: [
  182. 'image',
  183. {
  184. initialValue: { key: '', label: '' },
  185. rules: [
  186. { validator: isRequired(), message: i18n.t('compute.text_214') },
  187. ],
  188. },
  189. ],
  190. imageType: [
  191. 'imageType',
  192. {
  193. initialValue: imageTypeInitValue,
  194. },
  195. ],
  196. },
  197. cloudprovider: [
  198. 'cloudprovider',
  199. {
  200. rules: [
  201. { required: true, message: i18n.t('compute.text_149') },
  202. ],
  203. },
  204. ],
  205. pci: {
  206. pciEnable: [
  207. 'pciEnable',
  208. {
  209. valuePropName: 'checked',
  210. initialValue: false,
  211. },
  212. ],
  213. pciDevType: i => [
  214. `pciDevType[${i}]`,
  215. ],
  216. pciModel: i => [
  217. `pciModel[${i}]`,
  218. {
  219. rules: [
  220. { required: true, message: i18n.t('compute.text_147') },
  221. ],
  222. },
  223. ],
  224. pciCount: i => [
  225. `pciCount[${i}]`,
  226. {
  227. initialValue: 1,
  228. },
  229. ],
  230. },
  231. vcpu: [
  232. 'vcpu',
  233. {
  234. initialValue: 2,
  235. },
  236. ],
  237. vmem: [
  238. 'vmem',
  239. {
  240. initialValue: 2048,
  241. },
  242. ],
  243. sku: [
  244. 'sku',
  245. {
  246. rules: [
  247. { validator: isRequired(true, 'id'), message: i18n.t('compute.text_216') },
  248. ],
  249. },
  250. ],
  251. dataDisk: {
  252. type: i => [
  253. `dataDiskTypes[${i}]`,
  254. {
  255. rules: [
  256. { validator: isRequired(), message: i18n.t('compute.text_121') },
  257. ],
  258. },
  259. ],
  260. size: i => [
  261. `dataDiskSizes[${i}]`,
  262. {
  263. rules: [
  264. { required: true, message: i18n.t('compute.text_122') },
  265. ],
  266. },
  267. ],
  268. schedtag: i => [
  269. `dataDiskSchedtags[${i}]`,
  270. {
  271. validateTrigger: ['change', 'blur'],
  272. rules: [{
  273. required: true,
  274. message: i18n.t('compute.text_123'),
  275. }],
  276. },
  277. ],
  278. policy: i => [
  279. `dataDiskPolicys[${i}]`,
  280. {
  281. initialValue: '',
  282. validateTrigger: ['blur', 'change'],
  283. rules: [{
  284. required: true,
  285. message: i18n.t('compute.text_123'),
  286. }],
  287. },
  288. ],
  289. snapshot: i => [
  290. `dataDiskSnapshots[${i}]`,
  291. {
  292. validateTrigger: ['blur', 'change'],
  293. rules: [{
  294. required: true,
  295. message: i18n.t('compute.text_124'),
  296. }],
  297. },
  298. ],
  299. filetype: i => [
  300. `dataDiskFiletypes[${i}]`,
  301. {
  302. validateTrigger: ['blur', 'change'],
  303. rules: [{
  304. required: true,
  305. message: i18n.t('compute.text_125'),
  306. }],
  307. },
  308. ],
  309. mountPath: i => [
  310. `dataDiskMountPaths[${i}]`,
  311. {
  312. validateTrigger: ['blur', 'change'],
  313. rules: [{
  314. required: true,
  315. message: i18n.t('compute.text_126'),
  316. }, {
  317. validator: diskValidator,
  318. }],
  319. },
  320. ],
  321. storage: i => [
  322. `dataDiskStorages[${i}]`,
  323. {
  324. rules: [{
  325. required: true,
  326. message: i18n.t('compute.text_1351'),
  327. }],
  328. },
  329. ],
  330. iops: i => [
  331. `dataDiskIops[${i}]`,
  332. {
  333. rules: [{
  334. required: true,
  335. message: i18n.t('compute.iops_input_tip'),
  336. }],
  337. },
  338. ],
  339. throughput: i => [
  340. `dataDiskThroughputs[${i}]`,
  341. {
  342. rules: [{
  343. required: true,
  344. message: i18n.t('compute.throughput_input_tip'),
  345. }],
  346. },
  347. ],
  348. preallocation: i => [
  349. `dataDiskPreallocation[${i}]`,
  350. ],
  351. },
  352. network: {
  353. networkType: [
  354. 'networkType',
  355. {
  356. initialValue: NETWORK_OPTIONS_MAP.default.key,
  357. },
  358. ],
  359. networkConfig: {
  360. vpcs: i => [
  361. `vpcs[${i}]`,
  362. {
  363. validateTrigger: ['change', 'blur'],
  364. rules: [{
  365. required: true,
  366. message: i18n.t('compute.text_194'),
  367. }],
  368. },
  369. ],
  370. networks: i => [
  371. `networks[${i}]`,
  372. {
  373. validateTrigger: ['change', 'blur'],
  374. rules: [{
  375. required: true,
  376. message: i18n.t('compute.text_217'),
  377. }],
  378. },
  379. ],
  380. ips: (i, networkData) => [
  381. `networkIps[${i}]`,
  382. {
  383. validateFirst: true,
  384. validateTrigger: ['blur', 'change'],
  385. rules: [
  386. {
  387. required: true,
  388. message: i18n.t('compute.text_218'),
  389. },
  390. {
  391. validator: validateForm('IPv4'),
  392. },
  393. {
  394. validator: checkIpInSegment(i, networkData),
  395. },
  396. ],
  397. },
  398. ],
  399. macs: (i, networkData) => [
  400. `networkMacs[${i}]`,
  401. {
  402. validateFirst: true,
  403. validateTrigger: ['blur', 'change'],
  404. rules: [
  405. {
  406. required: true,
  407. message: i18n.t('compute.text_806'),
  408. },
  409. {
  410. validator: validateForm('mac'),
  411. },
  412. ],
  413. },
  414. ],
  415. ips6: (i, networkData) => [
  416. `networkIpsAddress6[${i}]`,
  417. {
  418. validateFirst: true,
  419. validateTrigger: ['blur', 'change'],
  420. rules: [
  421. {
  422. required: true,
  423. message: i18n.t('compute.complete_ipv6_address'),
  424. },
  425. {
  426. validator: checkIpV6(i, networkData),
  427. },
  428. ],
  429. },
  430. ],
  431. ipv6_mode: (i, networkData) => [
  432. `networkIPv6Modes[${i}]`,
  433. {
  434. validateTrigger: ['change', 'blur'],
  435. },
  436. ],
  437. ipv6s: (i, networkData) => [
  438. `networkIPv6s[${i}]`,
  439. {
  440. validateFirst: true,
  441. validateTrigger: ['blur', 'change'],
  442. },
  443. ],
  444. devices: i => [
  445. `networkDevices[${i}]`,
  446. {
  447. validateTrigger: ['change', 'blur'],
  448. rules: [{
  449. required: true,
  450. message: i18n.t('compute.sriov_device_tips'),
  451. }],
  452. },
  453. ],
  454. },
  455. networkSchedtag: {
  456. schedtags: i => [
  457. `networkSchedtags[${i}]`,
  458. {
  459. validateTrigger: ['change', 'blur'],
  460. rules: [{
  461. required: true,
  462. message: i18n.t('compute.text_123'),
  463. }],
  464. },
  465. ],
  466. policys: (i, networkData) => [
  467. `networkPolicys[${i}]`,
  468. {
  469. validateTrigger: ['blur', 'change'],
  470. rules: [{
  471. required: true,
  472. message: i18n.t('common_256'),
  473. }],
  474. },
  475. ],
  476. devices: i => [
  477. `networkDevices[${i}]`,
  478. {
  479. validateTrigger: ['change', 'blur'],
  480. rules: [{
  481. required: true,
  482. message: i18n.t('compute.sriov_device_tips'),
  483. }],
  484. },
  485. ],
  486. },
  487. },
  488. schedPolicy: {
  489. schedPolicyType: [
  490. 'schedPolicyType',
  491. {
  492. initialValue: 'default',
  493. },
  494. ],
  495. schedPolicyHost: [
  496. 'schedPolicyHost',
  497. {
  498. rules: [
  499. { required: true, message: i18n.t('compute.text_219') },
  500. ],
  501. },
  502. ],
  503. policySchedtag: {
  504. schedtags: i => [
  505. `policySchedtagSchedtags[${i}]`,
  506. {
  507. validateTrigger: ['change', 'blur'],
  508. rules: [{
  509. required: true,
  510. message: i18n.t('compute.text_123'),
  511. }],
  512. },
  513. ],
  514. policys: (i, networkData) => [
  515. `policySchedtagPolicys[${i}]`,
  516. {
  517. validateTrigger: ['blur', 'change'],
  518. rules: [{
  519. required: true,
  520. message: i18n.t('common_256'),
  521. }],
  522. },
  523. ],
  524. },
  525. },
  526. duration: {
  527. durationStandard: [
  528. 'durationStandard',
  529. {
  530. initialValue: 'none',
  531. },
  532. ],
  533. duration: [
  534. 'duration',
  535. {
  536. initialValue: '1h',
  537. },
  538. ],
  539. },
  540. groups: {
  541. groupsEnable: [
  542. 'groupsEnable',
  543. {
  544. valuePropName: 'checked',
  545. initialValue: false,
  546. },
  547. ],
  548. groups: [
  549. 'groups',
  550. ],
  551. },
  552. bill: {
  553. billType: [
  554. 'billType',
  555. {
  556. initialValue: 'quantity',
  557. },
  558. ],
  559. duration: [
  560. 'duration',
  561. {
  562. initialValue: '1M',
  563. },
  564. ],
  565. autoRenew: [
  566. 'autoRenew',
  567. {
  568. valuePropName: 'checked',
  569. },
  570. ],
  571. },
  572. resourceType: [
  573. 'resourceType',
  574. {
  575. preserve: true,
  576. initialValue: RESOURCE_TYPES_MAP.shared.key,
  577. },
  578. ],
  579. eip: {
  580. type: [
  581. 'eip_type',
  582. {
  583. initialValue: 'none',
  584. },
  585. ],
  586. charge_type: [
  587. 'eip_charge_type',
  588. ],
  589. bgp_type: [
  590. 'eip_bgp_type',
  591. {
  592. initialValue: '',
  593. },
  594. ],
  595. bandwidth: [
  596. 'eip_bw',
  597. {
  598. initialValue: 30,
  599. },
  600. ],
  601. eip: [
  602. 'eip',
  603. {
  604. rules: [
  605. { required: true, message: i18n.t('compute.text_145') },
  606. ],
  607. },
  608. ],
  609. },
  610. secgroup: {
  611. type: [
  612. 'secgroup_type',
  613. {
  614. initialValue: 'default',
  615. },
  616. ],
  617. secgroup: [
  618. 'secgroup',
  619. {
  620. validateFirst: true,
  621. rules: [
  622. { required: true, message: i18n.t('compute.text_190') },
  623. ],
  624. },
  625. ],
  626. },
  627. tag: [
  628. 'tag',
  629. {
  630. rules: [
  631. { validator: validateForm('tagName') },
  632. ],
  633. },
  634. ],
  635. os_arch: [
  636. 'os_arch',
  637. {
  638. rules: [
  639. { required: true, message: i18n.t('compute.text_1363') },
  640. ],
  641. },
  642. ],
  643. hostName: [
  644. 'hostName',
  645. ],
  646. portMapping: {
  647. key: i => [
  648. `containerPorts[${i}]`,
  649. {
  650. rules: [
  651. { required: true, message: i18n.t('common.tips.input', [i18n.t('compute.repo.container_port')]) },
  652. ],
  653. },
  654. ],
  655. value: i => [
  656. `hostPorts[${i}]`,
  657. ],
  658. },
  659. containers: {
  660. name: i => [
  661. `containerNames[${i}]`,
  662. {
  663. rules: [
  664. { required: true, message: `${i18n.t('common.placeholder')}${i18n.t('common.name')}` },
  665. { validator: validateForm('k8sLabel') },
  666. ],
  667. },
  668. ],
  669. source: i => [
  670. `containerSources[${i}]`,
  671. {
  672. initialValue: 'custom',
  673. },
  674. ],
  675. registryImage: i => [
  676. `registryImages[${i}]`,
  677. {
  678. rules: [
  679. { required: true, message: i18n.t('common.tips.select', [i18n.t('compute.eci.repo.image.registry')]) },
  680. ],
  681. },
  682. ],
  683. imageCredentialId: i => [
  684. `imageCredentialIds[${i}]`,
  685. ],
  686. image: i => [
  687. `containerimages[${i}]`,
  688. {
  689. rules: [
  690. { required: true, message: `${i18n.t('common.placeholder')}${i18n.t('compute.repo.container_image')}` },
  691. ],
  692. },
  693. ],
  694. cpu: i => [
  695. `containerCpus[${i}]`,
  696. ],
  697. memory: i => [
  698. `containerMemorys[${i}]`,
  699. ],
  700. command: i => [
  701. `containerCommands[${i}]`,
  702. ],
  703. arg: i => [
  704. `containerArgs[${i}]`,
  705. ],
  706. volumeMount: i => ({
  707. key: j => [
  708. `containerVolumeMountNames[${i}][${j}]`,
  709. {
  710. rules: [
  711. { validator: validateMountNames, trigger: 'blur' },
  712. ],
  713. },
  714. ],
  715. value: j => [
  716. `containerVolumeMountPaths[${i}][${j}]`,
  717. {
  718. rules: [
  719. { required: true, message: i18n.t('common.tips.input', []) },
  720. { validator: validateValidPath, trigger: 'blur' },
  721. ],
  722. },
  723. ],
  724. }),
  725. env: i => ({
  726. key: j => [
  727. `containerEnvNames[${i}][${j}]`,
  728. {
  729. rules: [
  730. { required: true, message: i18n.t('common.tips.input', [i18n.t('compute.repo.variables')]) },
  731. ],
  732. },
  733. ],
  734. value: j => [
  735. `containerEnvValues[${i}][${j}]`,
  736. {
  737. rules: [
  738. { required: true, message: i18n.t('common.tips.input', [i18n.t('compute.repo.value')]) },
  739. ],
  740. },
  741. ],
  742. }),
  743. privileged: i => [
  744. `containerPrivilegeds[${i}]`,
  745. {
  746. valuePropName: 'checked',
  747. initialValue: false,
  748. },
  749. ],
  750. },
  751. }
  752. }
  753. export class Decorator {
  754. decorators = {}
  755. // eslint-disable-next-line
  756. constructor(type) {
  757. this.type = type
  758. }
  759. createDecorators () {
  760. return createVmDecorators(this.type)
  761. }
  762. }
  763. /**
  764. * 根据表单拼装创建参数
  765. *
  766. * @export
  767. * @class GenCreateData
  768. */
  769. export class GenCreateData {
  770. // eslint-disable-next-line
  771. constructor(fd, fi) {
  772. if (R.isNil(fd)) return
  773. this.fd = fd
  774. this.fi = fi
  775. this.createType = this.fi.createType
  776. this.isIDC = this.createType === SERVER_TYPE.idc
  777. this.isPrepaid = this.fd.resourceType === RESOURCE_TYPES_MAP.prepaid.key
  778. }
  779. /**
  780. * 拼装磁盘数据
  781. *
  782. * @param { Object } item // 磁盘数据
  783. * @param { String } type // 磁盘类型 sys | data
  784. * @param { Number } index // 序号
  785. * @returns { Object }
  786. * @memberof GenCreateData
  787. */
  788. genDisk (item, type, index) {
  789. const ret = {
  790. disk_type: type,
  791. index,
  792. backend: item.type === STORAGE_AUTO.key ? '' : item.type,
  793. size: item.size * 1024,
  794. format: 'raw',
  795. fs: 'ext4',
  796. }
  797. if (type === 'sys' && this.fd.imageType === IMAGES_TYPE_MAP.iso.key && this.isWindows()) {
  798. ret.driver = 'ide'
  799. }
  800. if (item.medium) {
  801. ret.medium = item.medium
  802. }
  803. if (item.schedtags) {
  804. ret.schedtags = item.schedtags
  805. }
  806. if (item.filetype) {
  807. ret.fs = item.filetype
  808. if (item.filetype !== 'swap') {
  809. ret.mountpoint = item.mountpoint
  810. }
  811. }
  812. if (item.storage_id) {
  813. ret.storage_id = item.storage_id
  814. }
  815. if (item.iops) {
  816. ret.iops = item.iops
  817. }
  818. if (item.throughput) {
  819. ret.throughput = item.throughput
  820. }
  821. if (item.preallocation) {
  822. ret.preallocation = item.preallocation
  823. }
  824. // 磁盘区分介质
  825. if (diskSupportTypeMedium(this.fd.hypervisor)) {
  826. ret.backend = getOriginDiskKey(ret.backend)
  827. }
  828. return ret
  829. }
  830. isWindows () {
  831. let isWindows = false
  832. const osType = (_.get(this.fi, 'imageMsg.info.properties.os_type') || '').toLowerCase()
  833. const os = (_.get(this.fd, 'os') || '').toLowerCase()
  834. if (~[osType, os].indexOf('windows')) {
  835. isWindows = true
  836. }
  837. return isWindows
  838. }
  839. _getDataDiskType (dataDiskTypes) {
  840. if (!R.isNil(dataDiskTypes) && !R.isEmpty(dataDiskTypes)) {
  841. const firstKey = Object.keys(dataDiskTypes)[0]
  842. if (firstKey && dataDiskTypes[firstKey]) {
  843. return dataDiskTypes[firstKey].key
  844. }
  845. }
  846. }
  847. _genDisksArr () {
  848. const dataDiskType = this._getDataDiskType(this.fd.dataDiskTypes)
  849. const dataDisk = []
  850. R.forEachObjIndexed((value, key) => {
  851. const diskObj = {
  852. size: value,
  853. type: dataDiskType,
  854. }
  855. if (this.fd.dataDiskFiletypes && this.fd.dataDiskFiletypes[key]) {
  856. diskObj.filetype = this.fd.dataDiskFiletypes[key]
  857. }
  858. if (this.fd.dataDiskMountPaths && this.fd.dataDiskMountPaths[key]) {
  859. diskObj.mountpoint = this.fd.dataDiskMountPaths[key]
  860. }
  861. if (this.fd.dataDiskSnapshots && this.fd.dataDiskSnapshots[key]) {
  862. diskObj.snapshot_id = this.fd.dataDiskSnapshots[key]
  863. }
  864. if (this.fd.dataDiskSchedtags && this.fd.dataDiskSchedtags[key]) {
  865. diskObj.schedtags = [
  866. { id: this.fd.dataDiskSchedtags[key] },
  867. ]
  868. if (this.fd.dataDiskPolicys && this.fd.dataDiskPolicys[key]) {
  869. diskObj.schedtags[0].strategy = this.fd.dataDiskPolicys[key]
  870. }
  871. }
  872. if (this.fd.dataDiskStorages && this.fd.dataDiskStorages[key]) {
  873. // if system disk specifies storage, the data disks should do the same
  874. diskObj.storage_id = this.fd.dataDiskStorages[key] || this.fd.systemDiskStorage
  875. }
  876. if (this.fd.dataDiskIops && this.fd.dataDiskIops[key]) {
  877. diskObj.iops = this.fd.dataDiskIops[key]
  878. }
  879. if (this.fd.dataDiskThroughputs && this.fd.dataDiskThroughputs[key]) {
  880. diskObj.throughput = this.fd.dataDiskThroughputs[key]
  881. }
  882. if (this.fi.dataDiskMedium) {
  883. diskObj.medium = this.fi.dataDiskMedium
  884. }
  885. if (this.fd.dataDiskPreallocation && this.fd.dataDiskPreallocation[key]) {
  886. diskObj.preallocation = this.fd.dataDiskPreallocation[key]
  887. }
  888. dataDisk.push(diskObj)
  889. }, this.fd.dataDiskSizes)
  890. const disks = { data: dataDisk }
  891. return disks
  892. }
  893. /**
  894. * 组装所有磁盘数据,数据盘
  895. *
  896. * @returns { Array }
  897. * @memberof GenCreateData
  898. */
  899. genDisks () {
  900. const disks = this._genDisksArr()
  901. if (this.isPublic && this.isPrepaid) {
  902. return this.fd.spec.disks
  903. }
  904. const ret = []
  905. for (let i = 0, len = disks.data.length; i < len; i++) {
  906. ret.push(this.genDisk(disks.data[i], 'data', i + 1))
  907. }
  908. return ret
  909. }
  910. /**
  911. * 组装所有网络数据
  912. *
  913. * @returns { Array }
  914. * @memberof GenCreateData
  915. */
  916. getPortMappings () {
  917. const port_mappings = []
  918. if (this.fd.containerPorts) {
  919. for (const k in this.fd.containerPorts) {
  920. const pm = {}
  921. if (this.fd.containerPorts[k]) {
  922. pm.port = this.fd.containerPorts[k]
  923. }
  924. if (this.fd.hostPorts[k]) {
  925. pm.host_port = this.fd.hostPorts[k]
  926. }
  927. if (pm) {
  928. port_mappings.push(pm)
  929. }
  930. }
  931. }
  932. return port_mappings
  933. }
  934. genNetworks () {
  935. let ret = [{ exit: false }]
  936. // 指定 IP 子网
  937. if (this.fd.networkType === NETWORK_OPTIONS_MAP.manual.key) {
  938. ret = []
  939. R.forEachObjIndexed((value, key) => {
  940. const obj = {
  941. network: value,
  942. }
  943. if (this.fd.networkIps) {
  944. const address = this.fd.networkIps[key]
  945. if (address) {
  946. obj.address = address
  947. }
  948. }
  949. if (this.fd.networkMacs) {
  950. const mac = this.fd.networkMacs[key]
  951. if (mac) {
  952. obj.mac = mac
  953. }
  954. }
  955. if (this.fd.networkExits) {
  956. const exit = this.fd.networkExits[key]
  957. if (exit) {
  958. obj.exit = true
  959. }
  960. }
  961. if (this.fd.networkIPv6s) {
  962. const ipv6 = this.fd.networkIPv6s[key]
  963. if (ipv6) {
  964. obj.require_ipv6 = true
  965. }
  966. }
  967. const target = this.fi.networkList.filter(item => item.key === key)
  968. if (this.fd.networkIpsAddress6) {
  969. const ipv6Last = this.fd.networkIpsAddress6[key]
  970. const ipv6First = getIpv6Start(target[0]?.network?.guest_ip6_start)
  971. obj.address6 = ipv6First + ipv6Last
  972. }
  973. if (obj.require_ipv6) {
  974. if (this.fd.networkIPv6Modes && this.fd.networkIPv6Modes[key] === 'only') {
  975. obj.strict_ipv6 = true
  976. }
  977. }
  978. if (!target[0]?.network?.guest_ip_start && !target[0]?.network?.guest_ip_end && target[0]?.network?.guest_ip6_start) {
  979. obj.strict_ipv6 = true
  980. }
  981. if (this.fd.networkDevices) {
  982. const device = this.fd.networkDevices[key]
  983. if (device) {
  984. obj.sriov_device = { model: device }
  985. }
  986. }
  987. const portMappings = this.getPortMappings()
  988. if (portMappings.length > 0) {
  989. obj.port_mappings = portMappings
  990. }
  991. ret.push(obj)
  992. }, this.fd.networks)
  993. }
  994. // 指定 调度标签
  995. if (this.fd.networkType === NETWORK_OPTIONS_MAP.schedtag.key) {
  996. ret = []
  997. R.forEachObjIndexed((value, key) => {
  998. const obj = {
  999. id: value,
  1000. }
  1001. const strategy = this.fd.networkPolicys[key]
  1002. if (strategy) {
  1003. obj.strategy = strategy
  1004. }
  1005. if (this.fd.networkDevices) {
  1006. const device = this.fd.networkDevices[key]
  1007. if (device) {
  1008. obj.sriov_device = { model: device }
  1009. }
  1010. }
  1011. ret.push({
  1012. schedtags: [obj],
  1013. })
  1014. }, this.fd.networkSchedtags)
  1015. }
  1016. return ret
  1017. }
  1018. /**
  1019. * 获取配置的PCI数据
  1020. *
  1021. * @returns { Array }
  1022. * @memberof GenCreateData
  1023. */
  1024. genPciDevices () {
  1025. const ret = []
  1026. const { pciCount, pciModel } = this.fd
  1027. const pciKeys = Object.keys(this.fd.pciCount)
  1028. pciKeys.forEach(key => {
  1029. for (let i = 0, len = pciCount[key]; i < len; i++) {
  1030. const regexp = /vendor=(.+):(.+)/
  1031. const matched = pciModel[key].match(regexp)
  1032. const model = matched[2]
  1033. const vendor = matched[1]
  1034. ret.push({
  1035. model,
  1036. vendor,
  1037. })
  1038. }
  1039. })
  1040. return ret
  1041. }
  1042. /**
  1043. * 获取调度策略所提交的 key 与 value
  1044. *
  1045. * @returns
  1046. * @memberof GenCreateData
  1047. */
  1048. getSchedPolicyValueKey () {
  1049. const ret = {}
  1050. // 调度策略选择为 指定宿主机
  1051. if (this.fd.schedPolicyType === SCHED_POLICY_OPTIONS_MAP.host.key) {
  1052. if (this.isPublic) {
  1053. ret.key = 'prefer_manager'
  1054. } else {
  1055. ret.key = 'prefer_host'
  1056. }
  1057. ret.value = this.fd.schedPolicyHost
  1058. } else if (this.showPreferManager()) { // 如果是通过云账号过滤镜像
  1059. ret.key = 'prefer_manager'
  1060. ret.value = this.fd.prefer_manager
  1061. }
  1062. // 调度策略选择为 调度标签
  1063. if (this.fd.schedPolicyType === SCHED_POLICY_OPTIONS_MAP.schedtag.key) {
  1064. ret.key = 'schedtags'
  1065. ret.value = []
  1066. R.forEachObjIndexed((value, key) => {
  1067. const item = { id: value }
  1068. if (this.fd.policySchedtagPolicys[key]) {
  1069. item.strategy = this.fd.policySchedtagPolicys[key]
  1070. }
  1071. ret.value.push(item)
  1072. }, this.fd.policySchedtagSchedtags)
  1073. }
  1074. return ret
  1075. }
  1076. /**
  1077. * 获取平台
  1078. *
  1079. * @returns { String }
  1080. * @memberof GenCreateData
  1081. */
  1082. getHypervisor () {
  1083. return HYPERVISORS_MAP.pod.key
  1084. }
  1085. /**
  1086. * 是否是通过云账号过滤后选择的镜像
  1087. *
  1088. * @returns { String }
  1089. * @memberof GenCreateData
  1090. */
  1091. showPreferManager () {
  1092. const imageMsg = IMAGES_TYPE_MAP[this.fd.imageType]
  1093. return imageMsg && imageMsg.enable_cloudaccount
  1094. }
  1095. /**
  1096. * 获取Region
  1097. *
  1098. * @returns { String }
  1099. * @memberof GenCreateData
  1100. */
  1101. getPreferRegion () {
  1102. if (this.isPublic && !this.isPrepaid) {
  1103. return this.fd.sku.cloudregion_id
  1104. }
  1105. return this.fd.cloudregion.key
  1106. }
  1107. /**
  1108. * 获取Zone
  1109. *
  1110. * @returns { String }
  1111. * @memberof GenCreateData
  1112. */
  1113. getPreferZone () {
  1114. let ret = ''
  1115. if (R.is(Object, this.fd.zone)) {
  1116. ret = this.fd.zone.key
  1117. }
  1118. if (R.is(String, this.fd.zone)) { // 字符串形式是公有云 AreaSelect 的 zone
  1119. ret = this.fd.zone
  1120. }
  1121. return ret
  1122. }
  1123. /**
  1124. * 获取CPU核数
  1125. *
  1126. * @returns { String }
  1127. * @memberof GenCreateData
  1128. */
  1129. getCpuCount () {
  1130. let ret = this.fd.vcpu
  1131. if (this.isPublic && this.isPrepaid) {
  1132. ret = this.fd.spec.vcpu_count
  1133. }
  1134. return ret
  1135. }
  1136. /**
  1137. * 获取CPU插槽数
  1138. * @returns
  1139. */
  1140. getCpuSockets () {
  1141. return this.fi.cpuSockets
  1142. }
  1143. /**
  1144. * 获取内存
  1145. *
  1146. * @returns { String }
  1147. * @memberof GenCreateData
  1148. */
  1149. getMemSize () {
  1150. let ret = this.fd.vmem
  1151. if (this.isPublic && this.isPrepaid) {
  1152. ret = this.fd.spec.vmem_size * 1024
  1153. }
  1154. return ret
  1155. }
  1156. getPreferManagerId () {
  1157. let prefer_manager_id = ''
  1158. const hypervisor = this.getHypervisor()
  1159. const privateHypervisors = [HYPERVISORS_MAP.hcso.key, HYPERVISORS_MAP.hcs.key]
  1160. if (this.isPublic || privateHypervisors.includes(hypervisor)) {
  1161. prefer_manager_id = this.fd.cloudprovider
  1162. }
  1163. return prefer_manager_id
  1164. }
  1165. generatePod () {
  1166. const tabKeys = this.fi.containerPanes.map(v => v.key)
  1167. const getEnvs = (names, values) => {
  1168. const envs = []
  1169. if (names) {
  1170. for (const k in names) {
  1171. envs.push({ key: names[k], value: values[k] })
  1172. }
  1173. }
  1174. return envs
  1175. }
  1176. const getVolumeMounts = (names, paths) => {
  1177. const volume_mounts = []
  1178. if (names) {
  1179. for (const k in names) {
  1180. volume_mounts.push({
  1181. type: 'disk',
  1182. disk: {
  1183. index: names[k],
  1184. },
  1185. mount_path: paths[k],
  1186. })
  1187. }
  1188. }
  1189. return volume_mounts
  1190. }
  1191. const containers = tabKeys.map(k => {
  1192. const pciDevices = (this.fd.pciEnable && this.genPciDevices()) || []
  1193. const image = this.fd.registryImages?.[k] || ''
  1194. const credentialId = this.fd.imageCredentialIds?.[k] || ''
  1195. const spec = {
  1196. name: this.fd.containerNames?.[k],
  1197. image: removeHttp(image) || this.fd.containerimages?.[k],
  1198. command: this.fd.containerCommands?.[k]?.split(' '),
  1199. args: this.fd.containerArgs?.[k]?.split(' '),
  1200. privileged: this.fd.containerPrivilegeds?.[k],
  1201. devices: pciDevices.map((item, idx) => {
  1202. return {
  1203. type: 'isolated_device',
  1204. isolated_device: {
  1205. index: idx,
  1206. },
  1207. }
  1208. }),
  1209. envs: getEnvs(this.fd.containerEnvNames?.[k], this.fd.containerEnvValues?.[k]),
  1210. volume_mounts: getVolumeMounts(this.fd.containerVolumeMountNames?.[k], this.fd.containerVolumeMountPaths?.[k]),
  1211. }
  1212. if (credentialId) {
  1213. spec.image_credential_id = credentialId
  1214. }
  1215. return spec
  1216. })
  1217. return {
  1218. containers,
  1219. // port_mappings: getPortMappings(),
  1220. }
  1221. }
  1222. /**
  1223. * 组装所有的创建数据
  1224. *
  1225. * @returns { Object }
  1226. * @memberof GenCreateData
  1227. */
  1228. all () {
  1229. const { create_server_auto_start = true } = store.getters.globalSetting.value || {}
  1230. const data = {
  1231. auto_start: create_server_auto_start,
  1232. generate_name: this.fd.name && this.fd.name.trim(),
  1233. description: this.fd.description,
  1234. hypervisor: this.getHypervisor(),
  1235. __count__: this.fd.count,
  1236. disks: this.genDisks(),
  1237. nets: this.genNetworks(),
  1238. prefer_region: this.getPreferRegion(),
  1239. vcpu_count: this.getCpuCount(),
  1240. vmem_size: this.getMemSize(),
  1241. project_id: (this.fd.project && this.fd.project.key) || store.getters.userInfo.projectId,
  1242. os_arch: _.get(HOST_CPU_ARCHS, `[${this.fd.os_arch}].key`),
  1243. hostname: this.fd.hostName,
  1244. }
  1245. // 非预付费资源池不会添加sku
  1246. if (!this.isPrepaid) {
  1247. data.sku = this.fd.sku.name
  1248. }
  1249. // 弹性IP
  1250. if (this.fd.eip_type === EIP_TYPES_MAP.new.key || this.fd.eip_type === EIP_TYPES_MAP.public.key) {
  1251. if (
  1252. this.fd.eip_charge_type === EIP_CHARGE_TYPES_MAP.traffic.key ||
  1253. this.fd.eip_charge_type === EIP_CHARGE_TYPES_MAP.bandwidth.key
  1254. ) {
  1255. if (this.fd.eip_type === EIP_TYPES_MAP.public.key) {
  1256. data.public_ip_charge_type = this.fd.eip_charge_type
  1257. data.public_ip_bw = this.fd.eip_bw
  1258. } else {
  1259. data.eip_charge_type = this.fd.eip_charge_type
  1260. data.eip_bw = this.fd.eip_bw
  1261. }
  1262. }
  1263. }
  1264. if (this.fd.eip_type === EIP_TYPES_MAP.bind.key) {
  1265. data.eip = this.fd.eip
  1266. }
  1267. // 包年包月参数
  1268. if (this.fd.billType === BILL_TYPES_MAP.package.key) {
  1269. data.duration = this.fd.duration
  1270. // 自动续费
  1271. data.auto_renew = this.fd.autoRenew
  1272. }
  1273. // 线路类型
  1274. if (this.fd.eip_bgp_type) {
  1275. data.eip_bgp_type = this.fd.eip_bgp_type
  1276. }
  1277. // pci
  1278. if (this.fd.pciEnable) {
  1279. data.isolated_devices = this.genPciDevices()
  1280. }
  1281. // 安全组
  1282. if (this.fd.secgroup_type && this.fd.secgroup_type === SECGROUP_OPTIONS_MAP.bind.key) {
  1283. data.secgroups = this.fd.secgroup
  1284. }
  1285. // 如果设置了调度策略则拼装调度所需数据 或者 通过云账号过滤镜像
  1286. if ((this.fd.schedPolicyType !== SCHED_POLICY_OPTIONS_MAP.default.key) || this.showPreferManager()) {
  1287. const schedPolicyValueKey = this.getSchedPolicyValueKey()
  1288. data[schedPolicyValueKey.key] = schedPolicyValueKey.value
  1289. }
  1290. // zone
  1291. const zoneId = this.getPreferZone()
  1292. if (zoneId) {
  1293. data.prefer_zone = zoneId
  1294. }
  1295. const prefer_manager_id = this.getPreferManagerId()
  1296. if (prefer_manager_id) {
  1297. data.prefer_manager_id = prefer_manager_id
  1298. }
  1299. // 到期释放
  1300. if (this.fd.billType !== BILL_TYPES_MAP.package.key && this.fd.durationStandard !== 'none') {
  1301. if (this.fd.durationStandard === 'custom') {
  1302. data.duration = this.fd.duration
  1303. } else {
  1304. data.duration = this.fd.durationStandard
  1305. }
  1306. data.billing_type = 'postpaid'
  1307. }
  1308. // 镜像类型为 iso 需要加参数 cdrom
  1309. if (this.fd.imageType === IMAGES_TYPE_MAP.iso.key || this.fd.imageType === IMAGES_TYPE_MAP.private_iso.key) {
  1310. data.cdrom = this.fd.image.key
  1311. }
  1312. // 主机镜像需要guest image id参数,并且把磁盘中的镜像ID回填回去
  1313. if (this.fd.imageType === IMAGES_TYPE_MAP.host.key) {
  1314. data.guest_image_id = this.fd.image.key
  1315. data.disks.forEach((val, i) => {
  1316. if (this.fi.imageMsg.data_images && i < this.fi.imageMsg.data_images.length) {
  1317. data.disks[i] = { ...val, image_id: this.fi.imageMsg.data_images[i].id }
  1318. }
  1319. })
  1320. }
  1321. // 主机快照需要instance_snapshot_id参数
  1322. if (this.fd.imageType === IMAGES_TYPE_MAP.snapshot.key) {
  1323. data.instance_snapshot_id = this.fd.image.key
  1324. delete data.disks // 主机快照不需要 disks 字段
  1325. } else if (this.fd.imageType === IMAGES_TYPE_MAP.backup.key) {
  1326. data.instance_backup_id = this.fd.image.key
  1327. // delete data.disks // 主机备份可以保留 disks 字段
  1328. }
  1329. // 主机组
  1330. if (this.fd.groupsEnable && this.fd.groups && this.fd.groups.length) {
  1331. data.groups = this.fd.groups
  1332. }
  1333. // 标签
  1334. if (this.fd.tag) {
  1335. data.__meta__ = this.fd.tag
  1336. }
  1337. // 插槽数
  1338. if (this.fi.showCpuSockets) {
  1339. data.cpu_sockets = this.getCpuSockets()
  1340. }
  1341. // 容器
  1342. data.pod = this.generatePod(data)
  1343. return data
  1344. }
  1345. /**
  1346. * 获取创建预测的错误信息
  1347. *
  1348. * @returns { Array }
  1349. * @memberof GenCreateData
  1350. */
  1351. getForecastErrors (data) {
  1352. const errors = []
  1353. if (data.filtered_candidates && data.filtered_candidates.length > 0) {
  1354. for (let i = 0, len = data.filtered_candidates.length; i < len; i++) {
  1355. const item = data.filtered_candidates[i]
  1356. let message = `${i18n.t('dictionary.host')}【${item.name}】`
  1357. const filterMapItem = FORECAST_FILTERS_MAP[item.filter_name]
  1358. if (filterMapItem) {
  1359. message += filterMapItem
  1360. } else {
  1361. message += i18n.t('compute.text_1325', [item.filter_name])
  1362. }
  1363. errors.push({
  1364. message,
  1365. children: item.reasons,
  1366. })
  1367. }
  1368. } else {
  1369. errors.push({
  1370. message: i18n.t('compute.text_227'),
  1371. })
  1372. }
  1373. return {
  1374. errors,
  1375. allow_count: data.allow_count || 0,
  1376. req_count: data.req_count || 1,
  1377. not_allow_reasons: data.not_allow_reasons,
  1378. }
  1379. }
  1380. }