diff --git a/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue b/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue
index 81029521..3c56ef9f 100644
--- a/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue
+++ b/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue
@@ -6,6 +6,7 @@ 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 { set } from '@shell/utils/object';
import { Banner } from '@components/Banner';
import KeyValue from '@shell/components/form/KeyValue';
import NodeScheduling from '@shell/components/form/NodeScheduling';
@@ -22,6 +23,7 @@ import Reserved from './kubevirt.io.virtualmachine/VirtualMachineReserved';
import Volume from './kubevirt.io.virtualmachine/VirtualMachineVolume';
import Network from './kubevirt.io.virtualmachine/VirtualMachineNetwork';
import CpuMemory from './kubevirt.io.virtualmachine/VirtualMachineCpuMemory';
+import CpuModel from './kubevirt.io.virtualmachine/VirtualMachineCpuModel';
import CloudConfig from './kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
import SSHKey from './kubevirt.io.virtualmachine/VirtualMachineSSHKey';
@@ -38,6 +40,7 @@ export default {
Network,
Checkbox,
CpuMemory,
+ CpuModel,
CruResource,
CloudConfig,
LabeledSelect,
@@ -70,12 +73,12 @@ export default {
return {
templateId,
- templateValue: null,
- templateSpec: null,
- versionName: '',
- description: '',
- defaultVersion: null,
- isDefaultVersion: false,
+ templateValue: null,
+ templateSpec: null,
+ versionName: '',
+ description: '',
+ defaultVersion: null,
+ isDefaultVersion: false,
};
},
@@ -154,6 +157,18 @@ export default {
},
methods: {
+ updateCpuModel(value) {
+ if (!this.spec?.template?.spec?.domain?.cpu) {
+ set(this.spec, 'template.spec.domain.cpu', {});
+ }
+
+ if (value && value !== '') {
+ set(this.spec.template.spec.domain.cpu, 'model', value);
+ } else {
+ delete this.spec.template.spec.domain.cpu.model;
+ }
+ },
+
async saveVMT(buttonCb) {
this.parseVM();
@@ -436,6 +451,17 @@ export default {
/>
+
+
+
+import YAML from 'yaml';
+import LabeledSelect from '@shell/components/form/LabeledSelect';
+import { CONFIG_MAP } from '@shell/config/types';
+import { Banner } from '@components/Banner';
+
+const CPU_MODEL_CONFIG_MAP_ID = 'harvester-system/node-cpu-model-configuration';
+
+export default {
+ name: 'HarvesterCpuModel',
+
+ emits: ['update:value'],
+
+ components: {
+ LabeledSelect,
+ Banner
+ },
+
+ props: {
+ value: {
+ type: String,
+ default: ''
+ },
+ mode: {
+ type: String,
+ default: 'create',
+ },
+ },
+
+ async fetch() {
+ const inStore = this.$store.getters['currentProduct'].inStore;
+
+ try {
+ await this.$store.dispatch(`${ inStore }/find`, { type: CONFIG_MAP, id: CPU_MODEL_CONFIG_MAP_ID });
+ this.fetchError = null;
+ } catch (e) {
+ this.fetchError = this.t('harvester.virtualMachine.cpuModel.fetchError', { error: e.message || e });
+ }
+ },
+
+ data() {
+ return { fetchError: null };
+ },
+
+ computed: {
+ localValue: {
+ get() {
+ return this.value ?? '';
+ },
+ set(val) {
+ this.$emit('update:value', val ?? '');
+ }
+ },
+
+ cpuModelConfigMap() {
+ const inStore = this.$store.getters['currentProduct'].inStore;
+
+ return this.$store.getters[`${ inStore }/byId`](
+ CONFIG_MAP,
+ CPU_MODEL_CONFIG_MAP_ID
+ );
+ },
+
+ cpuModelOptions() {
+ if (!this.cpuModelConfigMap?.data?.cpuModels) {
+ return [{ label: this.t('generic.default'), value: '' }];
+ }
+
+ let cpuModelsData;
+
+ try {
+ cpuModelsData = YAML.parse(this.cpuModelConfigMap.data?.cpuModels || '');
+ } catch (e) {
+ return [{ label: this.t('generic.default'), value: '' }];
+ }
+
+ const options = [];
+
+ options.push({
+ label: this.t('generic.default'),
+ value: ''
+ });
+
+ // Add global models (host-model, host-passthrough)
+ const globalModels = cpuModelsData.globalModels || [];
+
+ globalModels.forEach((modelName) => {
+ options.push({
+ label: modelName,
+ value: modelName
+ });
+ });
+
+ // Add regular models with node count
+ const modelEntries = Object.entries(cpuModelsData.models || {});
+
+ // Sort models alphabetically for consistent display
+ modelEntries.sort((a, b) => a[0].localeCompare(b[0]));
+
+ modelEntries.forEach(([modelName, modelInfo]) => {
+ const readyCount = modelInfo.readyCount || 0;
+ const label = this.t('harvester.virtualMachine.cpuModel.optionLabel', { modelName, count: readyCount });
+
+ options.push({
+ label,
+ value: modelName
+ });
+ });
+
+ return options;
+ },
+ },
+};
+
+
+
+
+
+ {{ fetchError }}
+
+
+
+
diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue
index 2af391e7..80d2eb35 100644
--- a/pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue
+++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue
@@ -2,7 +2,7 @@
import { isEqual } from 'lodash';
import { mapGetters } from 'vuex';
import Tabbed from '@shell/components/Tabbed';
-import { clone } from '@shell/utils/object';
+import { clone, set } from '@shell/utils/object';
import Tab from '@shell/components/Tabbed/Tab';
import { Checkbox } from '@components/Form/Checkbox';
import CruResource from '@shell/components/CruResource';
@@ -32,6 +32,7 @@ import PciDevices from './VirtualMachinePciDevices/index';
import AccessCredentials from './VirtualMachineAccessCredentials';
import CloudConfig from './VirtualMachineCloudConfig';
import CpuMemory from './VirtualMachineCpuMemory';
+import CpuModel from './VirtualMachineCpuModel';
import Network from './VirtualMachineNetwork';
import Volume from './VirtualMachineVolume';
import SSHKey from './VirtualMachineSSHKey';
@@ -57,6 +58,7 @@ export default {
SSHKey,
Network,
CpuMemory,
+ CpuModel,
CloudConfig,
NodeScheduling,
PodAffinity,
@@ -538,6 +540,18 @@ export default {
return out;
},
+
+ updateCpuModel(value) {
+ if (!this.spec?.template?.spec?.domain?.cpu) {
+ set(this.spec, 'template.spec.domain.cpu', {});
+ }
+
+ if (value && value !== '') {
+ set(this.spec.template.spec.domain.cpu, 'model', value);
+ } else {
+ delete this.spec.template.spec.domain.cpu.model;
+ }
+ },
},
};
@@ -870,6 +884,16 @@ export default {
+
+