mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2025-12-13 21:21:44 +00:00
feat: CPU / Memory hotplug support (#413)
* feat: add maxCPU and maxMemory Signed-off-by: Andy Lee <andy.lee@suse.com> * feat: add hotplug dialog Signed-off-by: Andy Lee <andy.lee@suse.com> * feat: add restart message Signed-off-by: Andy Lee <andy.lee@suse.com> * feat: let VM template support cpuMemoryHotplug Signed-off-by: Andy Lee <andy.lee@suse.com> * feat: add feature flag Signed-off-by: Andy Lee <andy.lee@suse.com> * feat: add max-hotplug-ratio setting Signed-off-by: Andy Lee <andy.lee@suse.com> --------- Signed-off-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
parent
e294f4c00f
commit
be9311dc0c
@ -44,7 +44,8 @@ const FEATURE_FLAGS = {
|
|||||||
'csiOnlineExpandValidation',
|
'csiOnlineExpandValidation',
|
||||||
'vmNetworkMigration',
|
'vmNetworkMigration',
|
||||||
'kubeovnVpcSubnet',
|
'kubeovnVpcSubnet',
|
||||||
'rancherClusterSetting'
|
'rancherClusterSetting',
|
||||||
|
'cpuMemoryHotplug'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -70,5 +70,6 @@ export const HCI = {
|
|||||||
K8S_ARCH: 'kubernetes.io/arch',
|
K8S_ARCH: 'kubernetes.io/arch',
|
||||||
IMAGE_DISPLAY_NAME: 'harvesterhci.io/imageDisplayName',
|
IMAGE_DISPLAY_NAME: 'harvesterhci.io/imageDisplayName',
|
||||||
CUSTOM_IP: 'harvesterhci.io/custom-ip',
|
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',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -38,6 +38,7 @@ export const HCI_SETTING = {
|
|||||||
UPGRADE_CONFIG: 'upgrade-config',
|
UPGRADE_CONFIG: 'upgrade-config',
|
||||||
VM_MIGRATION_NETWORK: 'vm-migration-network',
|
VM_MIGRATION_NETWORK: 'vm-migration-network',
|
||||||
RANCHER_CLUSTER: 'rancher-cluster',
|
RANCHER_CLUSTER: 'rancher-cluster',
|
||||||
|
MAX_HOTPLUG_RATIO: 'max-hotplug-ratio'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HCI_ALLOWED_SETTINGS = {
|
export const HCI_ALLOWED_SETTINGS = {
|
||||||
@ -112,6 +113,7 @@ export const HCI_ALLOWED_SETTINGS = {
|
|||||||
[HCI_SETTING.RANCHER_CLUSTER]: {
|
[HCI_SETTING.RANCHER_CLUSTER]: {
|
||||||
kind: 'custom', from: 'import', canReset: true, featureFlag: 'rancherClusterSetting'
|
kind: 'custom', from: 'import', canReset: true, featureFlag: 'rancherClusterSetting'
|
||||||
},
|
},
|
||||||
|
[HCI_SETTING.MAX_HOTPLUG_RATIO]: { kind: 'number', featureFlag: 'cpuMemoryHotplug' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HCI_SINGLE_CLUSTER_ALLOWED_SETTING = {
|
export const HCI_SINGLE_CLUSTER_ALLOWED_SETTING = {
|
||||||
|
|||||||
@ -172,6 +172,9 @@ export default {
|
|||||||
:cpu="cpu"
|
:cpu="cpu"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:memory="memory"
|
:memory="memory"
|
||||||
|
:max-cpu="maxCpu"
|
||||||
|
:max-memory="maxMemory"
|
||||||
|
:enable-hot-plug="cpuMemoryHotplugEnabled"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
@ -172,6 +172,9 @@ export default {
|
|||||||
:cpu="cpu"
|
:cpu="cpu"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:memory="memory"
|
:memory="memory"
|
||||||
|
:max-cpu="maxCpu"
|
||||||
|
:max-memory="maxMemory"
|
||||||
|
:enable-hot-plug="cpuMemoryHotplugEnabled"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="row mb-10">
|
<div class="row mb-10">
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import CreateEditView from '@shell/mixins/create-edit-view';
|
|||||||
import HarvesterIpAddress from '../../../formatters/HarvesterIpAddress';
|
import HarvesterIpAddress from '../../../formatters/HarvesterIpAddress';
|
||||||
import VMConsoleBar from '../../../components/VMConsoleBar';
|
import VMConsoleBar from '../../../components/VMConsoleBar';
|
||||||
import { HCI } from '../../../types';
|
import { HCI } from '../../../types';
|
||||||
|
import { getVmCPUMemoryValues } from '../../../utils/cpuMemory';
|
||||||
|
|
||||||
const UNDEFINED = 'n/a';
|
const UNDEFINED = 'n/a';
|
||||||
|
|
||||||
@ -91,9 +92,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
flavor() {
|
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() {
|
kernelRelease() {
|
||||||
|
|||||||
185
pkg/harvester/dialog/HarvesterCPUMemoryHotPlugDialog.vue
Normal file
185
pkg/harvester/dialog/HarvesterCPUMemoryHotPlugDialog.vue
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
<script>
|
||||||
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import { getVmCPUMemoryValues } from '../utils/cpuMemory';
|
||||||
|
import UnitInput from '@shell/components/form/UnitInput';
|
||||||
|
import { Card } from '@components/Card';
|
||||||
|
import { Banner } from '@components/Banner';
|
||||||
|
import AsyncButton from '@shell/components/AsyncButton';
|
||||||
|
import { GIBIBYTE } from '../utils/unit';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CPUMemoryHotplugModal',
|
||||||
|
|
||||||
|
emits: ['close'],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
AsyncButton, Card, Banner, UnitInput
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
resources: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
const { cpu, memory } = getVmCPUMemoryValues(this.resources[0] || {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
cpu,
|
||||||
|
memory,
|
||||||
|
errors: [],
|
||||||
|
GIBIBYTE
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapGetters({ t: 'i18n/t' }),
|
||||||
|
|
||||||
|
actionResource() {
|
||||||
|
return this.resources[0] || {};
|
||||||
|
},
|
||||||
|
maxResourcesMessage() {
|
||||||
|
const { maxCpu, maxMemory } = getVmCPUMemoryValues(this.actionResource);
|
||||||
|
|
||||||
|
if (maxCpu && maxMemory) {
|
||||||
|
return this.t('harvester.modal.cpuMemoryHotplug.maxResourcesMessage', { maxCpu, maxMemory });
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
close() {
|
||||||
|
this.cpu = '';
|
||||||
|
this.memory = '';
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
|
||||||
|
change() {
|
||||||
|
if (parseInt(this.memory, 10) < 1 ) {
|
||||||
|
this.memory = '1Gi';
|
||||||
|
}
|
||||||
|
if (this.cpu < 1) {
|
||||||
|
this.cpu = 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async save(buttonCb) {
|
||||||
|
if (this.actionResource) {
|
||||||
|
try {
|
||||||
|
const res = await this.actionResource.doAction('cpuAndMemoryHotplug', { sockets: this.cpu, memory: this.memory });
|
||||||
|
|
||||||
|
if (res._status === 200 || res._status === 204) {
|
||||||
|
this.$store.dispatch('growl/success', {
|
||||||
|
title: this.t('generic.notification.title.succeed'),
|
||||||
|
message: this.t('harvester.modal.cpuMemoryHotplug.success', { vm: this.actionResource.nameDisplay })
|
||||||
|
}, { root: true });
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
buttonCb(true);
|
||||||
|
} else {
|
||||||
|
const error = [res?.data] || exceptionToErrorsArray(res);
|
||||||
|
|
||||||
|
this['errors'] = error;
|
||||||
|
buttonCb(false);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const error = err?.data || err;
|
||||||
|
const message = exceptionToErrorsArray(error);
|
||||||
|
|
||||||
|
this['errors'] = message;
|
||||||
|
buttonCb(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Card
|
||||||
|
ref="modal"
|
||||||
|
name="modal"
|
||||||
|
:show-highlight-border="false"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<h4
|
||||||
|
v-clean-html="t('harvester.modal.cpuMemoryHotplug.title')"
|
||||||
|
class="text-default-text"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body>
|
||||||
|
<Banner
|
||||||
|
v-if="maxResourcesMessage"
|
||||||
|
:label="maxResourcesMessage"
|
||||||
|
color="info"
|
||||||
|
/>
|
||||||
|
<UnitInput
|
||||||
|
v-model:value="cpu"
|
||||||
|
:label="t('harvester.virtualMachine.input.cpu')"
|
||||||
|
:delay="0"
|
||||||
|
required
|
||||||
|
suffix="C"
|
||||||
|
class="mb-20"
|
||||||
|
:mode="mode"
|
||||||
|
:min="1"
|
||||||
|
@update:value="change"
|
||||||
|
/>
|
||||||
|
<UnitInput
|
||||||
|
v-model:value="memory"
|
||||||
|
:label="t('harvester.virtualMachine.input.memory')"
|
||||||
|
:mode="mode"
|
||||||
|
:input-exponent="3"
|
||||||
|
:delay="0"
|
||||||
|
:min="1"
|
||||||
|
:increment="1024"
|
||||||
|
:output-modifier="true"
|
||||||
|
:disabled="disabled"
|
||||||
|
:suffix="GIBIBYTE"
|
||||||
|
class="mb-20"
|
||||||
|
@update:value="change"
|
||||||
|
/>
|
||||||
|
<Banner
|
||||||
|
v-for="(err, i) in errors"
|
||||||
|
:key="i"
|
||||||
|
:label="err"
|
||||||
|
color="error"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<div class="actions">
|
||||||
|
<div class="buttons">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn role-secondary mr-10"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
{{ t('generic.cancel') }}
|
||||||
|
</button>
|
||||||
|
<AsyncButton
|
||||||
|
mode="apply"
|
||||||
|
@click="save"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.actions {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -259,6 +259,9 @@ export default {
|
|||||||
<CpuMemory
|
<CpuMemory
|
||||||
:cpu="cpu"
|
:cpu="cpu"
|
||||||
:memory="memory"
|
:memory="memory"
|
||||||
|
:max-cpu="maxCpu"
|
||||||
|
:max-memory="maxMemory"
|
||||||
|
:enable-hot-plug="cpuMemoryHotplugEnabled"
|
||||||
:disabled="isConfig"
|
:disabled="isConfig"
|
||||||
@updateCpuMemory="updateCpuMemory"
|
@updateCpuMemory="updateCpuMemory"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,23 +2,44 @@
|
|||||||
import UnitInput from '@shell/components/form/UnitInput';
|
import UnitInput from '@shell/components/form/UnitInput';
|
||||||
import InputOrDisplay from '@shell/components/InputOrDisplay';
|
import InputOrDisplay from '@shell/components/InputOrDisplay';
|
||||||
import { GIBIBYTE } from '../../utils/unit';
|
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 {
|
export default {
|
||||||
name: 'HarvesterEditCpuMemory',
|
name: 'HarvesterEditCpuMemory',
|
||||||
|
|
||||||
emits: ['updateCpuMemory'],
|
emits: ['updateCpuMemory'],
|
||||||
|
|
||||||
components: { UnitInput, InputOrDisplay },
|
components: {
|
||||||
|
UnitInput, InputOrDisplay, Checkbox
|
||||||
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
cpu: {
|
cpu: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
maxCpu: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
memory: {
|
memory: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
maxMemory: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
enableHotPlug: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
mode: {
|
mode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'create',
|
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() {
|
data() {
|
||||||
return {
|
return {
|
||||||
GIBIBYTE,
|
GIBIBYTE,
|
||||||
localCpu: this.cpu,
|
localCpu: this.cpu,
|
||||||
localMemory: this.memory
|
localMemory: this.memory,
|
||||||
|
maxLocalCpu: this.maxCpu,
|
||||||
|
maxLocalMemory: this.maxMemory,
|
||||||
|
localEnableHotPlug: this.enableHotPlug,
|
||||||
|
settings: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
cupDisplay() {
|
isView() {
|
||||||
|
return this.mode === _VIEW;
|
||||||
|
},
|
||||||
|
cpuDisplay() {
|
||||||
return `${ this.localCpu } C`;
|
return `${ this.localCpu } C`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
maxCpuDisplay() {
|
||||||
|
return `${ this.maxLocalCpu } C`;
|
||||||
|
},
|
||||||
|
|
||||||
memoryDisplay() {
|
memoryDisplay() {
|
||||||
return `${ this.localMemory }`;
|
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,35 +114,60 @@ export default {
|
|||||||
if (neu && !neu.includes('null')) {
|
if (neu && !neu.includes('null')) {
|
||||||
this.localMemory = neu;
|
this.localMemory = neu;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
maxCpu(neu) {
|
||||||
|
this.maxLocalCpu = neu;
|
||||||
|
},
|
||||||
|
maxMemory(neu) {
|
||||||
|
if (neu && !neu.includes('null')) {
|
||||||
|
this.maxLocalMemory = neu;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
enableHotPlug(neu) {
|
||||||
|
this.localEnableHotPlug = neu;
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
change() {
|
hotPlugChanged(neu) {
|
||||||
let memory = '';
|
// If hot plug is enabled, we need to update the maxCpu and maxMemory values
|
||||||
|
if (neu) {
|
||||||
if (String(this.localMemory).includes('Gi')) {
|
this.maxLocalCpu = this.localCpu ? this.localCpu * this.maxHotplugRatio : null;
|
||||||
memory = this.localMemory;
|
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 {
|
} 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);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col span-6 mb-10">
|
<div class="col span-6 mb-10">
|
||||||
<InputOrDisplay
|
<InputOrDisplay
|
||||||
name="CPU"
|
name="CPU"
|
||||||
:value="cupDisplay"
|
:value="cpuDisplay"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
|
class="mb-10"
|
||||||
>
|
>
|
||||||
<UnitInput
|
<UnitInput
|
||||||
v-model:value="localCpu"
|
v-model:value="localCpu"
|
||||||
@ -94,11 +178,10 @@ export default {
|
|||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
@update:value="change"
|
@update:value="changeCPU"
|
||||||
/>
|
/>
|
||||||
</InputOrDisplay>
|
</InputOrDisplay>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col span-6 mb-10">
|
<div class="col span-6 mb-10">
|
||||||
<InputOrDisplay
|
<InputOrDisplay
|
||||||
:name="t('harvester.virtualMachine.input.memory')"
|
:name="t('harvester.virtualMachine.input.memory')"
|
||||||
@ -117,9 +200,75 @@ export default {
|
|||||||
required
|
required
|
||||||
:suffix="GIBIBYTE"
|
:suffix="GIBIBYTE"
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
@update:value="change"
|
@update:value="changeMemory"
|
||||||
/>
|
/>
|
||||||
</InputOrDisplay>
|
</InputOrDisplay>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="isCPUMemoryHotPlugFeatureEnabled"
|
||||||
|
class="row"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
v-model:value="localEnableHotPlug"
|
||||||
|
class="check"
|
||||||
|
type="checkbox"
|
||||||
|
:label="t('harvester.virtualMachine.hotplug.title')"
|
||||||
|
:disabled="isView"
|
||||||
|
@update:value="hotPlugChanged"
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
v-clean-tooltip="{content: cpuMemoryHotplugTooltip, triggers: ['hover', 'touch', 'focus'] }"
|
||||||
|
v-stripped-aria-label="cpuMemoryHotplugTooltip"
|
||||||
|
class="icon icon-info"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="localEnableHotPlug && isCPUMemoryHotPlugFeatureEnabled"
|
||||||
|
class="row"
|
||||||
|
>
|
||||||
|
<div class="col span-6 mb-10">
|
||||||
|
<InputOrDisplay
|
||||||
|
:name="t('harvester.virtualMachine.input.maxCpu')"
|
||||||
|
:value="maxCpuDisplay"
|
||||||
|
:mode="mode"
|
||||||
|
class="mt-20"
|
||||||
|
>
|
||||||
|
<UnitInput
|
||||||
|
v-model:value="maxLocalCpu"
|
||||||
|
:label="t('harvester.virtualMachine.input.maxCpu')"
|
||||||
|
suffix="C"
|
||||||
|
:delay="0"
|
||||||
|
:disabled="disabled"
|
||||||
|
:mode="mode"
|
||||||
|
class="mt-20"
|
||||||
|
@update:value="changeMaxCPUMemory"
|
||||||
|
/>
|
||||||
|
</InputOrDisplay>
|
||||||
|
</div>
|
||||||
|
<div class="col span-6 mb-10">
|
||||||
|
<InputOrDisplay
|
||||||
|
:name="t('harvester.virtualMachine.input.maxMemory')"
|
||||||
|
:value="maxMemoryDisplay"
|
||||||
|
:mode="mode"
|
||||||
|
class="mt-20"
|
||||||
|
>
|
||||||
|
<UnitInput
|
||||||
|
v-model:value="maxLocalMemory"
|
||||||
|
:label="t('harvester.virtualMachine.input.maxMemory')"
|
||||||
|
:mode="mode"
|
||||||
|
:input-exponent="3"
|
||||||
|
:delay="0"
|
||||||
|
:increment="1024"
|
||||||
|
:output-modifier="true"
|
||||||
|
:disabled="disabled"
|
||||||
|
:suffix="GIBIBYTE"
|
||||||
|
class="mt-20"
|
||||||
|
@update:value="changeMaxCPUMemory"
|
||||||
|
/>
|
||||||
|
</InputOrDisplay>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -240,7 +240,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="mt-20">
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
v-model:value="checkedSsh"
|
v-model:value="checkedSsh"
|
||||||
:label="t('harvester.virtualMachine.input.sshKey')"
|
:label="t('harvester.virtualMachine.input.sshKey')"
|
||||||
|
|||||||
@ -466,7 +466,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const errors = this.getAccessCredentialsValidation();
|
const cpuMemoryErrors = this.getCPUMemoryValidation();
|
||||||
|
const accessCredentialsErrors = this.getAccessCredentialsValidation();
|
||||||
|
const errors = [...cpuMemoryErrors, ...accessCredentialsErrors];
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return Promise.reject(errors);
|
return Promise.reject(errors);
|
||||||
@ -604,8 +606,11 @@ export default {
|
|||||||
>
|
>
|
||||||
<CpuMemory
|
<CpuMemory
|
||||||
:cpu="cpu"
|
:cpu="cpu"
|
||||||
|
:max-cpu="maxCpu"
|
||||||
:memory="memory"
|
:memory="memory"
|
||||||
|
:max-memory="maxMemory"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
|
:enable-hot-plug="cpuMemoryHotplugEnabled"
|
||||||
@updateCpuMemory="updateCpuMemory"
|
@updateCpuMemory="updateCpuMemory"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@ -157,6 +157,10 @@ harvester:
|
|||||||
hotplug:
|
hotplug:
|
||||||
success: 'Volume { diskName } is mounted to the virtual machine { vm }.'
|
success: 'Volume { diskName } is mounted to the virtual machine { vm }.'
|
||||||
title: Add Volume
|
title: Add Volume
|
||||||
|
cpuMemoryHotplug:
|
||||||
|
success: 'CPU and Memory are updated to the virtual machine { vm }.'
|
||||||
|
title: Edit CPU and Memory
|
||||||
|
maxResourcesMessage: 'You can increase the CPU to maximum { maxCpu }C and memory to maximum { maxMemory }.'
|
||||||
hotunplug:
|
hotunplug:
|
||||||
success: 'Volume { name } is detached successfully.'
|
success: 'Volume { name } is detached successfully.'
|
||||||
snapshot:
|
snapshot:
|
||||||
@ -213,6 +217,7 @@ harvester:
|
|||||||
suspendSchedule: Suspend
|
suspendSchedule: Suspend
|
||||||
restoreExistingVM: Replace Existing
|
restoreExistingVM: Replace Existing
|
||||||
migrate: Migrate
|
migrate: Migrate
|
||||||
|
cpuAndMemoryHotplug: Edit CPU and Memory
|
||||||
abortMigration: Abort Migration
|
abortMigration: Abort Migration
|
||||||
createTemplate: Generate Template
|
createTemplate: Generate Template
|
||||||
enableMaintenance: Enable Maintenance Mode
|
enableMaintenance: Enable Maintenance Mode
|
||||||
@ -571,6 +576,10 @@ harvester:
|
|||||||
virtualMachine:
|
virtualMachine:
|
||||||
label: Virtual Machines
|
label: Virtual Machines
|
||||||
osType: OS Type
|
osType: OS Type
|
||||||
|
hotplug:
|
||||||
|
title: Enable CPU and memory hotplug
|
||||||
|
tooltip: The default maximum CPU and maximum memory are {hotPlugTimes} times based on CPU and memory.
|
||||||
|
restartVMMessage: Restart action is required for the virtual machine configuration change to take effect
|
||||||
instance:
|
instance:
|
||||||
singleInstance:
|
singleInstance:
|
||||||
multipleInstance:
|
multipleInstance:
|
||||||
@ -716,6 +725,9 @@ harvester:
|
|||||||
invalidUser: Invalid Username.
|
invalidUser: Invalid Username.
|
||||||
input:
|
input:
|
||||||
name: Name
|
name: Name
|
||||||
|
cpu: CPU
|
||||||
|
maxCpu: Maximum CPU
|
||||||
|
maxMemory: Maximum Memory
|
||||||
memory: Memory
|
memory: Memory
|
||||||
image: Image
|
image: Image
|
||||||
sshKey: SSHKey
|
sshKey: SSHKey
|
||||||
@ -1712,6 +1724,7 @@ advancedSettings:
|
|||||||
'harv-upgrade-config': 'Configure image preloading and VM restore options for upgrades. See related fields in <a href="{url}" target="_blank" rel="noopener">settings/upgrade-config</a>'
|
'harv-upgrade-config': 'Configure image preloading and VM restore options for upgrades. See related fields in <a href="{url}" target="_blank" rel="noopener">settings/upgrade-config</a>'
|
||||||
'harv-vm-migration-network': 'Segregated network for VM migration traffic.'
|
'harv-vm-migration-network': 'Segregated network for VM migration traffic.'
|
||||||
'harv-rancher-cluster': 'Configure Rancher cluster integration settings for guest cluster management.'
|
'harv-rancher-cluster': 'Configure Rancher cluster integration settings for guest cluster management.'
|
||||||
|
'harv-max-hotplug-ratio': 'The ratio for kubevirt to limit the maximum CPU and memory that can be hotplugged to a VM. The value could be an integer between 1 and 20, default to 4.'
|
||||||
|
|
||||||
typeLabel:
|
typeLabel:
|
||||||
kubevirt.io.virtualmachine: |-
|
kubevirt.io.virtualmachine: |-
|
||||||
|
|||||||
@ -22,8 +22,8 @@ export const VM_HEADERS = [
|
|||||||
{
|
{
|
||||||
name: 'CPU',
|
name: 'CPU',
|
||||||
label: 'CPU',
|
label: 'CPU',
|
||||||
sort: ['spec.template.spec.domain.cpu.cores'],
|
sort: ['displayCPU'],
|
||||||
value: 'spec.template.spec.domain.cpu.cores',
|
value: 'displayCPU',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
dashIfEmpty: true,
|
dashIfEmpty: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,10 +3,9 @@ import jsyaml from 'js-yaml';
|
|||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import difference from 'lodash/difference';
|
import difference from 'lodash/difference';
|
||||||
|
|
||||||
import { sortBy } from '@shell/utils/sort';
|
import { sortBy } from '@shell/utils/sort';
|
||||||
import { set } from '@shell/utils/object';
|
import { set } from '@shell/utils/object';
|
||||||
|
import { getVmCPUMemoryValues } from '../../utils/cpuMemory';
|
||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import { randomStr } from '@shell/utils/string';
|
import { randomStr } from '@shell/utils/string';
|
||||||
import { base64Decode } from '@shell/utils/crypto';
|
import { base64Decode } from '@shell/utils/crypto';
|
||||||
@ -164,6 +163,9 @@ export default {
|
|||||||
deleteAgent: true,
|
deleteAgent: true,
|
||||||
memory: null,
|
memory: null,
|
||||||
cpu: '',
|
cpu: '',
|
||||||
|
maxMemory: null,
|
||||||
|
maxCpu: '',
|
||||||
|
cpuMemoryHotplugEnabled: false,
|
||||||
reservedMemory: null,
|
reservedMemory: null,
|
||||||
accessCredentials: [],
|
accessCredentials: [],
|
||||||
efiEnabled: false,
|
efiEnabled: false,
|
||||||
@ -348,8 +350,11 @@ export default {
|
|||||||
const runStrategy = spec.runStrategy || 'RerunOnFailure';
|
const runStrategy = spec.runStrategy || 'RerunOnFailure';
|
||||||
const machineType = spec.template.spec.domain?.machine?.type || this.machineTypes[0];
|
const machineType = spec.template.spec.domain?.machine?.type || this.machineTypes[0];
|
||||||
|
|
||||||
const cpu = spec.template.spec.domain?.cpu?.cores;
|
const {
|
||||||
const memory = spec.template.spec.domain.resources.limits.memory;
|
cpu, memory, maxCpu, maxMemory, isHotplugEnabled
|
||||||
|
} = getVmCPUMemoryValues(vm);
|
||||||
|
const cpuMemoryHotplugEnabled = isHotplugEnabled;
|
||||||
|
|
||||||
const reservedMemory = vm.metadata?.annotations?.[HCI_ANNOTATIONS.VM_RESERVED_MEMORY];
|
const reservedMemory = vm.metadata?.annotations?.[HCI_ANNOTATIONS.VM_RESERVED_MEMORY];
|
||||||
const terminationGracePeriodSeconds = spec.template.spec?.terminationGracePeriodSeconds || this.defaultTerminationSetting;
|
const terminationGracePeriodSeconds = spec.template.spec?.terminationGracePeriodSeconds || this.defaultTerminationSetting;
|
||||||
|
|
||||||
@ -409,6 +414,9 @@ export default {
|
|||||||
|
|
||||||
this['cpu'] = cpu;
|
this['cpu'] = cpu;
|
||||||
this['memory'] = memory;
|
this['memory'] = memory;
|
||||||
|
this['maxCpu'] = maxCpu;
|
||||||
|
this['maxMemory'] = maxMemory;
|
||||||
|
this['cpuMemoryHotplugEnabled'] = cpuMemoryHotplugEnabled;
|
||||||
this['reservedMemory'] = reservedMemory;
|
this['reservedMemory'] = reservedMemory;
|
||||||
this['machineType'] = machineType;
|
this['machineType'] = machineType;
|
||||||
this['terminationGracePeriodSeconds'] = terminationGracePeriodSeconds;
|
this['terminationGracePeriodSeconds'] = terminationGracePeriodSeconds;
|
||||||
@ -633,20 +641,26 @@ export default {
|
|||||||
this.spec.template.spec.domain.machine['type'] = this.machineType;
|
this.spec.template.spec.domain.machine['type'] = this.machineType;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.spec.template.spec.domain.cpu.cores = this.cpu;
|
this.setCPUAndMemory();
|
||||||
this.spec.template.spec.domain.resources.limits.cpu = this.cpu ? this.cpu.toString() : this.cpu;
|
// update terminationGracePeriodSeconds
|
||||||
this.spec.template.spec.domain.resources.limits.memory = this.memory;
|
|
||||||
this.spec.template.spec.terminationGracePeriodSeconds = this.terminationGracePeriodSeconds;
|
this.spec.template.spec.terminationGracePeriodSeconds = this.terminationGracePeriodSeconds;
|
||||||
|
|
||||||
// parse reserved memory
|
|
||||||
const vm = this.resourceType === HCI.VM ? this.value : this.value.spec.vm;
|
const vm = this.resourceType === HCI.VM ? this.value : this.value.spec.vm;
|
||||||
|
|
||||||
|
// parse reserved memory
|
||||||
if (!this.reservedMemory) {
|
if (!this.reservedMemory) {
|
||||||
delete vm.metadata.annotations[HCI_ANNOTATIONS.VM_RESERVED_MEMORY];
|
delete vm.metadata.annotations[HCI_ANNOTATIONS.VM_RESERVED_MEMORY];
|
||||||
} else {
|
} else {
|
||||||
vm.metadata.annotations[HCI_ANNOTATIONS.VM_RESERVED_MEMORY] = this.reservedMemory;
|
vm.metadata.annotations[HCI_ANNOTATIONS.VM_RESERVED_MEMORY] = this.reservedMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add or remove cpu memory hotplug annotation
|
||||||
|
if (this.cpuMemoryHotplugEnabled) {
|
||||||
|
vm.metadata.annotations[HCI_ANNOTATIONS.VM_CPU_MEMORY_HOTPLUG] = this.cpuMemoryHotplugEnabled.toString();
|
||||||
|
} else {
|
||||||
|
delete vm.metadata.annotations[HCI_ANNOTATIONS.VM_CPU_MEMORY_HOTPLUG];
|
||||||
|
}
|
||||||
|
|
||||||
if (this.maintenanceStrategy === 'Migrate') {
|
if (this.maintenanceStrategy === 'Migrate') {
|
||||||
delete vm.metadata.labels[HCI_ANNOTATIONS.VM_MAINTENANCE_MODE_STRATEGY];
|
delete vm.metadata.labels[HCI_ANNOTATIONS.VM_MAINTENANCE_MODE_STRATEGY];
|
||||||
} else {
|
} else {
|
||||||
@ -654,6 +668,36 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setCPUAndMemory() {
|
||||||
|
if (this.cpuMemoryHotplugEnabled) {
|
||||||
|
// set CPU
|
||||||
|
this.spec.template.spec.domain.cpu.sockets = this.cpu;
|
||||||
|
this.spec.template.spec.domain.cpu.cores = 1;
|
||||||
|
|
||||||
|
// set max CPU
|
||||||
|
set(this.spec.template.spec, 'domain.cpu.maxSockets', this.maxCpu);
|
||||||
|
// domain.resources.limits.cpu and memory are defined by k8s which requires string values
|
||||||
|
// see https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/
|
||||||
|
set(this.spec.template.spec, 'domain.resources.limits.cpu', this.maxCpu?.toString());
|
||||||
|
|
||||||
|
// set memory
|
||||||
|
set(this.spec.template.spec, 'domain.memory.guest', this.memory);
|
||||||
|
|
||||||
|
// set max memory
|
||||||
|
set(this.spec.template.spec, 'domain.memory.maxGuest', this.maxMemory);
|
||||||
|
set(this.spec.template.spec, 'domain.resources.limits.memory', this.maxMemory);
|
||||||
|
} else {
|
||||||
|
this.spec.template.spec.domain.cpu.sockets = 1;
|
||||||
|
this.spec.template.spec.domain.cpu.cores = this.cpu;
|
||||||
|
this.spec.template.spec.domain.resources.limits.cpu = this.cpu?.toString();
|
||||||
|
this.spec.template.spec.domain.resources.limits.memory = this.memory;
|
||||||
|
// clean
|
||||||
|
delete this.spec.template.spec.resources;
|
||||||
|
delete this.spec.template.spec.domain.memory;
|
||||||
|
delete this.spec.template.spec.domain.cpu.maxSockets;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
parseDiskRows(disk) {
|
parseDiskRows(disk) {
|
||||||
const disks = [];
|
const disks = [];
|
||||||
const volumes = [];
|
const volumes = [];
|
||||||
@ -952,9 +996,12 @@ export default {
|
|||||||
this['sshKey'] = neu;
|
this['sshKey'] = neu;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateCpuMemory(cpu, memory) {
|
updateCpuMemory(cpu, memory, maxCpu = '', maxMemory = null, cpuMemoryHotplugEnabled = false) {
|
||||||
this['cpu'] = cpu;
|
this['cpu'] = cpu;
|
||||||
this['memory'] = memory;
|
this['memory'] = memory;
|
||||||
|
this['maxCpu'] = maxCpu;
|
||||||
|
this['maxMemory'] = maxMemory;
|
||||||
|
this['cpuMemoryHotplugEnabled'] = cpuMemoryHotplugEnabled;
|
||||||
},
|
},
|
||||||
|
|
||||||
parseDisk(R, index) {
|
parseDisk(R, index) {
|
||||||
@ -1311,6 +1358,21 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCPUMemoryValidation() {
|
||||||
|
const errors = [];
|
||||||
|
const { cpu, memory } = this;
|
||||||
|
|
||||||
|
if ((!cpu)) {
|
||||||
|
errors.push(this.t('validation.required', { key: this.t('harvester.virtualMachine.input.cpu') }, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!memory)) {
|
||||||
|
errors.push(this.t('validation.required', { key: this.t('harvester.virtualMachine.input.memory') }, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
},
|
||||||
|
|
||||||
getAccessCredentialsValidation() {
|
getAccessCredentialsValidation() {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { parseVolumeClaimTemplates } from '@pkg/utils/vm';
|
|||||||
import { BACKUP_TYPE } from '../config/types';
|
import { BACKUP_TYPE } from '../config/types';
|
||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
import HarvesterResource from './harvester';
|
import HarvesterResource from './harvester';
|
||||||
|
import { getVmCPUMemoryValues } from '../utils/cpuMemory';
|
||||||
|
|
||||||
export const OFF = 'Off';
|
export const OFF = 'Off';
|
||||||
|
|
||||||
@ -169,6 +170,12 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
icon: 'icon icon-storage',
|
icon: 'icon icon-storage',
|
||||||
label: this.t('harvester.action.editVMQuota')
|
label: this.t('harvester.action.editVMQuota')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
action: 'cpuMemoryHotplug',
|
||||||
|
enabled: !!this.actions?.cpuAndMemoryHotplug,
|
||||||
|
icon: 'icon icon-os-management',
|
||||||
|
label: this.t('harvester.action.cpuAndMemoryHotplug')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
action: 'createSchedule',
|
action: 'createSchedule',
|
||||||
enabled: this.schedulingVMBackupFeatureEnabled,
|
enabled: this.schedulingVMBackupFeatureEnabled,
|
||||||
@ -476,6 +483,13 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpuMemoryHotplug(resources = this) {
|
||||||
|
this.$dispatch('promptModal', {
|
||||||
|
resources,
|
||||||
|
component: 'HarvesterCPUMemoryHotPlugDialog'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
abortMigrationVM() {
|
abortMigrationVM() {
|
||||||
this.doActionGrowl('abortMigration', {});
|
this.doActionGrowl('abortMigration', {});
|
||||||
}
|
}
|
||||||
@ -1091,19 +1105,6 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
maxLength: 63,
|
maxLength: 63,
|
||||||
translationKey: 'harvester.fields.name'
|
translationKey: 'harvester.fields.name'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
nullable: false,
|
|
||||||
path: 'spec.template.spec.domain.cpu.cores',
|
|
||||||
min: 1,
|
|
||||||
required: true,
|
|
||||||
translationKey: 'harvester.fields.cpu'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nullable: false,
|
|
||||||
path: 'spec.template.spec.domain.resources.limits.memory',
|
|
||||||
required: true,
|
|
||||||
translationKey: 'harvester.fields.memory'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
nullable: false,
|
nullable: false,
|
||||||
path: 'spec.template.spec',
|
path: 'spec.template.spec',
|
||||||
@ -1127,12 +1128,11 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get memorySort() {
|
get memorySort() {
|
||||||
const memory =
|
const memory = getVmCPUMemoryValues(this).memory;
|
||||||
this?.spec?.template?.spec?.domain?.resources?.requests?.memory || 0;
|
|
||||||
|
|
||||||
const formatSize = parseSi(memory);
|
const formatSize = parseSi(memory);
|
||||||
|
|
||||||
return parseInt(formatSize);
|
return parseInt(formatSize, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
get ingoreVMMessage() {
|
get ingoreVMMessage() {
|
||||||
@ -1158,14 +1158,22 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get stateDescription() {
|
get stateDescription() {
|
||||||
|
const conditions = get(this, 'status.conditions');
|
||||||
|
const restartRequired = findBy(conditions, 'type', 'RestartRequired');
|
||||||
|
|
||||||
|
if (restartRequired && restartRequired.status === 'True') {
|
||||||
|
return this.t('harvester.virtualMachine.hotplug.restartVMMessage');
|
||||||
|
}
|
||||||
|
|
||||||
return this.ingoreVMMessage ? '' : super.stateDescription;
|
return this.ingoreVMMessage ? '' : super.stateDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get displayCPU() {
|
||||||
|
return getVmCPUMemoryValues(this).cpu;
|
||||||
|
}
|
||||||
|
|
||||||
get displayMemory() {
|
get displayMemory() {
|
||||||
return (
|
return getVmCPUMemoryValues(this).memory;
|
||||||
this.spec.template.spec.domain.resources?.limits?.memory ||
|
|
||||||
this.spec.template.spec.domain.resources?.requests?.memory
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isQemuInstalled() {
|
get isQemuInstalled() {
|
||||||
|
|||||||
36
pkg/harvester/utils/cpuMemory.js
Normal file
36
pkg/harvester/utils/cpuMemory.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||||
|
|
||||||
|
export function getVmCPUMemoryValues(vm) {
|
||||||
|
if (!vm) {
|
||||||
|
return {
|
||||||
|
cpu: 0,
|
||||||
|
memory: null,
|
||||||
|
isHotplugEnabled: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const isHotplugEnabled = isCPUMemoryHotPlugEnabled(vm);
|
||||||
|
|
||||||
|
if (isHotplugEnabled) {
|
||||||
|
return {
|
||||||
|
cpu: vm.spec.template.spec.domain.cpu.sockets,
|
||||||
|
memory: vm.spec.template.spec.domain?.memory?.guest || null,
|
||||||
|
maxCpu: vm.spec.template.spec.domain.cpu?.maxSockets || 0,
|
||||||
|
maxMemory: vm.spec.template.spec.domain?.memory?.maxGuest || null,
|
||||||
|
isHotplugEnabled
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
cpu: vm.spec.template.spec.domain.cpu.cores,
|
||||||
|
memory: vm.spec.template.spec.domain.resources?.limits?.memory || null,
|
||||||
|
isHotplugEnabled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCPUMemoryHotPlugEnabled(vm) {
|
||||||
|
return vm?.metadata?.annotations[HCI_ANNOTATIONS.VM_CPU_MEMORY_HOTPLUG] === 'true' ||
|
||||||
|
!!vm?.spec?.template?.spec?.domain.cpu?.maxSockets ||
|
||||||
|
!!vm?.spec?.template?.spec?.domain?.memory?.maxGuest ||
|
||||||
|
false;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user