import * as R from 'ramda' import _ from 'lodash' import ipaddr from 'ipaddr.js' import { NETWORK_OPTIONS_MAP, SERVER_TYPE, EIP_TYPES_MAP, EIP_CHARGE_TYPES_MAP, BILL_TYPES_MAP, SCHED_POLICY_OPTIONS_MAP, STORAGE_AUTO, SECGROUP_OPTIONS_MAP, FORECAST_FILTERS_MAP, RESOURCE_TYPES_MAP, } from '@Compute/constants' import { IMAGES_TYPE_MAP, HOST_CPU_ARCHS } from '@/constants/compute' import { HYPERVISORS_MAP } from '@/constants' import validateForm, { isRequired, isWithinRange } from '@/utils/validate' import store from '@/store' import i18n from '@/locales' import { removeHttp } from '@/utils/url' import { diskSupportTypeMedium, getOriginDiskKey } from '@/utils/common/hypervisor' export function getIpv6Start (ipv6) { try { const IPv6 = ipaddr.IPv6.parse(ipv6) return IPv6.toNormalizedString().split(':').slice(0, 4).join(':') + ':' } catch (err) { console.error('IPv6 address is error') return '' } } export function ipv6ToHex (ipv6) { return ipv6.parts.map(part => part.toString(16).padStart(4, '0')).join('') } export function checkIpV6 (i, networkData) { return (rule, value, cb) => { const ipv6First = getIpv6Start(networkData.guest_ip6_start) try { const ipv6 = ipv6First + value const ipAddr = ipaddr.IPv6.parse(ipv6) const subnet1Addr = ipaddr.IPv6.parse(networkData.guest_ip6_start) const subnet2Addr = ipaddr.IPv6.parse(networkData.guest_ip6_end) if (ipAddr.kind() !== 'ipv6') { cb(new Error(i18n.t('compute.error_ipv6'))) } const target = ipv6ToHex(ipAddr) const start = ipv6ToHex(subnet1Addr) const end = ipv6ToHex(subnet2Addr) // 检查IP是否在两个子网之间 if (!((target >= start && target <= end))) { cb(new Error(i18n.t('compute.ipv6_within_range'))) } cb() } catch (err) { cb(new Error(i18n.t('compute.error_ipv6'))) } } } export function checkIpInSegment (i, networkData) { return (rule, value, cb) => { const isIn = isWithinRange(value, networkData.guest_ip_start, networkData.guest_ip_end) if (isIn) { cb() } else { cb(new Error(i18n.t('compute.text_205'))) } } } export function diskValidator (rule, value, callback) { if (R.isNil(value) || R.isEmpty(value)) { return callback(new Error(i18n.t('compute.text_206'))) } if (!value.startsWith('/')) { return callback(new Error(i18n.t('compute.text_207'))) } if (value === '/') { return callback(new Error(i18n.t('compute.text_208'))) } callback() } const validateValidPath = (rule, value, callback) => { if (value.startsWith('/')) { if (value === '/') { callback(new Error(i18n.t('compute.repo.mount_point.check_content'))) } else { callback() } } else { callback(new Error(i18n.t('compute.repo.mount_point.check_start'))) } } const validateMountNames = (rule, value, callback) => { if (value === undefined) { callback(new Error(i18n.t('common.tips.select', [i18n.t('compute.repo.storage_statement')]))) } callback() } export const createVmDecorators = () => { const imageTypeInitValue = IMAGES_TYPE_MAP.standard.key return { domain: [ 'domain', { rules: [ { validator: isRequired(), message: i18n.t('rules.domain'), trigger: 'change' }, ], }, ], project: [ 'project', { rules: [ { validator: isRequired(), message: i18n.t('rules.project'), trigger: 'change' }, ], }, ], name: [ 'name', { initialValue: '', validateTrigger: 'blur', validateFirst: true, rules: [ { required: true, message: i18n.t('compute.text_210') }, ], }, ], description: [ 'description', { initialValue: '', }, ], count: [ 'count', { initialValue: 1, rules: [ { required: true, message: i18n.t('compute.text_211') }, ], }, ], cloudregionZone: { cloudregion: [ 'cloudregion', { initialValue: { key: '', label: '' }, rules: [ { validator: isRequired(), message: i18n.t('compute.text_212') }, ], }, ], zone: [ 'zone', { initialValue: { key: '', label: '' }, rules: [ { validator: isRequired(), message: i18n.t('compute.text_213') }, ], }, ], }, imageOS: { prefer_manager: [ 'prefer_manager', { rules: [ { required: true, message: i18n.t('compute.text_149') }, ], }, ], os: [ 'os', { initialValue: '', rules: [ { required: true, message: i18n.t('compute.text_153') }, ], }, ], image: [ 'image', { initialValue: { key: '', label: '' }, rules: [ { validator: isRequired(), message: i18n.t('compute.text_214') }, ], }, ], imageType: [ 'imageType', { initialValue: imageTypeInitValue, }, ], }, cloudprovider: [ 'cloudprovider', { rules: [ { required: true, message: i18n.t('compute.text_149') }, ], }, ], pci: { pciEnable: [ 'pciEnable', { valuePropName: 'checked', initialValue: false, }, ], pciDevType: i => [ `pciDevType[${i}]`, ], pciModel: i => [ `pciModel[${i}]`, { rules: [ { required: true, message: i18n.t('compute.text_147') }, ], }, ], pciCount: i => [ `pciCount[${i}]`, { initialValue: 1, }, ], }, vcpu: [ 'vcpu', { initialValue: 2, }, ], vmem: [ 'vmem', { initialValue: 2048, }, ], sku: [ 'sku', { rules: [ { validator: isRequired(true, 'id'), message: i18n.t('compute.text_216') }, ], }, ], dataDisk: { type: i => [ `dataDiskTypes[${i}]`, { rules: [ { validator: isRequired(), message: i18n.t('compute.text_121') }, ], }, ], size: i => [ `dataDiskSizes[${i}]`, { rules: [ { required: true, message: i18n.t('compute.text_122') }, ], }, ], schedtag: i => [ `dataDiskSchedtags[${i}]`, { validateTrigger: ['change', 'blur'], rules: [{ required: true, message: i18n.t('compute.text_123'), }], }, ], policy: i => [ `dataDiskPolicys[${i}]`, { initialValue: '', validateTrigger: ['blur', 'change'], rules: [{ required: true, message: i18n.t('compute.text_123'), }], }, ], snapshot: i => [ `dataDiskSnapshots[${i}]`, { validateTrigger: ['blur', 'change'], rules: [{ required: true, message: i18n.t('compute.text_124'), }], }, ], filetype: i => [ `dataDiskFiletypes[${i}]`, { validateTrigger: ['blur', 'change'], rules: [{ required: true, message: i18n.t('compute.text_125'), }], }, ], mountPath: i => [ `dataDiskMountPaths[${i}]`, { validateTrigger: ['blur', 'change'], rules: [{ required: true, message: i18n.t('compute.text_126'), }, { validator: diskValidator, }], }, ], storage: i => [ `dataDiskStorages[${i}]`, { rules: [{ required: true, message: i18n.t('compute.text_1351'), }], }, ], iops: i => [ `dataDiskIops[${i}]`, { rules: [{ required: true, message: i18n.t('compute.iops_input_tip'), }], }, ], throughput: i => [ `dataDiskThroughputs[${i}]`, { rules: [{ required: true, message: i18n.t('compute.throughput_input_tip'), }], }, ], preallocation: i => [ `dataDiskPreallocation[${i}]`, ], }, network: { networkType: [ 'networkType', { initialValue: NETWORK_OPTIONS_MAP.default.key, }, ], networkConfig: { vpcs: i => [ `vpcs[${i}]`, { validateTrigger: ['change', 'blur'], rules: [{ required: true, message: i18n.t('compute.text_194'), }], }, ], networks: i => [ `networks[${i}]`, { validateTrigger: ['change', 'blur'], rules: [{ required: true, message: i18n.t('compute.text_217'), }], }, ], ips: (i, networkData) => [ `networkIps[${i}]`, { validateFirst: true, validateTrigger: ['blur', 'change'], rules: [ { required: true, message: i18n.t('compute.text_218'), }, { validator: validateForm('IPv4'), }, { validator: checkIpInSegment(i, networkData), }, ], }, ], macs: (i, networkData) => [ `networkMacs[${i}]`, { validateFirst: true, validateTrigger: ['blur', 'change'], rules: [ { required: true, message: i18n.t('compute.text_806'), }, { validator: validateForm('mac'), }, ], }, ], ips6: (i, networkData) => [ `networkIpsAddress6[${i}]`, { validateFirst: true, validateTrigger: ['blur', 'change'], rules: [ { required: true, message: i18n.t('compute.complete_ipv6_address'), }, { validator: checkIpV6(i, networkData), }, ], }, ], ipv6_mode: (i, networkData) => [ `networkIPv6Modes[${i}]`, { validateTrigger: ['change', 'blur'], }, ], ipv6s: (i, networkData) => [ `networkIPv6s[${i}]`, { validateFirst: true, validateTrigger: ['blur', 'change'], }, ], devices: i => [ `networkDevices[${i}]`, { validateTrigger: ['change', 'blur'], rules: [{ required: true, message: i18n.t('compute.sriov_device_tips'), }], }, ], }, networkSchedtag: { schedtags: i => [ `networkSchedtags[${i}]`, { validateTrigger: ['change', 'blur'], rules: [{ required: true, message: i18n.t('compute.text_123'), }], }, ], policys: (i, networkData) => [ `networkPolicys[${i}]`, { validateTrigger: ['blur', 'change'], rules: [{ required: true, message: i18n.t('common_256'), }], }, ], devices: i => [ `networkDevices[${i}]`, { validateTrigger: ['change', 'blur'], rules: [{ required: true, message: i18n.t('compute.sriov_device_tips'), }], }, ], }, }, schedPolicy: { schedPolicyType: [ 'schedPolicyType', { initialValue: 'default', }, ], schedPolicyHost: [ 'schedPolicyHost', { rules: [ { required: true, message: i18n.t('compute.text_219') }, ], }, ], policySchedtag: { schedtags: i => [ `policySchedtagSchedtags[${i}]`, { validateTrigger: ['change', 'blur'], rules: [{ required: true, message: i18n.t('compute.text_123'), }], }, ], policys: (i, networkData) => [ `policySchedtagPolicys[${i}]`, { validateTrigger: ['blur', 'change'], rules: [{ required: true, message: i18n.t('common_256'), }], }, ], }, }, duration: { durationStandard: [ 'durationStandard', { initialValue: 'none', }, ], duration: [ 'duration', { initialValue: '1h', }, ], }, groups: { groupsEnable: [ 'groupsEnable', { valuePropName: 'checked', initialValue: false, }, ], groups: [ 'groups', ], }, bill: { billType: [ 'billType', { initialValue: 'quantity', }, ], duration: [ 'duration', { initialValue: '1M', }, ], autoRenew: [ 'autoRenew', { valuePropName: 'checked', }, ], }, resourceType: [ 'resourceType', { preserve: true, initialValue: RESOURCE_TYPES_MAP.shared.key, }, ], eip: { type: [ 'eip_type', { initialValue: 'none', }, ], charge_type: [ 'eip_charge_type', ], bgp_type: [ 'eip_bgp_type', { initialValue: '', }, ], bandwidth: [ 'eip_bw', { initialValue: 30, }, ], eip: [ 'eip', { rules: [ { required: true, message: i18n.t('compute.text_145') }, ], }, ], }, secgroup: { type: [ 'secgroup_type', { initialValue: 'default', }, ], secgroup: [ 'secgroup', { validateFirst: true, rules: [ { required: true, message: i18n.t('compute.text_190') }, ], }, ], }, tag: [ 'tag', { rules: [ { validator: validateForm('tagName') }, ], }, ], os_arch: [ 'os_arch', { rules: [ { required: true, message: i18n.t('compute.text_1363') }, ], }, ], hostName: [ 'hostName', ], portMapping: { key: i => [ `containerPorts[${i}]`, { rules: [ { required: true, message: i18n.t('common.tips.input', [i18n.t('compute.repo.container_port')]) }, ], }, ], value: i => [ `hostPorts[${i}]`, ], }, containers: { name: i => [ `containerNames[${i}]`, { rules: [ { required: true, message: `${i18n.t('common.placeholder')}${i18n.t('common.name')}` }, { validator: validateForm('k8sLabel') }, ], }, ], source: i => [ `containerSources[${i}]`, { initialValue: 'custom', }, ], registryImage: i => [ `registryImages[${i}]`, { rules: [ { required: true, message: i18n.t('common.tips.select', [i18n.t('compute.eci.repo.image.registry')]) }, ], }, ], imageCredentialId: i => [ `imageCredentialIds[${i}]`, ], image: i => [ `containerimages[${i}]`, { rules: [ { required: true, message: `${i18n.t('common.placeholder')}${i18n.t('compute.repo.container_image')}` }, ], }, ], cpu: i => [ `containerCpus[${i}]`, ], memory: i => [ `containerMemorys[${i}]`, ], command: i => [ `containerCommands[${i}]`, ], arg: i => [ `containerArgs[${i}]`, ], volumeMount: i => ({ key: j => [ `containerVolumeMountNames[${i}][${j}]`, { rules: [ { validator: validateMountNames, trigger: 'blur' }, ], }, ], value: j => [ `containerVolumeMountPaths[${i}][${j}]`, { rules: [ { required: true, message: i18n.t('common.tips.input', []) }, { validator: validateValidPath, trigger: 'blur' }, ], }, ], }), env: i => ({ key: j => [ `containerEnvNames[${i}][${j}]`, { rules: [ { required: true, message: i18n.t('common.tips.input', [i18n.t('compute.repo.variables')]) }, ], }, ], value: j => [ `containerEnvValues[${i}][${j}]`, { rules: [ { required: true, message: i18n.t('common.tips.input', [i18n.t('compute.repo.value')]) }, ], }, ], }), privileged: i => [ `containerPrivilegeds[${i}]`, { valuePropName: 'checked', initialValue: false, }, ], }, } } export class Decorator { decorators = {} // eslint-disable-next-line constructor(type) { this.type = type } createDecorators () { return createVmDecorators(this.type) } } /** * 根据表单拼装创建参数 * * @export * @class GenCreateData */ export class GenCreateData { // eslint-disable-next-line constructor(fd, fi) { if (R.isNil(fd)) return this.fd = fd this.fi = fi this.createType = this.fi.createType this.isIDC = this.createType === SERVER_TYPE.idc this.isPrepaid = this.fd.resourceType === RESOURCE_TYPES_MAP.prepaid.key } /** * 拼装磁盘数据 * * @param { Object } item // 磁盘数据 * @param { String } type // 磁盘类型 sys | data * @param { Number } index // 序号 * @returns { Object } * @memberof GenCreateData */ genDisk (item, type, index) { const ret = { disk_type: type, index, backend: item.type === STORAGE_AUTO.key ? '' : item.type, size: item.size * 1024, format: 'raw', fs: 'ext4', } if (type === 'sys' && this.fd.imageType === IMAGES_TYPE_MAP.iso.key && this.isWindows()) { ret.driver = 'ide' } if (item.medium) { ret.medium = item.medium } if (item.schedtags) { ret.schedtags = item.schedtags } if (item.filetype) { ret.fs = item.filetype if (item.filetype !== 'swap') { ret.mountpoint = item.mountpoint } } if (item.storage_id) { ret.storage_id = item.storage_id } if (item.iops) { ret.iops = item.iops } if (item.throughput) { ret.throughput = item.throughput } if (item.preallocation) { ret.preallocation = item.preallocation } // 磁盘区分介质 if (diskSupportTypeMedium(this.fd.hypervisor)) { ret.backend = getOriginDiskKey(ret.backend) } return ret } isWindows () { let isWindows = false const osType = (_.get(this.fi, 'imageMsg.info.properties.os_type') || '').toLowerCase() const os = (_.get(this.fd, 'os') || '').toLowerCase() if (~[osType, os].indexOf('windows')) { isWindows = true } return isWindows } _getDataDiskType (dataDiskTypes) { if (!R.isNil(dataDiskTypes) && !R.isEmpty(dataDiskTypes)) { const firstKey = Object.keys(dataDiskTypes)[0] if (firstKey && dataDiskTypes[firstKey]) { return dataDiskTypes[firstKey].key } } } _genDisksArr () { const dataDiskType = this._getDataDiskType(this.fd.dataDiskTypes) const dataDisk = [] R.forEachObjIndexed((value, key) => { const diskObj = { size: value, type: dataDiskType, } if (this.fd.dataDiskFiletypes && this.fd.dataDiskFiletypes[key]) { diskObj.filetype = this.fd.dataDiskFiletypes[key] } if (this.fd.dataDiskMountPaths && this.fd.dataDiskMountPaths[key]) { diskObj.mountpoint = this.fd.dataDiskMountPaths[key] } if (this.fd.dataDiskSnapshots && this.fd.dataDiskSnapshots[key]) { diskObj.snapshot_id = this.fd.dataDiskSnapshots[key] } if (this.fd.dataDiskSchedtags && this.fd.dataDiskSchedtags[key]) { diskObj.schedtags = [ { id: this.fd.dataDiskSchedtags[key] }, ] if (this.fd.dataDiskPolicys && this.fd.dataDiskPolicys[key]) { diskObj.schedtags[0].strategy = this.fd.dataDiskPolicys[key] } } if (this.fd.dataDiskStorages && this.fd.dataDiskStorages[key]) { // if system disk specifies storage, the data disks should do the same diskObj.storage_id = this.fd.dataDiskStorages[key] || this.fd.systemDiskStorage } if (this.fd.dataDiskIops && this.fd.dataDiskIops[key]) { diskObj.iops = this.fd.dataDiskIops[key] } if (this.fd.dataDiskThroughputs && this.fd.dataDiskThroughputs[key]) { diskObj.throughput = this.fd.dataDiskThroughputs[key] } if (this.fi.dataDiskMedium) { diskObj.medium = this.fi.dataDiskMedium } if (this.fd.dataDiskPreallocation && this.fd.dataDiskPreallocation[key]) { diskObj.preallocation = this.fd.dataDiskPreallocation[key] } dataDisk.push(diskObj) }, this.fd.dataDiskSizes) const disks = { data: dataDisk } return disks } /** * 组装所有磁盘数据,数据盘 * * @returns { Array } * @memberof GenCreateData */ genDisks () { const disks = this._genDisksArr() if (this.isPublic && this.isPrepaid) { return this.fd.spec.disks } const ret = [] for (let i = 0, len = disks.data.length; i < len; i++) { ret.push(this.genDisk(disks.data[i], 'data', i + 1)) } return ret } /** * 组装所有网络数据 * * @returns { Array } * @memberof GenCreateData */ getPortMappings () { const port_mappings = [] if (this.fd.containerPorts) { for (const k in this.fd.containerPorts) { const pm = {} if (this.fd.containerPorts[k]) { pm.port = this.fd.containerPorts[k] } if (this.fd.hostPorts[k]) { pm.host_port = this.fd.hostPorts[k] } if (pm) { port_mappings.push(pm) } } } return port_mappings } genNetworks () { let ret = [{ exit: false }] // 指定 IP 子网 if (this.fd.networkType === NETWORK_OPTIONS_MAP.manual.key) { ret = [] R.forEachObjIndexed((value, key) => { const obj = { network: value, } if (this.fd.networkIps) { const address = this.fd.networkIps[key] if (address) { obj.address = address } } if (this.fd.networkMacs) { const mac = this.fd.networkMacs[key] if (mac) { obj.mac = mac } } if (this.fd.networkExits) { const exit = this.fd.networkExits[key] if (exit) { obj.exit = true } } if (this.fd.networkIPv6s) { const ipv6 = this.fd.networkIPv6s[key] if (ipv6) { obj.require_ipv6 = true } } const target = this.fi.networkList.filter(item => item.key === key) if (this.fd.networkIpsAddress6) { const ipv6Last = this.fd.networkIpsAddress6[key] const ipv6First = getIpv6Start(target[0]?.network?.guest_ip6_start) obj.address6 = ipv6First + ipv6Last } if (obj.require_ipv6) { if (this.fd.networkIPv6Modes && this.fd.networkIPv6Modes[key] === 'only') { obj.strict_ipv6 = true } } if (!target[0]?.network?.guest_ip_start && !target[0]?.network?.guest_ip_end && target[0]?.network?.guest_ip6_start) { obj.strict_ipv6 = true } if (this.fd.networkDevices) { const device = this.fd.networkDevices[key] if (device) { obj.sriov_device = { model: device } } } const portMappings = this.getPortMappings() if (portMappings.length > 0) { obj.port_mappings = portMappings } ret.push(obj) }, this.fd.networks) } // 指定 调度标签 if (this.fd.networkType === NETWORK_OPTIONS_MAP.schedtag.key) { ret = [] R.forEachObjIndexed((value, key) => { const obj = { id: value, } const strategy = this.fd.networkPolicys[key] if (strategy) { obj.strategy = strategy } if (this.fd.networkDevices) { const device = this.fd.networkDevices[key] if (device) { obj.sriov_device = { model: device } } } ret.push({ schedtags: [obj], }) }, this.fd.networkSchedtags) } return ret } /** * 获取配置的PCI数据 * * @returns { Array } * @memberof GenCreateData */ genPciDevices () { const ret = [] const { pciCount, pciModel } = this.fd const pciKeys = Object.keys(this.fd.pciCount) pciKeys.forEach(key => { for (let i = 0, len = pciCount[key]; i < len; i++) { const regexp = /vendor=(.+):(.+)/ const matched = pciModel[key].match(regexp) const model = matched[2] const vendor = matched[1] ret.push({ model, vendor, }) } }) return ret } /** * 获取调度策略所提交的 key 与 value * * @returns * @memberof GenCreateData */ getSchedPolicyValueKey () { const ret = {} // 调度策略选择为 指定宿主机 if (this.fd.schedPolicyType === SCHED_POLICY_OPTIONS_MAP.host.key) { if (this.isPublic) { ret.key = 'prefer_manager' } else { ret.key = 'prefer_host' } ret.value = this.fd.schedPolicyHost } else if (this.showPreferManager()) { // 如果是通过云账号过滤镜像 ret.key = 'prefer_manager' ret.value = this.fd.prefer_manager } // 调度策略选择为 调度标签 if (this.fd.schedPolicyType === SCHED_POLICY_OPTIONS_MAP.schedtag.key) { ret.key = 'schedtags' ret.value = [] R.forEachObjIndexed((value, key) => { const item = { id: value } if (this.fd.policySchedtagPolicys[key]) { item.strategy = this.fd.policySchedtagPolicys[key] } ret.value.push(item) }, this.fd.policySchedtagSchedtags) } return ret } /** * 获取平台 * * @returns { String } * @memberof GenCreateData */ getHypervisor () { return HYPERVISORS_MAP.pod.key } /** * 是否是通过云账号过滤后选择的镜像 * * @returns { String } * @memberof GenCreateData */ showPreferManager () { const imageMsg = IMAGES_TYPE_MAP[this.fd.imageType] return imageMsg && imageMsg.enable_cloudaccount } /** * 获取Region * * @returns { String } * @memberof GenCreateData */ getPreferRegion () { if (this.isPublic && !this.isPrepaid) { return this.fd.sku.cloudregion_id } return this.fd.cloudregion.key } /** * 获取Zone * * @returns { String } * @memberof GenCreateData */ getPreferZone () { let ret = '' if (R.is(Object, this.fd.zone)) { ret = this.fd.zone.key } if (R.is(String, this.fd.zone)) { // 字符串形式是公有云 AreaSelect 的 zone ret = this.fd.zone } return ret } /** * 获取CPU核数 * * @returns { String } * @memberof GenCreateData */ getCpuCount () { let ret = this.fd.vcpu if (this.isPublic && this.isPrepaid) { ret = this.fd.spec.vcpu_count } return ret } /** * 获取CPU插槽数 * @returns */ getCpuSockets () { return this.fi.cpuSockets } /** * 获取内存 * * @returns { String } * @memberof GenCreateData */ getMemSize () { let ret = this.fd.vmem if (this.isPublic && this.isPrepaid) { ret = this.fd.spec.vmem_size * 1024 } return ret } getPreferManagerId () { let prefer_manager_id = '' const hypervisor = this.getHypervisor() const privateHypervisors = [HYPERVISORS_MAP.hcso.key, HYPERVISORS_MAP.hcs.key] if (this.isPublic || privateHypervisors.includes(hypervisor)) { prefer_manager_id = this.fd.cloudprovider } return prefer_manager_id } generatePod () { const tabKeys = this.fi.containerPanes.map(v => v.key) const getEnvs = (names, values) => { const envs = [] if (names) { for (const k in names) { envs.push({ key: names[k], value: values[k] }) } } return envs } const getVolumeMounts = (names, paths) => { const volume_mounts = [] if (names) { for (const k in names) { volume_mounts.push({ type: 'disk', disk: { index: names[k], }, mount_path: paths[k], }) } } return volume_mounts } const containers = tabKeys.map(k => { const pciDevices = (this.fd.pciEnable && this.genPciDevices()) || [] const image = this.fd.registryImages?.[k] || '' const credentialId = this.fd.imageCredentialIds?.[k] || '' const spec = { name: this.fd.containerNames?.[k], image: removeHttp(image) || this.fd.containerimages?.[k], command: this.fd.containerCommands?.[k]?.split(' '), args: this.fd.containerArgs?.[k]?.split(' '), privileged: this.fd.containerPrivilegeds?.[k], devices: pciDevices.map((item, idx) => { return { type: 'isolated_device', isolated_device: { index: idx, }, } }), envs: getEnvs(this.fd.containerEnvNames?.[k], this.fd.containerEnvValues?.[k]), volume_mounts: getVolumeMounts(this.fd.containerVolumeMountNames?.[k], this.fd.containerVolumeMountPaths?.[k]), } if (credentialId) { spec.image_credential_id = credentialId } return spec }) return { containers, // port_mappings: getPortMappings(), } } /** * 组装所有的创建数据 * * @returns { Object } * @memberof GenCreateData */ all () { const { create_server_auto_start = true } = store.getters.globalSetting.value || {} const data = { auto_start: create_server_auto_start, generate_name: this.fd.name && this.fd.name.trim(), description: this.fd.description, hypervisor: this.getHypervisor(), __count__: this.fd.count, disks: this.genDisks(), nets: this.genNetworks(), prefer_region: this.getPreferRegion(), vcpu_count: this.getCpuCount(), vmem_size: this.getMemSize(), project_id: (this.fd.project && this.fd.project.key) || store.getters.userInfo.projectId, os_arch: _.get(HOST_CPU_ARCHS, `[${this.fd.os_arch}].key`), hostname: this.fd.hostName, } // 非预付费资源池不会添加sku if (!this.isPrepaid) { data.sku = this.fd.sku.name } // 弹性IP if (this.fd.eip_type === EIP_TYPES_MAP.new.key || this.fd.eip_type === EIP_TYPES_MAP.public.key) { if ( this.fd.eip_charge_type === EIP_CHARGE_TYPES_MAP.traffic.key || this.fd.eip_charge_type === EIP_CHARGE_TYPES_MAP.bandwidth.key ) { if (this.fd.eip_type === EIP_TYPES_MAP.public.key) { data.public_ip_charge_type = this.fd.eip_charge_type data.public_ip_bw = this.fd.eip_bw } else { data.eip_charge_type = this.fd.eip_charge_type data.eip_bw = this.fd.eip_bw } } } if (this.fd.eip_type === EIP_TYPES_MAP.bind.key) { data.eip = this.fd.eip } // 包年包月参数 if (this.fd.billType === BILL_TYPES_MAP.package.key) { data.duration = this.fd.duration // 自动续费 data.auto_renew = this.fd.autoRenew } // 线路类型 if (this.fd.eip_bgp_type) { data.eip_bgp_type = this.fd.eip_bgp_type } // pci if (this.fd.pciEnable) { data.isolated_devices = this.genPciDevices() } // 安全组 if (this.fd.secgroup_type && this.fd.secgroup_type === SECGROUP_OPTIONS_MAP.bind.key) { data.secgroups = this.fd.secgroup } // 如果设置了调度策略则拼装调度所需数据 或者 通过云账号过滤镜像 if ((this.fd.schedPolicyType !== SCHED_POLICY_OPTIONS_MAP.default.key) || this.showPreferManager()) { const schedPolicyValueKey = this.getSchedPolicyValueKey() data[schedPolicyValueKey.key] = schedPolicyValueKey.value } // zone const zoneId = this.getPreferZone() if (zoneId) { data.prefer_zone = zoneId } const prefer_manager_id = this.getPreferManagerId() if (prefer_manager_id) { data.prefer_manager_id = prefer_manager_id } // 到期释放 if (this.fd.billType !== BILL_TYPES_MAP.package.key && this.fd.durationStandard !== 'none') { if (this.fd.durationStandard === 'custom') { data.duration = this.fd.duration } else { data.duration = this.fd.durationStandard } data.billing_type = 'postpaid' } // 镜像类型为 iso 需要加参数 cdrom if (this.fd.imageType === IMAGES_TYPE_MAP.iso.key || this.fd.imageType === IMAGES_TYPE_MAP.private_iso.key) { data.cdrom = this.fd.image.key } // 主机镜像需要guest image id参数,并且把磁盘中的镜像ID回填回去 if (this.fd.imageType === IMAGES_TYPE_MAP.host.key) { data.guest_image_id = this.fd.image.key data.disks.forEach((val, i) => { if (this.fi.imageMsg.data_images && i < this.fi.imageMsg.data_images.length) { data.disks[i] = { ...val, image_id: this.fi.imageMsg.data_images[i].id } } }) } // 主机快照需要instance_snapshot_id参数 if (this.fd.imageType === IMAGES_TYPE_MAP.snapshot.key) { data.instance_snapshot_id = this.fd.image.key delete data.disks // 主机快照不需要 disks 字段 } else if (this.fd.imageType === IMAGES_TYPE_MAP.backup.key) { data.instance_backup_id = this.fd.image.key // delete data.disks // 主机备份可以保留 disks 字段 } // 主机组 if (this.fd.groupsEnable && this.fd.groups && this.fd.groups.length) { data.groups = this.fd.groups } // 标签 if (this.fd.tag) { data.__meta__ = this.fd.tag } // 插槽数 if (this.fi.showCpuSockets) { data.cpu_sockets = this.getCpuSockets() } // 容器 data.pod = this.generatePod(data) return data } /** * 获取创建预测的错误信息 * * @returns { Array } * @memberof GenCreateData */ getForecastErrors (data) { const errors = [] if (data.filtered_candidates && data.filtered_candidates.length > 0) { for (let i = 0, len = data.filtered_candidates.length; i < len; i++) { const item = data.filtered_candidates[i] let message = `${i18n.t('dictionary.host')}【${item.name}】` const filterMapItem = FORECAST_FILTERS_MAP[item.filter_name] if (filterMapItem) { message += filterMapItem } else { message += i18n.t('compute.text_1325', [item.filter_name]) } errors.push({ message, children: item.reasons, }) } } else { errors.push({ message: i18n.t('compute.text_227'), }) } return { errors, allow_count: data.allow_count || 0, req_count: data.req_count || 1, not_allow_reasons: data.not_allow_reasons, } } }