mirror of https://github.com/rancher/dashboard.git
222 lines
7.1 KiB
JavaScript
222 lines
7.1 KiB
JavaScript
import { PVC } from '@shell/config/types';
|
|
import { SOURCE_TYPE } from '@shell/config/harvester-map';
|
|
import { HCI as HCI_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
|
|
export function vmNetworks(spec, getters, errors, validatorArgs) {
|
|
const { domain: { devices: { interfaces } }, networks } = spec;
|
|
const allNames = new Set();
|
|
|
|
interfaces.map( (I, index) => {
|
|
allNames.add(I.name);
|
|
const N = networks.find( N => I.name === N.name);
|
|
const prefix = (I.name || N.name) || index + 1;
|
|
|
|
if (I.name.length > 20) {
|
|
const message = getters['i18n/t']('harvester.validation.custom.tooLongName', { max: 20 });
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.network.error', { prefix, message }));
|
|
}
|
|
|
|
if (!I.name || !N.name) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.name');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.network.error', { prefix, message }));
|
|
}
|
|
|
|
if (N.multus) {
|
|
if (!N.multus.networkName) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.network.name');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.network.error', { prefix, message }));
|
|
}
|
|
}
|
|
|
|
if (I.macAddress && !isValidMac(I.macAddress) && !N.pod) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.network.macFormat');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.network.error', { prefix, message }));
|
|
}
|
|
|
|
const portsName = new Set();
|
|
const portsNumber = new Set();
|
|
|
|
if (I.masquerade && I.ports) {
|
|
const ports = I?.ports || [];
|
|
|
|
ports.forEach((P) => {
|
|
portsName.add(P.name);
|
|
portsNumber.add(P.port);
|
|
});
|
|
|
|
if (portsName.size !== I.ports.length) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.network.duplicatedPortName');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.network.error', { prefix, message }));
|
|
}
|
|
|
|
if (portsNumber.size !== I.ports.length) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.network.duplicatedPortNumber');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.network.error', { prefix, message }));
|
|
}
|
|
}
|
|
});
|
|
|
|
if (allNames.size !== interfaces.length) {
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.network.duplicatedName'));
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
export function vmDisks(spec, getters, errors, validatorArgs, displayKey, value) {
|
|
const isVMTemplate = validatorArgs.includes('isVMTemplate');
|
|
const data = isVMTemplate ? this.value.spec.vm : value;
|
|
|
|
let _volumeClaimTemplates = [];
|
|
const volumeClaimTemplateString = data.metadata.annotations[HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE];
|
|
|
|
try {
|
|
_volumeClaimTemplates = JSON.parse(volumeClaimTemplateString);
|
|
} catch (e) {}
|
|
|
|
const _volumes = spec.template.spec.volumes || [];
|
|
const _disks = spec.template.spec.domain.devices.disks || [];
|
|
|
|
const allNames = new Set();
|
|
|
|
_disks.forEach((D, idx) => {
|
|
const prefix = D.name || _volumes[idx]?.name || idx + 1;
|
|
|
|
if (!D.disk && !D.cdrom) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.volume.type');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.error', { prefix, message }));
|
|
|
|
return allNames.add(D.name);
|
|
}
|
|
|
|
if (D.name?.length > 63) {
|
|
const message = getters['i18n/t']('harvester.validation.custom.tooLongName', { max: 63 });
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.error', { prefix, message }));
|
|
}
|
|
|
|
if (!D.name) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.name');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.error', { prefix, message }));
|
|
}
|
|
|
|
allNames.add(D.name);
|
|
});
|
|
|
|
if (allNames.size !== _disks.length) {
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.duplicatedName'));
|
|
}
|
|
|
|
if (_volumes.length === 0) {
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.needImageOrExisting'));
|
|
}
|
|
|
|
let requiredVolume = false;
|
|
|
|
_volumes.forEach((V, idx) => {
|
|
const { type, typeValue } = getVolumeType(V, _volumeClaimTemplates);
|
|
const prefix = V.name || idx + 1;
|
|
|
|
if ([SOURCE_TYPE.IMAGE, SOURCE_TYPE.ATTACH_VOLUME, SOURCE_TYPE.CONTAINER].includes(type)) { // root image
|
|
// const message = getters['i18n/t']('harvester.validation.vm.volume.needImageOrExisting');
|
|
requiredVolume = true;
|
|
// errors.push(message);
|
|
}
|
|
|
|
if (type === SOURCE_TYPE.NEW || type === SOURCE_TYPE.IMAGE) {
|
|
if (!/([1-9]|[1-9][0-9]+)[a-zA-Z]+/.test(typeValue?.spec?.resources?.requests?.storage)) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.volume.size');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.error', { prefix, message }));
|
|
}
|
|
|
|
if (type === SOURCE_TYPE.IMAGE && !typeValue?.spec?.storageClassName && !isVMTemplate) { // type === SOURCE_TYPE.IMAGE
|
|
const message = getters['i18n/t']('harvester.validation.vm.volume.image');
|
|
|
|
if (idx === 0) {
|
|
errors.push(message);
|
|
} else {
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.error', { prefix, message }));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type === SOURCE_TYPE.ATTACH_VOLUME) {
|
|
const allPVCs = getters['harvester/all'](PVC);
|
|
const hasExistingVolume = allPVCs.find(P => P.metadata.name === V?.persistentVolumeClaim?.claimName);
|
|
|
|
if (!hasExistingVolume) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.volume.volume');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.error', { prefix, message }));
|
|
}
|
|
}
|
|
|
|
if (type === SOURCE_TYPE.CONTAINER && !V.containerDisk.image) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.volume.docker');
|
|
|
|
errors.push(getters['i18n/t']('harvester.validation.vm.volume.error', { prefix, message }));
|
|
}
|
|
});
|
|
|
|
if (!requiredVolume && !value.links) {
|
|
const message = getters['i18n/t']('harvester.validation.vm.volume.needImageOrExisting');
|
|
|
|
errors.push(message);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
export function isValidMac(value) {
|
|
return /^[A-Fa-f0-9]{2}(-[A-Fa-f0-9]{2}){5}$|^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){5}$/.test(value);
|
|
}
|
|
|
|
export function getVolumeType(V, DVTS) {
|
|
let outValue = null;
|
|
|
|
if (V.persistentVolumeClaim) {
|
|
outValue = DVTS.find(DVT => V.persistentVolumeClaim.claimName === DVT.metadata.name && DVT.metadata?.annotations && Object.prototype.hasOwnProperty.call(DVT.metadata.annotations, 'harvesterhci.io/imageId'));
|
|
|
|
if (outValue) {
|
|
return {
|
|
type: SOURCE_TYPE.IMAGE,
|
|
typeValue: outValue
|
|
};
|
|
}
|
|
|
|
// new type
|
|
outValue = DVTS.find(DVT => V.persistentVolumeClaim.claimName === DVT.metadata.name);
|
|
|
|
if (outValue) {
|
|
return {
|
|
type: SOURCE_TYPE.NEW,
|
|
typeValue: outValue
|
|
};
|
|
}
|
|
|
|
// existing, container type volume doesn't need validator
|
|
return {
|
|
type: SOURCE_TYPE.ATTACH_VOLUME,
|
|
typeValue: null
|
|
};
|
|
}
|
|
|
|
if (V.containerDisk) {
|
|
return {
|
|
type: SOURCE_TYPE.CONTAINER,
|
|
typeValue: null
|
|
};
|
|
}
|
|
|
|
return {};
|
|
}
|