mirror of https://github.com/rancher/dashboard.git
347 lines
9.9 KiB
Vue
347 lines
9.9 KiB
Vue
<script>
|
|
import { mapGetters } from 'vuex';
|
|
import Tabbed from '@shell/components/Tabbed';
|
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
import { Checkbox } from '@components/Form/Checkbox';
|
|
import CruResource from '@shell/components/CruResource';
|
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
import UnitInput from '@shell/components/form/UnitInput';
|
|
|
|
import Volume from '@shell/edit/kubevirt.io.virtualmachine/VirtualMachineVolume';
|
|
import Network from '@shell/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork';
|
|
import CpuMemory from '@shell/edit/kubevirt.io.virtualmachine/VirtualMachineCpuMemory';
|
|
import CloudConfig from '@shell/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
|
|
import SSHKey from '@shell/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey';
|
|
import NodeScheduling from '@shell/components/form/NodeScheduling';
|
|
|
|
import { HCI } from '@shell/config/types';
|
|
import { randomStr } from '@shell/utils/string';
|
|
import { RunStrategys } from '@shell/config/harvester-map';
|
|
import { _CONFIG, _EDIT, _VIEW } from '@shell/config/query-params';
|
|
import { HCI as HCI_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
|
|
import VM_MIXIN from '@shell/mixins/harvester-vm';
|
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
import { AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
|
|
|
|
export default {
|
|
name: 'HarvesterEditVMTemplate',
|
|
|
|
components: {
|
|
Tab,
|
|
SSHKey,
|
|
Volume,
|
|
Tabbed,
|
|
Network,
|
|
Checkbox,
|
|
CpuMemory,
|
|
CruResource,
|
|
CloudConfig,
|
|
LabeledSelect,
|
|
UnitInput,
|
|
NameNsDescription,
|
|
NodeScheduling,
|
|
},
|
|
|
|
mixins: [CreateEditView, VM_MIXIN],
|
|
|
|
props: {
|
|
value: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
},
|
|
|
|
data() {
|
|
if (this.mode === _EDIT) {
|
|
this.value.cleanForNew();
|
|
}
|
|
|
|
const templateId = this.value.templateId || this.$route.query.templateId;
|
|
|
|
return {
|
|
templateId,
|
|
templateValue: null,
|
|
templateSpec: null,
|
|
versionName: '',
|
|
description: '',
|
|
defaultVersion: null,
|
|
isDefaultVersion: false,
|
|
RunStrategys,
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters({ t: 'i18n/t' }),
|
|
isConfig() {
|
|
return this.$route.query?.as === _CONFIG || this.isView;
|
|
},
|
|
|
|
realTemplateMode() {
|
|
return this.templateId ? _VIEW : this.mode;
|
|
},
|
|
secretNamePrefix() {
|
|
return this.templateValue?.metadata?.name;
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
templateId: {
|
|
async handler(neu) {
|
|
const templates = await this.$store.dispatch('harvester/findAll', { type: HCI.VM_TEMPLATE });
|
|
let templateValue = templates.find( V => V.id === neu) || null;
|
|
let templateSpec = templateValue?.spec;
|
|
|
|
if (!templateValue) {
|
|
templateSpec = {
|
|
description: '',
|
|
defaultVersionId: ''
|
|
};
|
|
|
|
templateValue = await this.$store.dispatch('harvester/create', {
|
|
metadata: {
|
|
name: '',
|
|
namespace: ''
|
|
},
|
|
spec: templateSpec,
|
|
type: HCI.VM_TEMPLATE
|
|
});
|
|
}
|
|
|
|
this.templateValue = templateValue;
|
|
this.templateSpec = templateSpec;
|
|
},
|
|
immediate: true
|
|
}
|
|
},
|
|
|
|
created() {
|
|
this.registerAfterHook(async() => {
|
|
if (this.isDefaultVersion) {
|
|
// Set the default version according to annotation:[HCI_ANNOTATIONS.TEMPLATE_VERSION_CUSTOM_NAME]
|
|
const versions = await this.$store.dispatch('harvester/findAll', { type: HCI.VM_VERSION, opt: { force: true } });
|
|
|
|
const version = versions.find( V => V?.metadata?.annotations?.[HCI_ANNOTATIONS.TEMPLATE_VERSION_CUSTOM_NAME] === this.customName);
|
|
|
|
if (version) {
|
|
try {
|
|
this.templateValue.defaultVersionId = version.id;
|
|
|
|
const data = [{
|
|
op: 'replace', path: '/spec/defaultVersionId', value: version.id
|
|
}];
|
|
|
|
await this.templateValue.patch( data, { url: this.templateValue.linkFor('view') });
|
|
} catch (err) {
|
|
return Promise.reject(new Error(err.message));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
mounted() {
|
|
this.imageId = this.diskRows[0]?.image || '';
|
|
},
|
|
|
|
methods: {
|
|
async saveVMT(buttonCb) {
|
|
this.parseVM();
|
|
|
|
const templates = await this.$store.dispatch('harvester/findAll', { type: HCI.VM_TEMPLATE });
|
|
const template = templates.find( O => O.metadata.name === this.templateValue.metadata.name);
|
|
|
|
try {
|
|
if (!this.templateId) {
|
|
if (this.templateValue?.metadata?.name) {
|
|
await this.templateValue.save();
|
|
} else {
|
|
this.errors = [this.t('validation.required', { key: this.t('harvester.vmTemplate.nameNsDescription.name') })];
|
|
buttonCb(false);
|
|
|
|
return;
|
|
}
|
|
} else {
|
|
template.save();
|
|
}
|
|
|
|
this.value.cleanForNew();
|
|
this.customName = randomStr(10);
|
|
this.$set(this.value.metadata, 'annotations', {
|
|
...this.value.metadata.annotations,
|
|
[HCI_ANNOTATIONS.TEMPLATE_VERSION_CUSTOM_NAME]: this.customName
|
|
});
|
|
|
|
const name = this.templateValue.metadata.name || template.metadata.name;
|
|
const namespace = this.templateValue.metadata.namespace || template.metadata.namespace;
|
|
|
|
if (this.isCreate) {
|
|
this.value.metadata.namespace = namespace;
|
|
}
|
|
|
|
this.$set(this.value.spec, 'templateId', `${ namespace }/${ name }`);
|
|
const res = await this.value.save();
|
|
|
|
await this.saveSecret(res);
|
|
await this.applyHooks(AFTER_SAVE_HOOKS);
|
|
this.done();
|
|
} catch (e) {
|
|
this.errors = [e];
|
|
buttonCb(false);
|
|
}
|
|
},
|
|
|
|
onTabChanged({ tab }) {
|
|
if (tab.name === 'advanced') {
|
|
this.$refs.yamlEditor?.refresh();
|
|
}
|
|
}
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<CruResource
|
|
v-if="templateSpec && spec"
|
|
:done-route="doneRoute"
|
|
:resource="value"
|
|
:can-yaml="false"
|
|
:mode="mode"
|
|
:errors="errors"
|
|
:apply-hooks="applyHooks"
|
|
@finish="saveVMT"
|
|
>
|
|
<NameNsDescription
|
|
v-model="templateValue"
|
|
:mode="realTemplateMode"
|
|
name-label="harvester.vmTemplate.nameNsDescription.name"
|
|
:namespaced="true"
|
|
/>
|
|
|
|
<Checkbox
|
|
v-if="templateId"
|
|
v-model="isDefaultVersion"
|
|
class="mb-20"
|
|
:label="t('tableHeaders.defaultVersion')"
|
|
type="checkbox"
|
|
:mode="mode"
|
|
/>
|
|
|
|
<Tabbed :side-tabs="true" @changed="onTabChanged">
|
|
<Tab name="Basics" :label="t('harvester.vmTemplate.tabs.basics')">
|
|
<CpuMemory :cpu="cpu" :memory="memory" :disabled="isConfig" @updateCpuMemory="updateCpuMemory" />
|
|
|
|
<div class="mb-20">
|
|
<SSHKey v-model="sshKey" :namespace="templateValue.metadata.namespace" :disable-create="isView" :mode="mode" @update:sshKey="updateSSHKey" />
|
|
</div>
|
|
</Tab>
|
|
|
|
<Tab name="Volume" :label="t('harvester.tab.volume')" :weight="-1">
|
|
<Volume v-model="diskRows" :mode="mode" :namespace="value.metadata.namespace" :existing-volume-disabled="true" />
|
|
</Tab>
|
|
|
|
<Tab name="Network" :label="t('harvester.tab.network')" :weight="-2">
|
|
<Network v-model="networkRows" :mode="mode" />
|
|
</Tab>
|
|
|
|
<Tab
|
|
name="nodeScheduling"
|
|
:label="t('workload.container.titles.nodeScheduling')"
|
|
:weight="-89"
|
|
>
|
|
<NodeScheduling
|
|
:mode="mode"
|
|
:value="spec.template.spec"
|
|
:nodes="nodesIdOptions"
|
|
/>
|
|
</Tab>
|
|
|
|
<Tab name="advanced" :label="t('harvester.tab.advanced')" :weight="-99">
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<LabeledSelect
|
|
v-model="runStrategy"
|
|
label-key="harvester.virtualMachine.runStrategy"
|
|
:options="RunStrategys"
|
|
:mode="mode"
|
|
/>
|
|
</div>
|
|
|
|
<div class="col span-6">
|
|
<LabeledSelect
|
|
v-model="osType"
|
|
label-key="harvester.virtualMachine.osType"
|
|
:mode="mode"
|
|
:options="OS"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-20">
|
|
<a v-if="showAdvanced" v-t="'harvester.generic.showMore'" role="button" @click="toggleAdvanced" />
|
|
<a v-else v-t="'harvester.generic.showMore'" role="button" @click="toggleAdvanced" />
|
|
</div>
|
|
|
|
<div v-if="showAdvanced" class="row mb-20">
|
|
<div class="col span-6">
|
|
<UnitInput
|
|
v-model="reservedMemory"
|
|
v-int-number
|
|
:label="t('harvester.virtualMachine.input.reservedMemory')"
|
|
:mode="mode"
|
|
:input-exponent="2"
|
|
:increment="1024"
|
|
:output-modifier="true"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<CloudConfig
|
|
ref="yamlEditor"
|
|
:mode="mode"
|
|
:user-script="userScript"
|
|
:namespace="templateValue.metadata.namespace"
|
|
:network-script="networkScript"
|
|
@updateUserData="updateUserData"
|
|
@updateNetworkData="updateNetworkData"
|
|
/>
|
|
|
|
<div class="spacer"></div>
|
|
<Checkbox
|
|
v-model="installUSBTablet"
|
|
class="check"
|
|
type="checkbox"
|
|
:label="t('harvester.virtualMachine.enableUsb')"
|
|
:mode="mode"
|
|
/>
|
|
|
|
<Checkbox
|
|
v-model="installAgent"
|
|
class="check"
|
|
type="checkbox"
|
|
label-key="harvester.virtualMachine.installAgent"
|
|
:mode="mode"
|
|
/>
|
|
|
|
<Checkbox
|
|
v-model="efiEnabled"
|
|
class="check"
|
|
type="checkbox"
|
|
:label="t('harvester.virtualMachine.efiEnabled')"
|
|
:mode="mode"
|
|
/>
|
|
|
|
<Checkbox
|
|
v-if="efiEnabled"
|
|
v-model="secureBoot"
|
|
class="check"
|
|
type="checkbox"
|
|
:label="t('harvester.virtualMachine.secureBoot')"
|
|
:mode="mode"
|
|
/>
|
|
</Tab>
|
|
</Tabbed>
|
|
</CruResource>
|
|
</template>
|