createServer.js 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900
  1. // 创建主机相应组件的参数
  2. import * as R from 'ramda'
  3. import _ from 'lodash'
  4. import ipaddr from 'ipaddr.js'
  5. import {
  6. NETWORK_OPTIONS_MAP,
  7. SERVER_TYPE,
  8. EIP_TYPES_MAP,
  9. EIP_CHARGE_TYPES_MAP,
  10. BILL_TYPES_MAP,
  11. LOGIN_TYPES_MAP,
  12. SCHED_POLICY_OPTIONS_MAP,
  13. STORAGE_AUTO,
  14. SECGROUP_OPTIONS_MAP,
  15. FORECAST_FILTERS_MAP,
  16. RESOURCE_TYPES_MAP,
  17. GPU_DEV_TYPE_OPTION_MAP,
  18. } from '@Compute/constants'
  19. import { IMAGES_TYPE_MAP, HOST_CPU_ARCHS } from '@/constants/compute'
  20. import { HYPERVISORS_MAP } from '@/constants'
  21. import validateForm, { isRequired, isWithinRange } from '@/utils/validate'
  22. import store from '@/store'
  23. import i18n from '@/locales'
  24. import { diskSupportTypeMedium, getOriginDiskKey } from '@/utils/common/hypervisor'
  25. export function checkIpInSegment (i, networkData) {
  26. return (rule, value, cb) => {
  27. const isIn = isWithinRange(value, networkData.guest_ip_start, networkData.guest_ip_end)
  28. if (isIn) {
  29. cb()
  30. } else {
  31. cb(new Error(i18n.t('compute.text_205')))
  32. }
  33. }
  34. }
  35. export function getIpv6Start (ipv6) {
  36. try {
  37. const IPv6 = ipaddr.IPv6.parse(ipv6)
  38. return IPv6.toNormalizedString().split(':').slice(0, 4).join(':') + ':'
  39. } catch (err) {
  40. console.error('IPv6 address is error')
  41. return ''
  42. }
  43. }
  44. export function ipv6ToHex (ipv6) {
  45. return ipv6.parts.map(part => part.toString(16).padStart(4, '0')).join('')
  46. }
  47. export function checkIpV6 (i, networkData) {
  48. return (rule, value, cb) => {
  49. const ipv6First = getIpv6Start(networkData.guest_ip6_start)
  50. try {
  51. const ipv6 = ipv6First + value
  52. const ipAddr = ipaddr.IPv6.parse(ipv6)
  53. const subnet1Addr = ipaddr.IPv6.parse(networkData.guest_ip6_start)
  54. const subnet2Addr = ipaddr.IPv6.parse(networkData.guest_ip6_end)
  55. if (ipAddr.kind() !== 'ipv6') {
  56. cb(new Error(i18n.t('compute.error_ipv6')))
  57. }
  58. const target = ipv6ToHex(ipAddr)
  59. const start = ipv6ToHex(subnet1Addr)
  60. const end = ipv6ToHex(subnet2Addr)
  61. // 检查IP是否在两个子网之间
  62. if (!((target >= start && target <= end))) {
  63. cb(new Error(i18n.t('compute.ipv6_within_range')))
  64. }
  65. cb()
  66. } catch (err) {
  67. cb(new Error(i18n.t('compute.error_ipv6')))
  68. }
  69. }
  70. }
  71. export function diskValidator (rule, value, callback) {
  72. if (R.isNil(value) || R.isEmpty(value)) {
  73. return callback(new Error(i18n.t('compute.text_206')))
  74. }
  75. if (!value.startsWith('/')) {
  76. return callback(new Error(i18n.t('compute.text_207')))
  77. }
  78. if (value === '/') {
  79. return callback(new Error(i18n.t('compute.text_208')))
  80. }
  81. callback()
  82. }
  83. export const createVmDecorators = (type, initData = {}) => {
  84. let imageTypeInitValue = IMAGES_TYPE_MAP.standard.key
  85. if (type === SERVER_TYPE.public) {
  86. imageTypeInitValue = IMAGES_TYPE_MAP.public.key // 公有云机器默认选择公有云镜像
  87. }
  88. if (type === SERVER_TYPE.private) {
  89. imageTypeInitValue = IMAGES_TYPE_MAP.private.key // 私有云机器默认选择私有云镜像
  90. }
  91. let initImage = ''
  92. if (!R.isNil(initData.extraData?.image_type) && !R.isEmpty(initData.extraData?.image_type)) {
  93. if (initData.disks && initData.disks.length) {
  94. if (initData.disks[0].image_id) {
  95. initImage = initData.disks[0].image_id
  96. }
  97. }
  98. const initImageType = initData.extraData?.image_type
  99. if (initImageType === IMAGES_TYPE_MAP.iso.key || initImageType === IMAGES_TYPE_MAP.private_iso.key) {
  100. initImage = initData.cdrom || ''
  101. }
  102. if (initImageType === IMAGES_TYPE_MAP.host.key) {
  103. initImage = initData.guest_image_id || ''
  104. }
  105. if (initImageType === IMAGES_TYPE_MAP.snapshot.key) {
  106. initImage = initData.instance_snapshot_id || ''
  107. } else if (initImageType === IMAGES_TYPE_MAP.backup.key) {
  108. initImage = initData.instance_backup_id || ''
  109. }
  110. }
  111. let initSystemDiskIops = ''
  112. let initSystemDiskThroughput = ''
  113. if (initData.disks && initData.disks.length && initData.disks[0].disk_type === 'sys') {
  114. initSystemDiskIops = initData.disks[0].iops
  115. initSystemDiskThroughput = initData.disks[0].throughput
  116. }
  117. let initNetworkType = NETWORK_OPTIONS_MAP.default.key
  118. if (initData.nets) {
  119. if (initData.nets[0] && initData.nets[0].hasOwnProperty('exit') && !initData.nets[0].exit) {
  120. initNetworkType = NETWORK_OPTIONS_MAP.default.key
  121. }
  122. if (initData.nets[0] && initData.nets[0].hasOwnProperty('network') && initData.extraData?.nets && initData.extraData?.nets[0] && initData.extraData?.nets[0].hasOwnProperty('network_id')) {
  123. initNetworkType = NETWORK_OPTIONS_MAP.manual.key
  124. }
  125. }
  126. let initEipType = EIP_TYPES_MAP.none.key
  127. if (initData.eip_charge_type) {
  128. initEipType = EIP_TYPES_MAP.new.key
  129. } else if (initData.public_ip_charge_type) {
  130. initEipType = EIP_TYPES_MAP.public.key
  131. }
  132. let initSecgroupType = 'default'
  133. if (initData.secgroups && initData.secgroups.length) {
  134. initSecgroupType = 'bind'
  135. }
  136. let initSchedPolicyType = 'default'
  137. if (initData.prefer_host) {
  138. initSchedPolicyType = 'host'
  139. }
  140. if (initData.schedtags && initData.schedtags.length) {
  141. initSchedPolicyType = 'schedtag'
  142. }
  143. let initEncryptEnable = ''
  144. if (initData.encrypt_key_new) {
  145. initEncryptEnable = 'new'
  146. }
  147. if (initData.encrypt_key_id) {
  148. initEncryptEnable = 'existing'
  149. }
  150. let initLoginType = 'random'
  151. if (initData.keypair) {
  152. initLoginType = 'keypair'
  153. }
  154. if (initData.hasOwnProperty('reset_password') && !initData.reset_password) {
  155. initLoginType = 'image'
  156. }
  157. if (initData.hasOwnProperty('password') && initData.password) {
  158. initLoginType = 'password'
  159. }
  160. return {
  161. domain: [
  162. 'domain',
  163. {
  164. initialValue: initData.extraData?.domain_id,
  165. rules: [
  166. { validator: isRequired(), message: i18n.t('rules.domain'), trigger: 'change' },
  167. ],
  168. },
  169. ],
  170. project: [
  171. 'project',
  172. {
  173. initialValue: initData.project_id,
  174. rules: [
  175. { validator: isRequired(), message: i18n.t('rules.project'), trigger: 'change' },
  176. ],
  177. },
  178. ],
  179. name: [
  180. 'name',
  181. {
  182. initialValue: initData.generate_name || '',
  183. validateTrigger: 'blur',
  184. validateFirst: true,
  185. rules: [
  186. { required: true, message: i18n.t('compute.text_210') },
  187. // { validator: validateForm('resourceCreateName') },
  188. ],
  189. },
  190. ],
  191. description: [
  192. 'description',
  193. {
  194. initialValue: initData.description || '',
  195. },
  196. ],
  197. reason: [
  198. 'reason',
  199. {
  200. initialValue: initData.extraData?.reason || '',
  201. },
  202. ],
  203. count: [
  204. 'count',
  205. {
  206. initialValue: initData.__count__ || 1,
  207. rules: [
  208. { required: true, message: i18n.t('compute.text_211') },
  209. ],
  210. },
  211. ],
  212. cloudregionZone: {
  213. cloudregion: [
  214. 'cloudregion',
  215. {
  216. initialValue: { key: initData.prefer_region || '', label: '' },
  217. rules: [
  218. { validator: isRequired(), message: i18n.t('compute.text_212') },
  219. ],
  220. },
  221. ],
  222. zone: [
  223. 'zone',
  224. {
  225. initialValue: { key: initData.prefer_zone || '', label: '' },
  226. rules: [
  227. { validator: isRequired(), message: i18n.t('compute.text_213') },
  228. ],
  229. },
  230. ],
  231. },
  232. imageOS: {
  233. prefer_manager: [
  234. 'prefer_manager',
  235. {
  236. initialValue: initData.prefer_manager || '',
  237. rules: [
  238. { required: true, message: i18n.t('compute.text_149') },
  239. ],
  240. },
  241. ],
  242. os: [
  243. 'os',
  244. {
  245. initialValue: initData.extraData?.os || '',
  246. rules: [
  247. { required: true, message: i18n.t('compute.text_153') },
  248. ],
  249. },
  250. ],
  251. image: [
  252. 'image',
  253. {
  254. initialValue: { key: initImage, label: '' },
  255. rules: [
  256. { validator: isRequired(), message: i18n.t('compute.text_214') },
  257. ],
  258. },
  259. ],
  260. imageType: [
  261. 'imageType',
  262. {
  263. initialValue: initData.extraData?.image_type || imageTypeInitValue,
  264. },
  265. ],
  266. },
  267. kickstart: {
  268. kickstart_enabled: [
  269. 'kickstart_enabled',
  270. {
  271. initialValue: false,
  272. },
  273. ],
  274. kickstart_config: [
  275. 'kickstart_config',
  276. {
  277. initialValue: '',
  278. },
  279. ],
  280. },
  281. loginConfig: {
  282. loginType: [
  283. 'loginType',
  284. {
  285. initialValue: initLoginType,
  286. },
  287. ],
  288. keypair: [
  289. 'loginKeypair',
  290. {
  291. initialValue: initData.keypair || '', // { key: '', label: '' }
  292. rules: [
  293. { required: true, message: i18n.t('compute.text_203') },
  294. ],
  295. },
  296. ],
  297. password: [
  298. 'loginPassword',
  299. {
  300. initialValue: initData.password || '',
  301. validateFirst: true,
  302. rules: [
  303. { required: true, message: i18n.t('compute.text_204') },
  304. { validator: validateForm('sshPassword') },
  305. ],
  306. },
  307. ],
  308. },
  309. hypervisor: [
  310. 'hypervisor',
  311. {
  312. initialValue: initData.hypervisor || '',
  313. rules: [
  314. { required: true, message: i18n.t('compute.text_215') },
  315. ],
  316. },
  317. ],
  318. cloudprovider: [
  319. 'cloudprovider',
  320. {
  321. rules: [
  322. { required: true, message: i18n.t('compute.text_149') },
  323. ],
  324. },
  325. ],
  326. // 废弃数据,使用 pci 数据代替
  327. gpu: {
  328. gpuEnable: [
  329. 'gpuEnable',
  330. {
  331. valuePropName: 'checked',
  332. initialValue: false,
  333. },
  334. ],
  335. gpu: [
  336. 'gpu',
  337. {
  338. rules: [
  339. { required: true, message: i18n.t('compute.text_147') },
  340. ],
  341. },
  342. ],
  343. gpuCount: [
  344. 'gpuCount',
  345. {
  346. initialValue: 1,
  347. },
  348. ],
  349. devType: [
  350. 'devType',
  351. {
  352. initialValue: GPU_DEV_TYPE_OPTION_MAP['GPU-VGA'].value,
  353. },
  354. ],
  355. },
  356. pci: {
  357. pciEnable: [
  358. 'pciEnable',
  359. {
  360. valuePropName: 'checked',
  361. initialValue: false,
  362. },
  363. ],
  364. pciDevType: i => [
  365. `pciDevType[${i}]`,
  366. ],
  367. pciModel: i => [
  368. `pciModel[${i}]`,
  369. {
  370. rules: [
  371. { required: true, message: i18n.t('compute.text_147') },
  372. ],
  373. },
  374. ],
  375. pciCount: i => [
  376. `pciCount[${i}]`,
  377. {
  378. initialValue: 1,
  379. },
  380. ],
  381. },
  382. vcpu: [
  383. 'vcpu',
  384. {
  385. initialValue: initData.vcpu_count || 2,
  386. },
  387. ],
  388. vmem: [
  389. 'vmem',
  390. {
  391. initialValue: initData.vmem_size || 2048,
  392. },
  393. ],
  394. sku: [
  395. 'sku',
  396. {
  397. initialValue: initData.sku || '',
  398. rules: [
  399. { validator: isRequired(true, 'id'), message: i18n.t('compute.text_216') },
  400. ],
  401. },
  402. ],
  403. systemDisk: {
  404. type: [
  405. 'systemDiskType',
  406. {
  407. rules: [
  408. { validator: isRequired(), message: i18n.t('compute.text_121') },
  409. ],
  410. },
  411. ],
  412. size: [
  413. 'systemDiskSize',
  414. {
  415. rules: [
  416. { required: true, message: i18n.t('compute.text_122') },
  417. ],
  418. },
  419. ],
  420. schedtag: [
  421. 'systemDiskSchedtag',
  422. {
  423. validateTrigger: ['change', 'blur'],
  424. rules: [{
  425. required: true,
  426. message: i18n.t('compute.text_123'),
  427. }],
  428. },
  429. ],
  430. policy: [
  431. 'systemDiskPolicy',
  432. {
  433. initialValue: '',
  434. validateTrigger: ['blur', 'change'],
  435. rules: [{
  436. required: true,
  437. message: i18n.t('compute.text_123'),
  438. }],
  439. },
  440. ],
  441. storage: [
  442. 'systemDiskStorage',
  443. {
  444. rules: [{
  445. required: true,
  446. message: i18n.t('compute.text_1351'),
  447. }],
  448. },
  449. ],
  450. iops: [
  451. 'systemDiskIops',
  452. {
  453. initialValue: initSystemDiskIops,
  454. rules: [{
  455. required: true,
  456. message: i18n.t('compute.iops_input_tip'),
  457. }],
  458. },
  459. ],
  460. throughput: [
  461. 'systemDiskThroughput',
  462. {
  463. initialValue: initSystemDiskThroughput,
  464. rules: [{
  465. required: true,
  466. message: i18n.t('compute.throuthput_input_tip'),
  467. }],
  468. },
  469. ],
  470. preallocation: [
  471. 'systemDiskPreallocation',
  472. ],
  473. auto_reset: [
  474. 'systemDiskAutoReset',
  475. {
  476. valuePropName: 'checked',
  477. initialValue: false,
  478. },
  479. ],
  480. },
  481. dataDisk: {
  482. type: i => [
  483. `dataDiskTypes[${i}]`,
  484. {
  485. rules: [
  486. { validator: isRequired(), message: i18n.t('compute.text_121') },
  487. ],
  488. },
  489. ],
  490. size: i => [
  491. `dataDiskSizes[${i}]`,
  492. {
  493. rules: [
  494. { required: true, message: i18n.t('compute.text_122') },
  495. ],
  496. },
  497. ],
  498. schedtag: i => [
  499. `dataDiskSchedtags[${i}]`,
  500. {
  501. validateTrigger: ['change', 'blur'],
  502. rules: [{
  503. required: true,
  504. message: i18n.t('compute.text_123'),
  505. }],
  506. },
  507. ],
  508. policy: i => [
  509. `dataDiskPolicys[${i}]`,
  510. {
  511. initialValue: '',
  512. validateTrigger: ['blur', 'change'],
  513. rules: [{
  514. required: true,
  515. message: i18n.t('compute.text_123'),
  516. }],
  517. },
  518. ],
  519. snapshot: i => [
  520. `dataDiskSnapshots[${i}]`,
  521. {
  522. validateTrigger: ['blur', 'change'],
  523. rules: [{
  524. required: true,
  525. message: i18n.t('compute.text_124'),
  526. }],
  527. },
  528. ],
  529. filetype: i => [
  530. `dataDiskFiletypes[${i}]`,
  531. {
  532. validateTrigger: ['blur', 'change'],
  533. rules: [{
  534. required: true,
  535. message: i18n.t('compute.text_125'),
  536. }],
  537. },
  538. ],
  539. mountPath: i => [
  540. `dataDiskMountPaths[${i}]`,
  541. {
  542. validateTrigger: ['blur', 'change'],
  543. rules: [{
  544. required: true,
  545. message: i18n.t('compute.text_126'),
  546. }, {
  547. validator: diskValidator,
  548. }],
  549. },
  550. ],
  551. storage: i => [
  552. `dataDiskStorages[${i}]`,
  553. {
  554. rules: [{
  555. required: true,
  556. message: i18n.t('compute.text_1351'),
  557. }],
  558. },
  559. ],
  560. iops: i => [
  561. `dataDiskIops[${i}]`,
  562. {
  563. rules: [{
  564. required: true,
  565. message: i18n.t('compute.iops_input_tip'),
  566. }],
  567. },
  568. ],
  569. throughput: i => [
  570. `dataDiskThroughputs[${i}]`,
  571. {
  572. rules: [{
  573. required: true,
  574. message: i18n.t('compute.throughput_input_tip'),
  575. }],
  576. },
  577. ],
  578. preallocation: i => [
  579. `dataDiskPreallocation[${i}]`,
  580. ],
  581. auto_reset: i => [
  582. `dataDiskAutoReset[${i}]`,
  583. {
  584. valuePropName: 'checked',
  585. initialValue: false,
  586. },
  587. ],
  588. },
  589. network: {
  590. networkType: [
  591. 'networkType',
  592. {
  593. initialValue: initNetworkType,
  594. },
  595. ],
  596. networkConfig: {
  597. vpcs: i => [
  598. `vpcs[${i}]`,
  599. {
  600. validateTrigger: ['change', 'blur'],
  601. initialValue: ['default', 'default'],
  602. rules: [{
  603. required: true,
  604. message: i18n.t('compute.text_194'),
  605. }],
  606. },
  607. ],
  608. networks: i => [
  609. `networks[${i}]`,
  610. {
  611. validateTrigger: ['change', 'blur'],
  612. rules: [{
  613. required: true,
  614. message: i18n.t('compute.text_217'),
  615. }],
  616. },
  617. ],
  618. ips: (i, networkData) => [
  619. `networkIps[${i}]`,
  620. {
  621. validateFirst: true,
  622. validateTrigger: ['blur', 'change'],
  623. rules: [
  624. {
  625. required: true,
  626. message: i18n.t('compute.text_218'),
  627. },
  628. {
  629. validator: validateForm('IPv4'),
  630. },
  631. {
  632. validator: checkIpInSegment(i, networkData),
  633. },
  634. ],
  635. },
  636. ],
  637. ips6: (i, networkData) => [
  638. `networkIpsAddress6[${i}]`,
  639. {
  640. validateFirst: true,
  641. validateTrigger: ['blur', 'change'],
  642. rules: [
  643. {
  644. required: true,
  645. message: i18n.t('compute.complete_ipv6_address'),
  646. },
  647. {
  648. validator: checkIpV6(i, networkData),
  649. },
  650. ],
  651. },
  652. ],
  653. ipv6_mode: (i, networkData) => [
  654. `networkIPv6Modes[${i}]`,
  655. {
  656. initialValue: 'all',
  657. },
  658. ],
  659. macs: (i, networkData) => [
  660. `networkMacs[${i}]`,
  661. {
  662. validateFirst: true,
  663. validateTrigger: ['blur', 'change'],
  664. rules: [
  665. {
  666. required: true,
  667. message: i18n.t('compute.text_806'),
  668. },
  669. {
  670. validator: validateForm('mac'),
  671. },
  672. ],
  673. },
  674. ],
  675. ipv6s: (i, networkData) => [
  676. `networkIPv6s[${i}]`,
  677. {
  678. validateFirst: true,
  679. valuePropName: 'checked',
  680. validateTrigger: ['blur', 'change'],
  681. },
  682. ],
  683. devices: i => [
  684. `networkDevices[${i}]`,
  685. {
  686. validateTrigger: ['change', 'blur'],
  687. rules: [{
  688. required: true,
  689. message: i18n.t('compute.sriov_device_tips'),
  690. }],
  691. },
  692. ],
  693. secgroups: i => [
  694. `networkSecgroups[${i}]`,
  695. {
  696. validateTrigger: ['change', 'blur'],
  697. rules: [{
  698. required: true,
  699. message: i18n.t('compute.secgroup_tips'),
  700. }],
  701. },
  702. ],
  703. },
  704. networkSchedtag: {
  705. schedtags: i => [
  706. `networkSchedtags[${i}]`,
  707. {
  708. validateTrigger: ['change', 'blur'],
  709. rules: [{
  710. required: true,
  711. message: i18n.t('compute.text_123'),
  712. }],
  713. },
  714. ],
  715. policys: (i, networkData) => [
  716. `networkPolicys[${i}]`,
  717. {
  718. validateTrigger: ['blur', 'change'],
  719. rules: [{
  720. required: true,
  721. message: i18n.t('common_256'),
  722. }],
  723. },
  724. ],
  725. devices: i => [
  726. `networkDevices[${i}]`,
  727. {
  728. validateTrigger: ['change', 'blur'],
  729. rules: [{
  730. required: true,
  731. message: i18n.t('compute.sriov_device_tips'),
  732. }],
  733. },
  734. ],
  735. },
  736. },
  737. schedPolicy: {
  738. schedPolicyType: [
  739. 'schedPolicyType',
  740. {
  741. initialValue: initSchedPolicyType,
  742. },
  743. ],
  744. schedPolicyHost: [
  745. 'schedPolicyHost',
  746. {
  747. initialValue: initData.prefer_host,
  748. rules: [
  749. { required: true, message: i18n.t('compute.text_219') },
  750. ],
  751. },
  752. ],
  753. policySchedtag: {
  754. schedtags: i => [
  755. `policySchedtagSchedtags[${i}]`,
  756. {
  757. validateTrigger: ['change', 'blur'],
  758. rules: [{
  759. required: true,
  760. message: i18n.t('compute.text_123'),
  761. }],
  762. },
  763. ],
  764. policys: (i, networkData) => [
  765. `policySchedtagPolicys[${i}]`,
  766. {
  767. validateTrigger: ['blur', 'change'],
  768. rules: [{
  769. required: true,
  770. message: i18n.t('common_256'),
  771. }],
  772. },
  773. ],
  774. },
  775. },
  776. bios: [
  777. 'bios',
  778. {
  779. initialValue: initData.bios || '',
  780. },
  781. ],
  782. vdi: [
  783. 'vdi',
  784. {
  785. initialValue: initData.vdi || '',
  786. },
  787. ],
  788. vga: [
  789. 'vga',
  790. {
  791. initialValue: initData.vga || '',
  792. },
  793. ],
  794. machine: [
  795. 'machine',
  796. {
  797. initialValue: initData.machine || '',
  798. },
  799. ],
  800. backup: {
  801. backupEnable: [
  802. 'backupEnable',
  803. {
  804. valuePropName: 'checked',
  805. initialValue: !!initData.prefer_backup_host || false,
  806. },
  807. ],
  808. backup: [
  809. 'backup',
  810. {
  811. initialValue: initData.prefer_backup_host || '',
  812. },
  813. ],
  814. },
  815. duration: {
  816. durationStandard: [
  817. 'durationStandard',
  818. {
  819. initialValue: initData.duration ? 'custom' : 'none',
  820. },
  821. ],
  822. duration: [
  823. 'duration',
  824. {
  825. initialValue: initData.duration === '1w' ? '7d' : initData.duration || '1h',
  826. },
  827. ],
  828. },
  829. groups: {
  830. groupsEnable: [
  831. 'groupsEnable',
  832. {
  833. valuePropName: 'checked',
  834. initialValue: !!(initData.groups && initData.groups.length),
  835. },
  836. ],
  837. groups: [
  838. 'groups',
  839. {
  840. initialValue: initData.groups || [],
  841. },
  842. ],
  843. },
  844. bill: {
  845. billType: [
  846. 'billType',
  847. {
  848. initialValue: initData.extraData?.billType || 'quantity',
  849. },
  850. ],
  851. duration: [
  852. 'duration',
  853. {
  854. initialValue: initData.duration === '1w' ? '7d' : initData.duration || '1m',
  855. },
  856. ],
  857. autoRenew: [
  858. 'autoRenew',
  859. {
  860. initialValue: initData.auto_renew || false,
  861. valuePropName: 'checked',
  862. },
  863. ],
  864. },
  865. resourceType: [
  866. 'resourceType',
  867. {
  868. preserve: true,
  869. initialValue: RESOURCE_TYPES_MAP.shared.key,
  870. },
  871. ],
  872. eip: {
  873. type: [
  874. 'eip_type',
  875. {
  876. initialValue: initEipType,
  877. },
  878. ],
  879. charge_type: [
  880. 'eip_charge_type',
  881. {
  882. initialValue: initData.eip_charge_type || initData.public_ip_charge_type,
  883. },
  884. ],
  885. bgp_type: [
  886. 'eip_bgp_type',
  887. {
  888. initialValue: initData.eip_bgp_type || initData.public_ip_bgp_type,
  889. },
  890. ],
  891. bandwidth: [
  892. 'eip_bw',
  893. {
  894. initialValue: initData.eip_bw || initData.public_ip_bw || 30,
  895. },
  896. ],
  897. eip: [
  898. 'eip',
  899. {
  900. rules: [
  901. { required: true, message: i18n.t('compute.text_145') },
  902. ],
  903. },
  904. ],
  905. },
  906. secgroup: {
  907. type: [
  908. 'secgroup_type',
  909. {
  910. initialValue: initSecgroupType,
  911. },
  912. ],
  913. secgroup: [
  914. 'secgroup',
  915. {
  916. validateFirst: true,
  917. initialValue: initData.secgroups || [],
  918. rules: [
  919. { required: true, message: i18n.t('compute.text_190') },
  920. // { validator: secgroupValidator }, // Azure和Ucloud的安全组最大关联一个,动态效验放在组件内部
  921. ],
  922. },
  923. ],
  924. },
  925. tag: [
  926. 'tag',
  927. {
  928. initialValue: initData.__meta__ || {},
  929. rules: [
  930. { validator: validateForm('tagName') },
  931. ],
  932. },
  933. ],
  934. servertemplate: {
  935. servertemplate_name: [
  936. 'servertemplate_name',
  937. {
  938. validateFirst: true,
  939. rules: [
  940. { required: true, message: i18n.t('compute.text_220') },
  941. { validator: validateForm('resourceName') },
  942. ],
  943. },
  944. ],
  945. description: [
  946. 'description',
  947. ],
  948. },
  949. os_arch: [
  950. 'os_arch',
  951. {
  952. initialValue: initData.os_arch || '',
  953. rules: [
  954. { required: true, message: i18n.t('compute.text_1363') },
  955. ],
  956. },
  957. ],
  958. hostName: [
  959. 'hostName',
  960. {
  961. initialValue: initData.hostname || '',
  962. },
  963. ],
  964. encrypt_keys: {
  965. encryptEnable: [
  966. 'encryptEnable',
  967. {
  968. initialValue: initEncryptEnable || '',
  969. },
  970. ],
  971. encrypt_key_alg: [
  972. 'encrypt_key_alg',
  973. {
  974. initialValue: initData.encrypt_key_alg || '',
  975. },
  976. ],
  977. encrypt_key_id: [
  978. 'encrypt_key_id',
  979. {
  980. initialValue: initData.encrypt_key_id || '',
  981. },
  982. ],
  983. },
  984. custom_data_type: [
  985. 'custom_data_type',
  986. {
  987. initialValue: initData.user_data ? 'input' : '',
  988. },
  989. ],
  990. deploy_telegraf: [
  991. 'deploy_telegraf',
  992. {
  993. valuePropName: 'checked',
  994. initialValue: !(initData && initData.extraData && !initData.deploy_telegraf),
  995. },
  996. ],
  997. bastion_host: {
  998. bastion_host_enable: [
  999. 'bastion_host_enable',
  1000. {
  1001. valuePropName: 'checked',
  1002. initialValue: !!initData.bastion_server,
  1003. },
  1004. ],
  1005. bastion_host_id: [
  1006. 'bastion_host_id',
  1007. {
  1008. initialValue: initData.bastion_server?.bastion_host_id || '',
  1009. rules: [
  1010. { required: true, message: i18n.t('compute.bastionHost.bastion_host.placeholder') },
  1011. ],
  1012. },
  1013. ],
  1014. bastion_org_id: [
  1015. 'bastion_org_id',
  1016. {
  1017. initialValue: initData.bastion_server?.bastion_org_id || '',
  1018. rules: [
  1019. { required: true, message: i18n.t('compute.bastionHost.bastion_org.placeholder') },
  1020. ],
  1021. },
  1022. ],
  1023. nodes: [
  1024. 'nodes',
  1025. {
  1026. initialValue: initData.bastion_server?.nodes || [],
  1027. rules: [
  1028. { required: true, message: i18n.t('compute.bastionHost.node.placeholder') },
  1029. ],
  1030. },
  1031. ],
  1032. port: [
  1033. 'port',
  1034. {
  1035. initialValue: initData.bastion_server?.port || 22,
  1036. rules: [
  1037. { type: 'number', min: 0, max: 65535, message: i18n.t('compute.bastionHost.port.placeholder'), trigger: 'blur', transform: (v) => parseInt(v) },
  1038. ],
  1039. },
  1040. ],
  1041. privileged_accounts: [
  1042. 'privileged_accounts',
  1043. {
  1044. rules: [
  1045. { required: true, message: i18n.t('compute.bastionHost.privileged_account.placeholder') },
  1046. ],
  1047. },
  1048. ],
  1049. accounts: [
  1050. 'accounts',
  1051. {
  1052. rules: [
  1053. { required: true, message: i18n.t('compute.bastionHost.account.placeholder') },
  1054. ],
  1055. },
  1056. ],
  1057. bastion_domain_id: [
  1058. 'bastion_domain_id',
  1059. {
  1060. initialValue: initData.bastion_server?.bastion_domain_id || '',
  1061. },
  1062. ],
  1063. },
  1064. is_daemon: [
  1065. 'is_daemon',
  1066. {
  1067. valuePropName: 'checked',
  1068. initialValue: initData.is_daemon || false,
  1069. },
  1070. ],
  1071. }
  1072. }
  1073. const decoratorGroup = {
  1074. idc: ['domain', 'project', 'cloudregionZone', 'name', 'description', 'reason', 'count', 'imageOS', 'loginConfig', 'hypervisor', 'gpu', 'vcpu', 'vmem', 'sku', 'kickstart', 'systemDisk', 'dataDisk', 'network', 'secgroup', 'schedPolicy', 'bios', 'vdi', 'vga', 'machine', 'backup', 'duration', 'groups', 'tag', 'servertemplate', 'eip', 'os_arch', 'hostName', 'encrypt_keys', 'custom_data_type', 'deploy_telegraf', 'pci', 'bastion_host', 'is_daemon'],
  1075. public: ['domain', 'project', 'name', 'description', 'count', 'imageOS', 'reason', 'loginConfig', 'vcpu', 'vmem', 'sku', 'systemDisk', 'dataDisk', 'network', 'schedPolicy', 'bill', 'eip', 'secgroup', 'resourceType', 'tag', 'servertemplate', 'duration', 'cloudprovider', 'hostName', 'custom_data_type', 'bastion_host'],
  1076. private: ['domain', 'project', 'cloudregionZone', 'name', 'description', 'reason', 'count', 'imageOS', 'loginConfig', 'hypervisor', 'vcpu', 'vmem', 'sku', 'systemDisk', 'dataDisk', 'network', 'secgroup', 'schedPolicy', 'duration', 'tag', 'servertemplate', 'cloudprovider', 'hostName', 'custom_data_type', 'bastion_host', 'pci'],
  1077. }
  1078. export class Decorator {
  1079. decorators = {}
  1080. constructor (type) {
  1081. this.type = type
  1082. }
  1083. createDecorators (initData) {
  1084. const decoratorArr = decoratorGroup[this.type]
  1085. if (decoratorArr) {
  1086. decoratorArr.forEach(dec => {
  1087. const decorators = createVmDecorators(this.type, initData)
  1088. if (decorators[dec]) {
  1089. this.decorators[dec] = decorators[dec]
  1090. } else {
  1091. console.error(i18n.t('compute.text_221', [dec]))
  1092. }
  1093. })
  1094. }
  1095. return this.decorators
  1096. }
  1097. }
  1098. /**
  1099. * 根据表单拼装创建参数
  1100. *
  1101. * @export
  1102. * @class GenCreateData
  1103. */
  1104. export class GenCreateData {
  1105. constructor (fd, fi) {
  1106. if (R.isNil(fd)) return
  1107. this.fd = fd
  1108. this.fi = fi
  1109. this.createType = this.fi.createType
  1110. this.isPublic = this.createType === SERVER_TYPE.public
  1111. this.isIDC = this.createType === SERVER_TYPE.idc
  1112. this.isPrepaid = this.fd.resourceType === RESOURCE_TYPES_MAP.prepaid.key
  1113. }
  1114. /**
  1115. * 拼装磁盘数据
  1116. *
  1117. * @param { Object } item // 磁盘数据
  1118. * @param { String } type // 磁盘类型 sys | data
  1119. * @param { Number } index // 序号
  1120. * @returns { Object }
  1121. * @memberof GenCreateData
  1122. */
  1123. genDisk (item, type, index) {
  1124. const ret = {
  1125. disk_type: type,
  1126. index,
  1127. backend: item.type === STORAGE_AUTO.key ? '' : item.type,
  1128. size: item.size * 1024,
  1129. }
  1130. if (type === 'sys' && this.fd.imageType !== IMAGES_TYPE_MAP.iso.key && this.fd.hypervisor !== HYPERVISORS_MAP.proxmox.key && this.fd.hypervisor !== HYPERVISORS_MAP.sangfor.key && this.fd.hypervisor !== HYPERVISORS_MAP.uis.key) {
  1131. if (this.fd.image && this.fd.image.key) {
  1132. ret.image_id = this.fd.image.key
  1133. }
  1134. }
  1135. if (type === 'sys' && this.fd.imageType === IMAGES_TYPE_MAP.iso.key && this.isWindows()) {
  1136. ret.driver = 'ide'
  1137. }
  1138. if (item.medium) {
  1139. ret.medium = item.medium
  1140. }
  1141. if (item.schedtags) {
  1142. ret.schedtags = item.schedtags
  1143. }
  1144. if (item.filetype) {
  1145. ret.fs = item.filetype
  1146. if (item.filetype !== 'swap') {
  1147. ret.mountpoint = item.mountpoint
  1148. }
  1149. }
  1150. if (item.snapshot_id) {
  1151. ret.snapshot_id = item.snapshot_id
  1152. }
  1153. if (item.storage_id) {
  1154. ret.storage_id = item.storage_id
  1155. }
  1156. if (item.iops) {
  1157. ret.iops = item.iops
  1158. }
  1159. if (item.throughput) {
  1160. ret.throughput = item.throughput
  1161. }
  1162. if (item.preallocation) {
  1163. ret.preallocation = item.preallocation
  1164. }
  1165. // 磁盘区分介质
  1166. if (diskSupportTypeMedium(this.fd.hypervisor)) {
  1167. ret.backend = getOriginDiskKey(ret.backend)
  1168. }
  1169. if (item.auto_reset) {
  1170. ret.auto_reset = true
  1171. }
  1172. return ret
  1173. }
  1174. isWindows () {
  1175. let isWindows = false
  1176. const osType = (_.get(this.fi, 'imageMsg.info.properties.os_type') || '').toLowerCase()
  1177. const os = (_.get(this.fd, 'os') || '').toLowerCase()
  1178. if (~[osType, os].indexOf('windows')) {
  1179. isWindows = true
  1180. }
  1181. return isWindows
  1182. }
  1183. _getDataDiskType (dataDiskTypes) {
  1184. if (!R.isNil(dataDiskTypes) && !R.isEmpty(dataDiskTypes)) {
  1185. const firstKey = Object.keys(dataDiskTypes)[0]
  1186. if (firstKey && dataDiskTypes[firstKey]) {
  1187. return dataDiskTypes[firstKey].key
  1188. }
  1189. }
  1190. }
  1191. _genDisksArr () {
  1192. const sysDiskType = this.fd.systemDiskType.key
  1193. let dataDiskType = this._getDataDiskType(this.fd.dataDiskTypes)
  1194. const systemDisk = {
  1195. type: sysDiskType,
  1196. size: this.fd.systemDiskSize,
  1197. }
  1198. // 磁盘介质
  1199. if (this.fi.systemDiskMedium) {
  1200. systemDisk.medium = this.fi.systemDiskMedium
  1201. }
  1202. if (this.fd.systemDiskSchedtag) {
  1203. systemDisk.schedtags = [
  1204. { id: this.fd.systemDiskSchedtag },
  1205. ]
  1206. if (this.fd.systemDiskPolicy && this.fd.systemDiskPolicy) {
  1207. systemDisk.schedtags[0].strategy = this.fd.systemDiskPolicy
  1208. }
  1209. }
  1210. if (this.fd.systemDiskStorage) {
  1211. systemDisk.storage_id = this.fd.systemDiskStorage
  1212. }
  1213. if (this.fd.systemDiskIops) {
  1214. systemDisk.iops = this.fd.systemDiskIops
  1215. }
  1216. if (this.fd.systemDiskThroughput) {
  1217. systemDisk.throughput = this.fd.systemDiskThroughput
  1218. }
  1219. if (this.fd.systemDiskPreallocation) {
  1220. systemDisk.preallocation = this.fd.systemDiskPreallocation
  1221. }
  1222. if (this.fd.systemDiskAutoReset) {
  1223. systemDisk.auto_reset = this.fd.systemDiskAutoReset
  1224. }
  1225. // #7356 新建vmware主机,数据盘没有传磁盘类型字段
  1226. if (this.fd.hypervisor === HYPERVISORS_MAP.esxi.key) {
  1227. dataDiskType = dataDiskType || sysDiskType
  1228. }
  1229. const dataDisk = []
  1230. R.forEachObjIndexed((value, key) => {
  1231. const diskObj = {
  1232. size: value,
  1233. type: dataDiskType,
  1234. }
  1235. if (this.fd.provider === HYPERVISORS_MAP.aliyun.provider) {
  1236. if (this.fd.dataDiskTypes && this.fd.dataDiskTypes[key]) {
  1237. diskObj.type = this.fd.dataDiskTypes[key].key
  1238. }
  1239. }
  1240. if (this.fd.dataDiskFiletypes && this.fd.dataDiskFiletypes[key]) {
  1241. diskObj.filetype = this.fd.dataDiskFiletypes[key]
  1242. }
  1243. if (this.fd.dataDiskMountPaths && this.fd.dataDiskMountPaths[key]) {
  1244. diskObj.mountpoint = this.fd.dataDiskMountPaths[key]
  1245. }
  1246. if (this.fd.dataDiskSnapshots && this.fd.dataDiskSnapshots[key]) {
  1247. diskObj.snapshot_id = this.fd.dataDiskSnapshots[key]
  1248. }
  1249. if (this.fd.dataDiskSchedtags && this.fd.dataDiskSchedtags[key]) {
  1250. diskObj.schedtags = [
  1251. { id: this.fd.dataDiskSchedtags[key] },
  1252. ]
  1253. if (this.fd.dataDiskPolicys && this.fd.dataDiskPolicys[key]) {
  1254. diskObj.schedtags[0].strategy = this.fd.dataDiskPolicys[key]
  1255. }
  1256. }
  1257. if (this.fd.dataDiskStorages && this.fd.dataDiskStorages[key]) {
  1258. // if system disk specifies storage, the data disks should do the same
  1259. diskObj.storage_id = this.fd.dataDiskStorages[key] || this.fd.systemDiskStorage
  1260. }
  1261. if (this.fd.dataDiskIops && this.fd.dataDiskIops[key]) {
  1262. diskObj.iops = this.fd.dataDiskIops[key]
  1263. }
  1264. if (this.fd.dataDiskThroughputs && this.fd.dataDiskThroughputs[key]) {
  1265. diskObj.throughput = this.fd.dataDiskThroughputs[key]
  1266. }
  1267. if (this.fi.dataDiskMedium) {
  1268. diskObj.medium = this.fi.dataDiskMedium
  1269. }
  1270. if (this.fd.dataDiskPreallocation && this.fd.dataDiskPreallocation[key]) {
  1271. diskObj.preallocation = this.fd.dataDiskPreallocation[key]
  1272. }
  1273. if (this.fd.dataDiskAutoReset && this.fd.dataDiskAutoReset[key]) {
  1274. diskObj.auto_reset = this.fd.dataDiskAutoReset[key]
  1275. }
  1276. dataDisk.push(diskObj)
  1277. }, this.fd.dataDiskSizes)
  1278. const disks = { data: dataDisk, system: systemDisk }
  1279. return disks
  1280. }
  1281. /**
  1282. * 组装所有磁盘数据,包含系统盘及数据盘
  1283. *
  1284. * @returns { Array }
  1285. * @memberof GenCreateData
  1286. */
  1287. genDisks () {
  1288. const disks = this._genDisksArr()
  1289. if (this.isPublic && this.isPrepaid) {
  1290. return this.fd.spec.disks
  1291. }
  1292. const ret = [this.genDisk(disks.system, 'sys', 0)]
  1293. for (let i = 0, len = disks.data.length; i < len; i++) {
  1294. ret.push(this.genDisk(disks.data[i], 'data', i + 1))
  1295. }
  1296. return ret
  1297. }
  1298. /**
  1299. * 组装所有网络数据
  1300. *
  1301. * @returns { Array }
  1302. * @memberof GenCreateData
  1303. */
  1304. genNetworks () {
  1305. let ret = [{ exit: false }]
  1306. const extraRet = []
  1307. // 指定 IP 子网
  1308. if (this.fd.networkType === NETWORK_OPTIONS_MAP.manual.key) {
  1309. ret = []
  1310. R.forEachObjIndexed((value, key) => {
  1311. const obj = {
  1312. network: value,
  1313. }
  1314. const extraObj = {
  1315. vpc: this.fd[`vpcs[${key}]`] || this.fd.vpcs[key],
  1316. }
  1317. if (this.fd.networkIps) {
  1318. const address = this.fd.networkIps[key]
  1319. if (address) {
  1320. obj.address = address
  1321. }
  1322. }
  1323. if (this.fd.networkMacs) {
  1324. const mac = this.fd.networkMacs[key]
  1325. if (mac) {
  1326. obj.mac = mac
  1327. }
  1328. }
  1329. if (this.fd.networkExits) {
  1330. const exit = this.fd.networkExits[key]
  1331. if (exit) {
  1332. obj.exit = true
  1333. }
  1334. }
  1335. if (this.fd.networkSecgroups) {
  1336. const secgroup = this.fd.networkSecgroups[key]
  1337. if (secgroup) {
  1338. obj.secgroups = secgroup
  1339. }
  1340. }
  1341. if (this.fd.networkIPv6s) {
  1342. const ipv6 = this.fd.networkIPv6s[key]
  1343. if (ipv6) {
  1344. obj.require_ipv6 = true
  1345. }
  1346. }
  1347. const target = this.fi.networkList.filter(item => item.key === key)
  1348. if (this.fd.networkIpsAddress6) {
  1349. const ipv6Last = this.fd.networkIpsAddress6[key]
  1350. const ipv6First = getIpv6Start(target[0]?.network?.guest_ip6_start)
  1351. obj.address6 = ipv6First + ipv6Last
  1352. }
  1353. if (obj.require_ipv6) {
  1354. if (this.fd.networkIPv6Modes && this.fd.networkIPv6Modes[key] === 'only') {
  1355. obj.strict_ipv6 = true
  1356. }
  1357. }
  1358. if (!target[0]?.network?.guest_ip_start && !target[0]?.network?.guest_ip_end && target[0]?.network?.guest_ip6_start) {
  1359. obj.strict_ipv6 = true
  1360. }
  1361. if (this.fd.networkDevices) {
  1362. const device = this.fd.networkDevices[key]
  1363. if (device) {
  1364. obj.sriov_device = { model: device }
  1365. }
  1366. }
  1367. if (this.fd.networkSecgroups) {
  1368. const secgroup = this.fd.networkSecgroups[key]
  1369. if (secgroup) {
  1370. obj.secgroups = secgroup
  1371. }
  1372. }
  1373. ret.push(obj)
  1374. extraRet.push({ ...obj, ...extraObj })
  1375. }, this.fd.networks)
  1376. }
  1377. // 指定 调度标签
  1378. if (this.fd.networkType === NETWORK_OPTIONS_MAP.schedtag.key) {
  1379. ret = []
  1380. R.forEachObjIndexed((value, key) => {
  1381. const obj = {
  1382. id: value,
  1383. }
  1384. const extraObj = {}
  1385. const strategy = this.fd.networkPolicys[key]
  1386. if (strategy) {
  1387. obj.strategy = strategy
  1388. }
  1389. if (this.fd.networkDevices) {
  1390. const device = this.fd.networkDevices[key]
  1391. if (device) {
  1392. obj.sriov_device = { model: device }
  1393. }
  1394. }
  1395. ret.push({
  1396. schedtags: [obj],
  1397. })
  1398. extraRet.push({ schedtags: [{ ...obj, ...extraObj }] })
  1399. }, this.fd.networkSchedtags)
  1400. }
  1401. return { networks: ret, extraNetworks: extraRet }
  1402. }
  1403. /**
  1404. * 获取配置的GPU数据
  1405. *
  1406. * @returns { Array }
  1407. * @memberof GenCreateData
  1408. */
  1409. genDevices () {
  1410. const ret = []
  1411. for (let i = 0, len = this.fd.gpuCount; i < len; i++) {
  1412. const regexp = /vendor=(.+):(.+)/
  1413. const matched = this.fd.gpu.match(regexp)
  1414. const model = matched[2]
  1415. const vendor = matched[1]
  1416. ret.push({
  1417. model,
  1418. vendor,
  1419. })
  1420. }
  1421. return ret
  1422. }
  1423. /**
  1424. * 获取配置的PCI数据
  1425. *
  1426. * @returns { Array }
  1427. * @memberof GenCreateData
  1428. */
  1429. genPciDevices () {
  1430. const ret = []
  1431. const { pciCount, pciModel } = this.fd
  1432. const pciKeys = Object.keys(this.fd.pciCount)
  1433. pciKeys.forEach(key => {
  1434. for (let i = 0, len = pciCount[key]; i < len; i++) {
  1435. const regexp = /vendor=(.+):(.+)/
  1436. const matched = pciModel[key].match(regexp)
  1437. const model = matched[2]
  1438. const vendor = matched[1]
  1439. ret.push({
  1440. model,
  1441. vendor,
  1442. })
  1443. }
  1444. })
  1445. return ret
  1446. }
  1447. /**
  1448. * 获取管理员密码所提交的 key 与 value
  1449. *
  1450. * @returns { String }
  1451. * @memberof GenCreateData
  1452. */
  1453. getLoginValueKey () {
  1454. const ret = {}
  1455. switch (this.fd.loginType) {
  1456. case LOGIN_TYPES_MAP.keypair.key:
  1457. ret.key = 'keypair'
  1458. ret.value = this.fd.loginKeypair
  1459. break
  1460. case LOGIN_TYPES_MAP.image.key:
  1461. ret.key = 'reset_password'
  1462. ret.value = false
  1463. break
  1464. case LOGIN_TYPES_MAP.password.key:
  1465. ret.key = 'password'
  1466. ret.value = this.fd.loginPassword
  1467. break
  1468. default:
  1469. break
  1470. }
  1471. return ret
  1472. }
  1473. /**
  1474. * 获取调度策略所提交的 key 与 value
  1475. *
  1476. * @returns
  1477. * @memberof GenCreateData
  1478. */
  1479. getSchedPolicyValueKey () {
  1480. const ret = {}
  1481. // 调度策略选择为 指定宿主机
  1482. if (this.fd.schedPolicyType === SCHED_POLICY_OPTIONS_MAP.host.key) {
  1483. if (this.isPublic) {
  1484. ret.key = 'prefer_manager'
  1485. } else {
  1486. ret.key = 'prefer_host'
  1487. }
  1488. ret.value = this.fd.schedPolicyHost
  1489. } else if (this.showPreferManager()) { // 如果是通过云账号过滤镜像
  1490. ret.key = 'prefer_manager'
  1491. ret.value = this.fd.prefer_manager
  1492. }
  1493. // 调度策略选择为 云账号
  1494. // if (this.fd.schedPolicyType === SCHED_POLICY_OPTIONS_MAP.cloudprovider.key) {
  1495. // ret.key = 'prefer_manager'
  1496. // ret.value = this.fd.cloudprovider
  1497. // }
  1498. // 调度策略选择为 调度标签
  1499. if (this.fd.schedPolicyType === SCHED_POLICY_OPTIONS_MAP.schedtag.key) {
  1500. ret.key = 'schedtags'
  1501. ret.value = []
  1502. R.forEachObjIndexed((value, key) => {
  1503. const item = { id: value }
  1504. if (this.fd.policySchedtagPolicys[key]) {
  1505. item.strategy = this.fd.policySchedtagPolicys[key]
  1506. }
  1507. ret.value.push(item)
  1508. }, this.fd.policySchedtagSchedtags)
  1509. }
  1510. return ret
  1511. }
  1512. /**
  1513. * 获取平台
  1514. *
  1515. * @returns { String }
  1516. * @memberof GenCreateData
  1517. */
  1518. getHypervisor () {
  1519. let ret = this.fd.hypervisor
  1520. if (this.isPublic && !this.isPrepaid) {
  1521. const provider = this.fd.sku.provider
  1522. if (provider) ret = provider.toLowerCase()
  1523. }
  1524. return ret
  1525. }
  1526. /**
  1527. * 是否是通过云账号过滤后选择的镜像
  1528. *
  1529. * @returns { String }
  1530. * @memberof GenCreateData
  1531. */
  1532. showPreferManager () {
  1533. const imageMsg = IMAGES_TYPE_MAP[this.fd.imageType]
  1534. return imageMsg && imageMsg.enable_cloudaccount
  1535. }
  1536. /**
  1537. * 获取Region
  1538. *
  1539. * @returns { String }
  1540. * @memberof GenCreateData
  1541. */
  1542. getPreferRegion () {
  1543. if (this.isPublic && !this.isPrepaid) {
  1544. return this.fd.sku.cloudregion_id
  1545. }
  1546. return this.fd.cloudregion.key
  1547. }
  1548. /**
  1549. * 获取Zone
  1550. *
  1551. * @returns { String }
  1552. * @memberof GenCreateData
  1553. */
  1554. getPreferZone () {
  1555. let ret = ''
  1556. if (R.is(Object, this.fd.zone)) {
  1557. ret = this.fd.zone.key
  1558. }
  1559. if (R.is(String, this.fd.zone)) { // 字符串形式是公有云 AreaSelect 的 zone
  1560. ret = this.fd.zone
  1561. }
  1562. return ret
  1563. }
  1564. /**
  1565. * 获取CPU核数
  1566. *
  1567. * @returns { String }
  1568. * @memberof GenCreateData
  1569. */
  1570. getCpuCount () {
  1571. let ret = this.fd.vcpu
  1572. if (this.isPublic && this.isPrepaid) {
  1573. ret = this.fd.spec.vcpu_count
  1574. }
  1575. return ret
  1576. }
  1577. /**
  1578. * 获取CPU插槽数
  1579. * @returns
  1580. */
  1581. getCpuSockets () {
  1582. return this.fi.cpuSockets
  1583. }
  1584. /**
  1585. * 获取内存
  1586. *
  1587. * @returns { String }
  1588. * @memberof GenCreateData
  1589. */
  1590. getMemSize () {
  1591. let ret = this.fd.vmem
  1592. if (this.isPublic && this.isPrepaid) {
  1593. ret = this.fd.spec.vmem_size * 1024
  1594. }
  1595. return ret
  1596. }
  1597. getPreferManagerId () {
  1598. let prefer_manager_id = ''
  1599. const hypervisor = this.getHypervisor()
  1600. const privateHypervisors = [HYPERVISORS_MAP.hcso.key, HYPERVISORS_MAP.hcs.key]
  1601. if (this.isPublic || privateHypervisors.includes(hypervisor)) {
  1602. prefer_manager_id = this.fd.cloudprovider
  1603. }
  1604. return prefer_manager_id
  1605. }
  1606. /**
  1607. * 组装所有的创建数据
  1608. *
  1609. * @returns { Object }
  1610. * @memberof GenCreateData
  1611. */
  1612. all () {
  1613. const { create_server_auto_start = true } = store.getters.globalSetting.value || {}
  1614. const { networks, extraNetworks } = this.genNetworks()
  1615. const data = {
  1616. auto_start: create_server_auto_start,
  1617. generate_name: this.fd.name && this.fd.name.trim(),
  1618. description: this.fd.description,
  1619. hypervisor: this.getHypervisor(),
  1620. __count__: this.fd.count,
  1621. disks: this.genDisks(),
  1622. nets: networks,
  1623. prefer_region: this.getPreferRegion(),
  1624. vcpu_count: this.getCpuCount(),
  1625. vmem_size: this.getMemSize(),
  1626. project_id: (this.fd.project && this.fd.project.key) || store.getters.userInfo.projectId,
  1627. os_arch: _.get(HOST_CPU_ARCHS, `[${this.fd.os_arch}].key`),
  1628. hostname: this.fd.hostName,
  1629. extraData: {
  1630. domain_id: (this.fd.domain && this.fd.domain.key) || store.getters.userInfo.domainId,
  1631. image_type: this.fd.imageType,
  1632. os: this.fd.os,
  1633. nets: extraNetworks,
  1634. },
  1635. }
  1636. // 非预付费资源池不会添加sku
  1637. if (!this.isPrepaid) {
  1638. data.sku = this.fd.sku.name
  1639. }
  1640. // 弹性IP
  1641. if (this.isPublic || this.isIDC) {
  1642. if (this.fd.eip_type === EIP_TYPES_MAP.new.key || this.fd.eip_type === EIP_TYPES_MAP.public.key) {
  1643. if (
  1644. this.fd.eip_charge_type === EIP_CHARGE_TYPES_MAP.traffic.key ||
  1645. this.fd.eip_charge_type === EIP_CHARGE_TYPES_MAP.bandwidth.key
  1646. ) {
  1647. if (this.fd.eip_type === EIP_TYPES_MAP.public.key) {
  1648. data.public_ip_charge_type = this.fd.eip_charge_type
  1649. data.public_ip_bw = this.fd.eip_bw
  1650. } else {
  1651. data.eip_charge_type = this.fd.eip_charge_type
  1652. data.eip_bw = this.fd.eip_bw
  1653. }
  1654. }
  1655. }
  1656. if (this.fd.eip_type === EIP_TYPES_MAP.bind.key) {
  1657. data.eip = this.fd.eip
  1658. }
  1659. // 包年包月参数
  1660. if (this.fd.billType === BILL_TYPES_MAP.package.key) {
  1661. data.duration = this.fd.duration
  1662. // 自动续费
  1663. data.auto_renew = this.fd.autoRenew
  1664. data.extraData.billType = 'package'
  1665. }
  1666. // 线路类型
  1667. if (this.fd.eip_bgp_type) {
  1668. data.eip_bgp_type = this.fd.eip_bgp_type
  1669. }
  1670. }
  1671. // gpu
  1672. // if (this.fd.gpuEnable) {
  1673. // data.isolated_devices = this.genDevices()
  1674. // }
  1675. // pci
  1676. if (this.fd.pciEnable) {
  1677. data.isolated_devices = this.genPciDevices()
  1678. }
  1679. // 管理员密码非默认的情况下进行传参设置
  1680. if (this.fd.loginType !== LOGIN_TYPES_MAP.random.key) {
  1681. const loginValueKey = this.getLoginValueKey()
  1682. data[loginValueKey.key] = loginValueKey.value
  1683. }
  1684. // 安全组
  1685. if (this.fd.secgroup_type && this.fd.secgroup_type === SECGROUP_OPTIONS_MAP.bind.key) {
  1686. data.secgroups = this.fd.secgroup
  1687. }
  1688. // 如果设置了调度策略则拼装调度所需数据 或者 通过云账号过滤镜像
  1689. if ((this.fd.schedPolicyType !== SCHED_POLICY_OPTIONS_MAP.default.key) || this.showPreferManager()) {
  1690. const schedPolicyValueKey = this.getSchedPolicyValueKey()
  1691. data[schedPolicyValueKey.key] = schedPolicyValueKey.value
  1692. }
  1693. // 是否需要 拼装 高可用备份机
  1694. if (this.fd.backupEnable && this.fd.backup) {
  1695. data.backup = true
  1696. data.prefer_backup_host = this.fd.backup
  1697. }
  1698. // zone
  1699. const zoneId = this.getPreferZone()
  1700. if (zoneId) {
  1701. data.prefer_zone = zoneId
  1702. }
  1703. const prefer_manager_id = this.getPreferManagerId()
  1704. if (prefer_manager_id) {
  1705. data.prefer_manager_id = prefer_manager_id
  1706. }
  1707. // 只有kvm支持启动方式, VDI, VGA, Machine
  1708. if (this.fd.hypervisor === HYPERVISORS_MAP.kvm.key) {
  1709. data.bios = this.fd.bios
  1710. data.vdi = this.fd.vdi
  1711. data.vga = this.fd.vga
  1712. data.machine = this.fd.machine
  1713. }
  1714. // 到期释放
  1715. if (this.fd.billType !== BILL_TYPES_MAP.package.key && this.fd.durationStandard !== 'none') {
  1716. if (this.fd.durationStandard === 'custom') {
  1717. data.duration = this.fd.duration
  1718. } else {
  1719. data.duration = this.fd.durationStandard
  1720. }
  1721. data.billing_type = 'postpaid'
  1722. }
  1723. // 镜像类型为 iso 需要加参数 cdrom
  1724. if (this.fd.imageType === IMAGES_TYPE_MAP.iso.key || this.fd.imageType === IMAGES_TYPE_MAP.private_iso.key) {
  1725. data.cdrom = this.fd.image.key
  1726. }
  1727. // 主机镜像需要guest image id参数,并且把磁盘中的镜像ID回填回去
  1728. if (this.fd.imageType === IMAGES_TYPE_MAP.host.key) {
  1729. data.guest_image_id = this.fd.image.key
  1730. data.disks.forEach((val, i) => {
  1731. if (i === 0) { // 系统盘
  1732. data.disks[i] = { ...val, image_id: this.fi.imageMsg.root_image.id }
  1733. } else if (this.fi.imageMsg.data_images && i - 1 < this.fi.imageMsg.data_images.length) {
  1734. data.disks[i] = { ...val, image_id: this.fi.imageMsg.data_images[i - 1].id }
  1735. }
  1736. })
  1737. }
  1738. // cloudpods iso 镜像需加参数 cdrom,并移除磁盘中的image_id参数
  1739. if (this.fi.imageMsg?.disk_format === 'iso' || this.fi.imageMsg?.info?.disk_format === 'iso' || (this.fd.image?.label || '').endsWith('.iso')) {
  1740. const { hypervisor_info = {} } = this.fi.capability || {}
  1741. const keys = Object.keys(hypervisor_info)
  1742. if (keys.length === 1 && keys[0] === HYPERVISORS_MAP.cloudpods.provider) {
  1743. data.cdrom = this.fd.image.key
  1744. data.disks = (data.disks || []).map(item => {
  1745. const ret = { ...item }
  1746. delete ret.image_id
  1747. return ret
  1748. })
  1749. }
  1750. }
  1751. // 主机快照需要instance_snapshot_id参数
  1752. if (this.fd.imageType === IMAGES_TYPE_MAP.snapshot.key) {
  1753. data.instance_snapshot_id = this.fd.image.key
  1754. delete data.disks // 主机快照不需要 disks 字段
  1755. } else if (this.fd.imageType === IMAGES_TYPE_MAP.backup.key) {
  1756. data.instance_backup_id = this.fd.image.key
  1757. // delete data.disks // 主机备份可以保留 disks 字段
  1758. }
  1759. // 主机组
  1760. if (this.fd.groupsEnable && this.fd.groups && this.fd.groups.length) {
  1761. data.groups = this.fd.groups
  1762. }
  1763. // 标签
  1764. if (this.fd.tag) {
  1765. data.__meta__ = this.fd.tag
  1766. }
  1767. // 主机加密
  1768. if (this.fd.encryptEnable === 'existing' && this.fd.encrypt_key_id) {
  1769. data.encrypt_key_id = this.fd.encrypt_key_id
  1770. } else if (this.fd.encryptEnable === 'new') {
  1771. data.encrypt_key_new = true
  1772. data.encrypt_key_alg = this.fd.encrypt_key_alg
  1773. data.encrypt_key_user_id = store.getters.userInfo.id
  1774. }
  1775. // 自定义数据
  1776. if (this.fd.custom_data_type) {
  1777. data.custom_data_type = this.fd.custom_data_type
  1778. }
  1779. // kickstart
  1780. const { os_distribution = '' } = this.fi?.imageMsg?.properties || this.fi?.imageMsg?.info?.properties || {}
  1781. const isTargetOsType = ['centos', 'rhel', 'openeuler', 'ubuntu'].some(item => os_distribution.toLowerCase().includes(item))
  1782. if (isTargetOsType && this.fd.hypervisor === HYPERVISORS_MAP.kvm.key && this.fd.imageType === IMAGES_TYPE_MAP.iso.key) {
  1783. data.kickstart_config = {
  1784. config: this.fd.kickstart_config,
  1785. enabled: this.fd.kickstart_enabled,
  1786. os_type: os_distribution.toLowerCase(),
  1787. }
  1788. }
  1789. // 安装监控 agent
  1790. if (this.isIDC) {
  1791. if ((this.isWindows() && this.fd.os_arch !== HOST_CPU_ARCHS.arm.key) || !this.isWindows()) {
  1792. if (this.fd.deploy_telegraf) {
  1793. data.deploy_telegraf = this.fd.deploy_telegraf
  1794. }
  1795. }
  1796. }
  1797. if (this.fd.hypervisor === HYPERVISORS_MAP.kvm.key) {
  1798. if (this.fd.is_daemon) {
  1799. data.is_daemon = this.fd.is_daemon
  1800. }
  1801. }
  1802. if (this.fi.showCpuSockets) {
  1803. data.cpu_sockets = this.getCpuSockets()
  1804. }
  1805. if (this.fd.podImage) {
  1806. data.hypervisor = 'pod'
  1807. data.pod = {
  1808. containers: [
  1809. {
  1810. image: this.fd.podImage,
  1811. },
  1812. ],
  1813. }
  1814. if (data.isolated_devices) {
  1815. data.pod.containers = [
  1816. {
  1817. image: this.fd.podImage,
  1818. devices: data.isolated_devices.map((item, idx) => {
  1819. return {
  1820. type: 'isolated_device',
  1821. isolated_device: {
  1822. index: idx,
  1823. },
  1824. }
  1825. }),
  1826. },
  1827. ]
  1828. }
  1829. }
  1830. return data
  1831. }
  1832. /**
  1833. * 获取创建预测的错误信息
  1834. *
  1835. * @returns { Array }
  1836. * @memberof GenCreateData
  1837. */
  1838. getForecastErrors (data) {
  1839. const errors = []
  1840. if (data.filtered_candidates && data.filtered_candidates.length > 0) {
  1841. for (let i = 0, len = data.filtered_candidates.length; i < len; i++) {
  1842. const item = data.filtered_candidates[i]
  1843. let message = `${i18n.t('dictionary.host')}【${item.name}】`
  1844. const filterMapItem = FORECAST_FILTERS_MAP[item.filter_name]
  1845. if (filterMapItem) {
  1846. message += filterMapItem
  1847. } else {
  1848. message += i18n.t('compute.text_1325', [item.filter_name])
  1849. }
  1850. errors.push({
  1851. message,
  1852. children: item.reasons,
  1853. })
  1854. }
  1855. } else {
  1856. errors.push({
  1857. message: i18n.t('compute.text_227'),
  1858. })
  1859. }
  1860. return {
  1861. errors,
  1862. allow_count: data.allow_count || 0,
  1863. req_count: data.req_count || 1,
  1864. not_allow_reasons: data.not_allow_reasons,
  1865. }
  1866. }
  1867. }