diff --git a/pkg/harvester/config/feature-flags.js b/pkg/harvester/config/feature-flags.js index 6898bd06..05397eb4 100644 --- a/pkg/harvester/config/feature-flags.js +++ b/pkg/harvester/config/feature-flags.js @@ -44,7 +44,8 @@ const FEATURE_FLAGS = { 'csiOnlineExpandValidation', 'vmNetworkMigration', 'kubeovnVpcSubnet', - 'rancherClusterSetting' + 'rancherClusterSetting', + 'cpuMemoryHotplug' ] }; diff --git a/pkg/harvester/config/labels-annotations.js b/pkg/harvester/config/labels-annotations.js index 83383396..d38da610 100644 --- a/pkg/harvester/config/labels-annotations.js +++ b/pkg/harvester/config/labels-annotations.js @@ -70,5 +70,6 @@ export const HCI = { K8S_ARCH: 'kubernetes.io/arch', IMAGE_DISPLAY_NAME: 'harvesterhci.io/imageDisplayName', CUSTOM_IP: 'harvesterhci.io/custom-ip', - IMPORTED_IMAGE: 'migration.harvesterhci.io/imported' + IMPORTED_IMAGE: 'migration.harvesterhci.io/imported', + VM_CPU_MEMORY_HOTPLUG: 'harvesterhci.io/enableCPUAndMemoryHotplug', }; diff --git a/pkg/harvester/config/settings.ts b/pkg/harvester/config/settings.ts index 84f6b78f..799ccf78 100644 --- a/pkg/harvester/config/settings.ts +++ b/pkg/harvester/config/settings.ts @@ -38,6 +38,7 @@ export const HCI_SETTING = { UPGRADE_CONFIG: 'upgrade-config', VM_MIGRATION_NETWORK: 'vm-migration-network', RANCHER_CLUSTER: 'rancher-cluster', + MAX_HOTPLUG_RATIO: 'max-hotplug-ratio' }; export const HCI_ALLOWED_SETTINGS = { @@ -112,6 +113,7 @@ export const HCI_ALLOWED_SETTINGS = { [HCI_SETTING.RANCHER_CLUSTER]: { kind: 'custom', from: 'import', canReset: true, featureFlag: 'rancherClusterSetting' }, + [HCI_SETTING.MAX_HOTPLUG_RATIO]: { kind: 'number', featureFlag: 'cpuMemoryHotplug' }, }; export const HCI_SINGLE_CLUSTER_ALLOWED_SETTING = { diff --git a/pkg/harvester/detail/harvesterhci.io.virtualmachinebackup/index.vue b/pkg/harvester/detail/harvesterhci.io.virtualmachinebackup/index.vue index 8eae405c..5757e8c2 100644 --- a/pkg/harvester/detail/harvesterhci.io.virtualmachinebackup/index.vue +++ b/pkg/harvester/detail/harvesterhci.io.virtualmachinebackup/index.vue @@ -172,6 +172,9 @@ export default { :cpu="cpu" :mode="mode" :memory="memory" + :max-cpu="maxCpu" + :max-memory="maxMemory" + :enable-hot-plug="cpuMemoryHotplugEnabled" />
diff --git a/pkg/harvester/detail/harvesterhci.io.vmsnapshot/index.vue b/pkg/harvester/detail/harvesterhci.io.vmsnapshot/index.vue index f155af46..fc54aa70 100644 --- a/pkg/harvester/detail/harvesterhci.io.vmsnapshot/index.vue +++ b/pkg/harvester/detail/harvesterhci.io.vmsnapshot/index.vue @@ -172,6 +172,9 @@ export default { :cpu="cpu" :mode="mode" :memory="memory" + :max-cpu="maxCpu" + :max-memory="maxMemory" + :enable-hot-plug="cpuMemoryHotplugEnabled" />
diff --git a/pkg/harvester/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineBasics.vue b/pkg/harvester/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineBasics.vue index 2e3acf2a..7f92f48e 100644 --- a/pkg/harvester/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineBasics.vue +++ b/pkg/harvester/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineBasics.vue @@ -5,6 +5,7 @@ import CreateEditView from '@shell/mixins/create-edit-view'; import HarvesterIpAddress from '../../../formatters/HarvesterIpAddress'; import VMConsoleBar from '../../../components/VMConsoleBar'; import { HCI } from '../../../types'; +import { getVmCPUMemoryValues } from '../../../utils/cpuMemory'; const UNDEFINED = 'n/a'; @@ -91,9 +92,9 @@ export default { }, flavor() { - const domain = this.value?.spec?.template?.spec?.domain; + const { cpu, memory } = getVmCPUMemoryValues(this.value); - return `${ domain.cpu?.cores } vCPU , ${ domain.resources?.limits?.memory } ${ this.t('harvester.virtualMachine.input.memory') }`; + return `${ cpu } vCPU , ${ memory } ${ this.t('harvester.virtualMachine.input.memory') }`; }, kernelRelease() { diff --git a/pkg/harvester/dialog/HarvesterCPUMemoryHotPlugDialog.vue b/pkg/harvester/dialog/HarvesterCPUMemoryHotPlugDialog.vue new file mode 100644 index 00000000..a597107a --- /dev/null +++ b/pkg/harvester/dialog/HarvesterCPUMemoryHotPlugDialog.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue b/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue index 14ca5fd3..6c78ad7e 100644 --- a/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue +++ b/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue @@ -259,6 +259,9 @@ export default { diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineCpuMemory.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineCpuMemory.vue index b71333aa..165d71bb 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineCpuMemory.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineCpuMemory.vue @@ -2,23 +2,44 @@ import UnitInput from '@shell/components/form/UnitInput'; import InputOrDisplay from '@shell/components/InputOrDisplay'; import { GIBIBYTE } from '../../utils/unit'; +import { Checkbox } from '@components/Form/Checkbox'; +import { _VIEW } from '@shell/config/query-params'; +import { HCI } from '../../types'; +import { allHash } from '@shell/utils/promise'; +import { HCI_SETTING } from '../../config/settings'; + +const DEFAULT_HOT_PLUG_TIMES = 4; export default { name: 'HarvesterEditCpuMemory', emits: ['updateCpuMemory'], - components: { UnitInput, InputOrDisplay }, + components: { + UnitInput, InputOrDisplay, Checkbox + }, props: { cpu: { type: Number, default: null }, + maxCpu: { + type: Number, + default: null + }, memory: { type: String, default: null }, + maxMemory: { + type: String, + default: null + }, + enableHotPlug: { + type: Boolean, + default: false + }, mode: { type: String, default: 'create', @@ -29,21 +50,59 @@ export default { } }, + async fetch() { + const inStore = this.$store.getters['currentProduct'].inStore; + + const res = await allHash({ settings: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.SETTING }) }); + + this.settings = res.settings || []; + }, + data() { return { GIBIBYTE, - localCpu: this.cpu, - localMemory: this.memory + localCpu: this.cpu, + localMemory: this.memory, + maxLocalCpu: this.maxCpu, + maxLocalMemory: this.maxMemory, + localEnableHotPlug: this.enableHotPlug, + settings: [] }; }, computed: { - cupDisplay() { + isView() { + return this.mode === _VIEW; + }, + cpuDisplay() { return `${ this.localCpu } C`; }, + maxCpuDisplay() { + return `${ this.maxLocalCpu } C`; + }, + memoryDisplay() { return `${ this.localMemory }`; + }, + + maxMemoryDisplay() { + return `${ this.maxLocalMemory }`; + }, + + cpuMemoryHotplugTooltip() { + return this.t('harvester.virtualMachine.hotplug.tooltip', { hotPlugTimes: this.maxHotplugRatio }); + }, + + isCPUMemoryHotPlugFeatureEnabled() { + return this.$store.getters['harvester-common/getFeatureEnabled']('cpuMemoryHotplug'); + }, + + maxHotplugRatio() { + const maxHotPlugRatioSetting = this.settings.find((s) => s.id === HCI_SETTING.MAX_HOTPLUG_RATIO); + const maxPlugRatio = maxHotPlugRatioSetting?.value ? parseInt(maxHotPlugRatioSetting.value, 10) : DEFAULT_HOT_PLUG_TIMES; + + return maxPlugRatio; } }, @@ -55,71 +114,161 @@ export default { if (neu && !neu.includes('null')) { this.localMemory = neu; } - } + }, + maxCpu(neu) { + this.maxLocalCpu = neu; + }, + maxMemory(neu) { + if (neu && !neu.includes('null')) { + this.maxLocalMemory = neu; + } + }, + enableHotPlug(neu) { + this.localEnableHotPlug = neu; + }, + }, methods: { - change() { - let memory = ''; - - if (String(this.localMemory).includes('Gi')) { - memory = this.localMemory; + hotPlugChanged(neu) { + // If hot plug is enabled, we need to update the maxCpu and maxMemory values + if (neu) { + this.maxLocalCpu = this.localCpu ? this.localCpu * this.maxHotplugRatio : null; + this.maxLocalMemory = this.localMemory ? `${ parseInt(this.localMemory, 10) * this.maxHotplugRatio }${ GIBIBYTE }` : null; + this.$emit('updateCpuMemory', this.localCpu, this.localMemory, this.maxLocalCpu, this.maxLocalMemory, neu); } else { - memory = `${ this.localMemory }${ GIBIBYTE }`; + this.$emit('updateCpuMemory', this.localCpu, this.localMemory, '', null, neu); } - if (memory.includes('null')) { - memory = null; - } - this.$emit('updateCpuMemory', this.localCpu, memory); }, - + changeMemory() { + if (this.localEnableHotPlug) { + this.maxLocalMemory = this.localMemory ? `${ parseInt(this.localMemory, 10) * this.maxHotplugRatio }${ GIBIBYTE }` : null; + } + this.$emit('updateCpuMemory', this.localCpu, this.localMemory, this.maxLocalCpu, this.maxLocalMemory, this.localEnableHotPlug); + }, + changeCPU() { + if (this.localEnableHotPlug) { + this.maxLocalCpu = this.localCpu ? this.localCpu * this.maxHotplugRatio : null; + } + this.$emit('updateCpuMemory', this.localCpu, this.localMemory, this.maxLocalCpu, this.maxLocalMemory, this.localEnableHotPlug); + }, + changeMaxCPUMemory() { + this.$emit('updateCpuMemory', this.localCpu, this.localMemory, this.maxLocalCpu, this.maxLocalMemory, this.localEnableHotPlug); + }, } }; diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey.vue index c0eeaef8..ad22942b 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey.vue @@ -240,7 +240,7 @@ export default {