mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-03-21 20:51:45 +00:00
feat: add cpu model selection (#702)
Signed-off-by: Jack Yu <jack.yu@suse.com>
This commit is contained in:
parent
2db7ee7397
commit
c8a613874a
@ -6,6 +6,7 @@ import { Checkbox } from '@components/Form/Checkbox';
|
|||||||
import CruResource from '@shell/components/CruResource';
|
import CruResource from '@shell/components/CruResource';
|
||||||
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
|
import { set } from '@shell/utils/object';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
import KeyValue from '@shell/components/form/KeyValue';
|
import KeyValue from '@shell/components/form/KeyValue';
|
||||||
import NodeScheduling from '@shell/components/form/NodeScheduling';
|
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 Volume from './kubevirt.io.virtualmachine/VirtualMachineVolume';
|
||||||
import Network from './kubevirt.io.virtualmachine/VirtualMachineNetwork';
|
import Network from './kubevirt.io.virtualmachine/VirtualMachineNetwork';
|
||||||
import CpuMemory from './kubevirt.io.virtualmachine/VirtualMachineCpuMemory';
|
import CpuMemory from './kubevirt.io.virtualmachine/VirtualMachineCpuMemory';
|
||||||
|
import CpuModel from './kubevirt.io.virtualmachine/VirtualMachineCpuModel';
|
||||||
import CloudConfig from './kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
|
import CloudConfig from './kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
|
||||||
import SSHKey from './kubevirt.io.virtualmachine/VirtualMachineSSHKey';
|
import SSHKey from './kubevirt.io.virtualmachine/VirtualMachineSSHKey';
|
||||||
|
|
||||||
@ -38,6 +40,7 @@ export default {
|
|||||||
Network,
|
Network,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
CpuMemory,
|
CpuMemory,
|
||||||
|
CpuModel,
|
||||||
CruResource,
|
CruResource,
|
||||||
CloudConfig,
|
CloudConfig,
|
||||||
LabeledSelect,
|
LabeledSelect,
|
||||||
@ -70,12 +73,12 @@ export default {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
templateId,
|
templateId,
|
||||||
templateValue: null,
|
templateValue: null,
|
||||||
templateSpec: null,
|
templateSpec: null,
|
||||||
versionName: '',
|
versionName: '',
|
||||||
description: '',
|
description: '',
|
||||||
defaultVersion: null,
|
defaultVersion: null,
|
||||||
isDefaultVersion: false,
|
isDefaultVersion: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -154,6 +157,18 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
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) {
|
async saveVMT(buttonCb) {
|
||||||
this.parseVM();
|
this.parseVM();
|
||||||
|
|
||||||
@ -436,6 +451,17 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-20">
|
||||||
|
<div class="col span-6">
|
||||||
|
<CpuModel
|
||||||
|
:value="spec.template.spec.domain.cpu?.model || ''"
|
||||||
|
:mode="mode"
|
||||||
|
@update:value="updateCpuModel"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<a
|
<a
|
||||||
v-if="showAdvanced"
|
v-if="showAdvanced"
|
||||||
|
|||||||
@ -0,0 +1,133 @@
|
|||||||
|
<script>
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Banner
|
||||||
|
v-if="fetchError"
|
||||||
|
color="error"
|
||||||
|
class="mb-20"
|
||||||
|
>
|
||||||
|
{{ fetchError }}
|
||||||
|
</Banner>
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="localValue"
|
||||||
|
:label="t('harvester.virtualMachine.cpuModel.label')"
|
||||||
|
:options="cpuModelOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:disabled="!!fetchError"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -2,7 +2,7 @@
|
|||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import Tabbed from '@shell/components/Tabbed';
|
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 Tab from '@shell/components/Tabbed/Tab';
|
||||||
import { Checkbox } from '@components/Form/Checkbox';
|
import { Checkbox } from '@components/Form/Checkbox';
|
||||||
import CruResource from '@shell/components/CruResource';
|
import CruResource from '@shell/components/CruResource';
|
||||||
@ -32,6 +32,7 @@ import PciDevices from './VirtualMachinePciDevices/index';
|
|||||||
import AccessCredentials from './VirtualMachineAccessCredentials';
|
import AccessCredentials from './VirtualMachineAccessCredentials';
|
||||||
import CloudConfig from './VirtualMachineCloudConfig';
|
import CloudConfig from './VirtualMachineCloudConfig';
|
||||||
import CpuMemory from './VirtualMachineCpuMemory';
|
import CpuMemory from './VirtualMachineCpuMemory';
|
||||||
|
import CpuModel from './VirtualMachineCpuModel';
|
||||||
import Network from './VirtualMachineNetwork';
|
import Network from './VirtualMachineNetwork';
|
||||||
import Volume from './VirtualMachineVolume';
|
import Volume from './VirtualMachineVolume';
|
||||||
import SSHKey from './VirtualMachineSSHKey';
|
import SSHKey from './VirtualMachineSSHKey';
|
||||||
@ -57,6 +58,7 @@ export default {
|
|||||||
SSHKey,
|
SSHKey,
|
||||||
Network,
|
Network,
|
||||||
CpuMemory,
|
CpuMemory,
|
||||||
|
CpuModel,
|
||||||
CloudConfig,
|
CloudConfig,
|
||||||
NodeScheduling,
|
NodeScheduling,
|
||||||
PodAffinity,
|
PodAffinity,
|
||||||
@ -538,6 +540,18 @@ export default {
|
|||||||
|
|
||||||
return out;
|
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;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -870,6 +884,16 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-20">
|
||||||
|
<div class="col span-6">
|
||||||
|
<CpuModel
|
||||||
|
v-model:value="cpuModel"
|
||||||
|
:mode="mode"
|
||||||
|
@update:value="updateCpuModel"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<a
|
<a
|
||||||
v-if="showAdvanced"
|
v-if="showAdvanced"
|
||||||
|
|||||||
@ -617,6 +617,10 @@ harvester:
|
|||||||
virtualMachine:
|
virtualMachine:
|
||||||
label: Virtual Machines
|
label: Virtual Machines
|
||||||
osType: OS Type
|
osType: OS Type
|
||||||
|
cpuModel:
|
||||||
|
label: CPU Model
|
||||||
|
fetchError: 'Failed to load CPU model configuration: {error}'
|
||||||
|
optionLabel: "{modelName} ({count} {count, plural, one {node} other {nodes}})"
|
||||||
hotplug:
|
hotplug:
|
||||||
title: Enable CPU and memory hotplug
|
title: Enable CPU and memory hotplug
|
||||||
tooltip: The default maximum CPU and maximum memory are {hotPlugTimes} times based on CPU and memory.
|
tooltip: The default maximum CPU and maximum memory are {hotPlugTimes} times based on CPU and memory.
|
||||||
|
|||||||
@ -182,6 +182,7 @@ export default {
|
|||||||
immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW,
|
immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW,
|
||||||
terminationGracePeriodSeconds: '',
|
terminationGracePeriodSeconds: '',
|
||||||
cpuPinning: false,
|
cpuPinning: false,
|
||||||
|
cpuModel: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -394,6 +395,7 @@ export default {
|
|||||||
const efiPersistentStateEnabled = this.isEFIPersistentStateEnabled(spec);
|
const efiPersistentStateEnabled = this.isEFIPersistentStateEnabled(spec);
|
||||||
const secureBoot = this.isSecureBoot(spec);
|
const secureBoot = this.isSecureBoot(spec);
|
||||||
const cpuPinning = this.isCpuPinning(spec);
|
const cpuPinning = this.isCpuPinning(spec);
|
||||||
|
const cpuModel = spec.template.spec.domain.cpu?.model || '';
|
||||||
|
|
||||||
const secretRef = this.getSecret(spec);
|
const secretRef = this.getSecret(spec);
|
||||||
const accessCredentials = this.getAccessCredentials(spec);
|
const accessCredentials = this.getAccessCredentials(spec);
|
||||||
@ -431,6 +433,7 @@ export default {
|
|||||||
this['tpmPersistentStateEnabled'] = tpmPersistentStateEnabled;
|
this['tpmPersistentStateEnabled'] = tpmPersistentStateEnabled;
|
||||||
this['secureBoot'] = secureBoot;
|
this['secureBoot'] = secureBoot;
|
||||||
this['cpuPinning'] = cpuPinning;
|
this['cpuPinning'] = cpuPinning;
|
||||||
|
this['cpuModel'] = cpuModel;
|
||||||
|
|
||||||
this['hasCreateVolumes'] = hasCreateVolumes;
|
this['hasCreateVolumes'] = hasCreateVolumes;
|
||||||
this['networkRows'] = networkRows;
|
this['networkRows'] = networkRows;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user