feat: Allow user to customize snapshot/backup deadline (#866)

* feat: Allow user to customize snapshot/backup deadline

Related to: https://github.com/harvester/harvester/issues/4098
Related to: https://github.com/harvester/harvester/pull/10545

Signed-off-by: Volker Theile <vtheile@suse.com>

* feat: Allow user to customize snapshot/backup deadline

Related to: https://github.com/harvester/harvester/issues/4098
Related to: https://github.com/harvester/harvester/pull/10545

Signed-off-by: Volker Theile <vtheile@suse.com>

---------

Signed-off-by: Volker Theile <vtheile@suse.com>
This commit is contained in:
Volker Theile 2026-07-09 08:44:47 +02:00 committed by GitHub
parent 531ed03796
commit d98db6ba49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 108 additions and 5 deletions

View File

@ -75,6 +75,7 @@ const FEATURE_FLAGS = {
'expandOnlineEncryptedVolume', 'expandOnlineEncryptedVolume',
'longhornV2HugepageSettings', 'longhornV2HugepageSettings',
'staticIPForVM', 'staticIPForVM',
'fsFreezeDeadline',
], ],
}; };

View File

@ -5,6 +5,8 @@ import { Card } from '@components/Card';
import { Banner } from '@components/Banner'; import { Banner } from '@components/Banner';
import AsyncButton from '@shell/components/AsyncButton'; import AsyncButton from '@shell/components/AsyncButton';
import { LabeledInput } from '@components/Form/LabeledInput'; import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { DEFAULT_FS_FREEZE_DEADLINE, getFsFreezeDeadlineOptions } from '../utils/backup';
export default { export default {
name: 'HarvesterBackupModal', name: 'HarvesterBackupModal',
@ -15,6 +17,7 @@ export default {
AsyncButton, AsyncButton,
Card, Card,
LabeledInput, LabeledInput,
LabeledSelect,
Banner Banner
}, },
@ -27,8 +30,9 @@ export default {
data() { data() {
return { return {
backUpName: '', backUpName: '',
errors: [] errors: [],
fsFreezeDeadline: DEFAULT_FS_FREEZE_DEADLINE
}; };
}, },
@ -37,12 +41,22 @@ export default {
actionResource() { actionResource() {
return this.resources[0]; return this.resources[0];
},
fsFreezeDeadlineEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('fsFreezeDeadline');
},
fsFreezeDeadlineOptions() {
return getFsFreezeDeadlineOptions(this.t);
} }
}, },
methods: { methods: {
close() { close() {
this.backUpName = ''; this.backUpName = '';
this.fsFreezeDeadline = DEFAULT_FS_FREEZE_DEADLINE;
this.errors = [];
this.$emit('close'); this.$emit('close');
}, },
@ -51,7 +65,7 @@ export default {
try { try {
const res = await this.actionResource.doAction( const res = await this.actionResource.doAction(
'backup', 'backup',
{ name: this.backUpName }, { name: this.backUpName, fsFreezeDeadline: this.fsFreezeDeadline },
{}, {},
false false
); );
@ -100,9 +114,18 @@ export default {
<template #body> <template #body>
<LabeledInput <LabeledInput
v-model:value="backUpName" v-model:value="backUpName"
class="mt-20"
:label="t('generic.name')" :label="t('generic.name')"
required required
/> />
<LabeledSelect
v-if="fsFreezeDeadlineEnabled"
v-model:value="fsFreezeDeadline"
class="mt-20"
:options="fsFreezeDeadlineOptions"
:label="t('harvester.modal.backup.fileSystemFreezeDeadline.label')"
required
/>
<Banner <Banner
v-for="(err, i) in errors" v-for="(err, i) in errors"
:key="i" :key="i"

View File

@ -5,8 +5,10 @@ import { Card } from '@components/Card';
import { Banner } from '@components/Banner'; import { Banner } from '@components/Banner';
import AsyncButton from '@shell/components/AsyncButton'; import AsyncButton from '@shell/components/AsyncButton';
import { LabeledInput } from '@components/Form/LabeledInput'; import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { HCI } from '../types'; import { HCI } from '../types';
import { BACKUP_TYPE } from '../config/types'; import { BACKUP_TYPE } from '../config/types';
import { DEFAULT_FS_FREEZE_DEADLINE, getFsFreezeDeadlineOptions } from '../utils/backup';
export default { export default {
name: 'HarvesterVMSnapshotDialog', name: 'HarvesterVMSnapshotDialog',
@ -17,6 +19,7 @@ export default {
AsyncButton, AsyncButton,
Card, Card,
LabeledInput, LabeledInput,
LabeledSelect,
Banner Banner
}, },
@ -31,7 +34,8 @@ export default {
return { return {
snapshotName: '', snapshotName: '',
snapshotNamespace: '', snapshotNamespace: '',
errors: [] errors: [],
fsFreezeDeadline: DEFAULT_FS_FREEZE_DEADLINE
}; };
}, },
@ -40,13 +44,23 @@ export default {
actionResource() { actionResource() {
return this.resources[0]; return this.resources[0];
},
fsFreezeDeadlineEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('fsFreezeDeadline');
},
fsFreezeDeadlineOptions() {
return getFsFreezeDeadlineOptions(this.t);
} }
}, },
methods: { methods: {
close() { close() {
this.fsFreezeDeadline = DEFAULT_FS_FREEZE_DEADLINE;
this.snapshotNamespace = ''; this.snapshotNamespace = '';
this.snapshotName = ''; this.snapshotName = '';
this.errors = [];
this.$emit('close'); this.$emit('close');
}, },
@ -60,7 +74,8 @@ export default {
ownerReferences: this.getOwnerReferencesFromVM(this.actionResource) ownerReferences: this.getOwnerReferencesFromVM(this.actionResource)
}, },
spec: { spec: {
source: { fsFreezeDeadline: this.fsFreezeDeadline,
source: {
apiGroup: 'kubevirt.io', apiGroup: 'kubevirt.io',
kind: 'VirtualMachine', kind: 'VirtualMachine',
name: this.actionResource.metadata.name name: this.actionResource.metadata.name
@ -132,6 +147,14 @@ export default {
:label="t('generic.name')" :label="t('generic.name')"
required required
/> />
<LabeledSelect
v-if="fsFreezeDeadlineEnabled"
v-model:value="fsFreezeDeadline"
class="mt-20"
:options="fsFreezeDeadlineOptions"
:label="t('harvester.modal.vmSnapshot.fileSystemFreezeDeadline.label')"
required
/>
<Banner <Banner
v-for="(err, i) in errors" v-for="(err, i) in errors"
:key="i" :key="i"

View File

@ -8,6 +8,15 @@ generic:
basic: Basic basic: Basic
loading: Loading... loading: Loading...
none: None none: None
duration:
infinite: Infinite
1s: 1 second
5s: 5 seconds
10s: 10 seconds
30s: 30 seconds
1m: 1 minute
3m: 3 minutes
5m: 5 minutes
unsupported: unsupported:
serverVersion: 'Current version: <code>{serverVersion}</code>' serverVersion: 'Current version: <code>{serverVersion}</code>'
@ -75,6 +84,8 @@ harvester:
backup: backup:
success: 'Backup { backUpName } has been initiated.' success: 'Backup { backUpName } has been initiated.'
addBackup: Add Backup addBackup: Add Backup
fileSystemFreezeDeadline:
label: 'File System Freeze Deadline'
quota: quota:
editVMQuota: Edit VM Quota editVMQuota: Edit VM Quota
editQuota: Edit Quota editQuota: Edit Quota
@ -214,6 +225,8 @@ harvester:
title: Take Virtual Machine Snapshot title: Take Virtual Machine Snapshot
name: Name name: Name
success: 'Take virtual machine Snapshot { name } successfully.' success: 'Take virtual machine Snapshot { name } successfully.'
fileSystemFreezeDeadline:
label: 'File System Freeze Deadline'
restart: restart:
title: Restart Virtual Machine title: Restart Virtual Machine
tip: Restart the virtual machine for configuration changes to take effect. tip: Restart the virtual machine for configuration changes to take effect.

View File

@ -0,0 +1,43 @@
export const DEFAULT_FS_FREEZE_DEADLINE = '1s';
/**
* Generate filesystem freeze deadline options with i18n translations
* @param {Function} t - i18n translation function
* @returns {Array} Array of deadline options
*/
export function getFsFreezeDeadlineOptions(t) {
return [
{
label: t('generic.duration.infinite'),
value: '0s'
},
{
label: t('generic.duration.1s'),
value: '1s'
},
{
label: t('generic.duration.5s'),
value: '5s'
},
{
label: t('generic.duration.10s'),
value: '10s'
},
{
label: t('generic.duration.30s'),
value: '30s'
},
{
label: t('generic.duration.1m'),
value: '1m'
},
{
label: t('generic.duration.3m'),
value: '3m'
},
{
label: t('generic.duration.5m'),
value: '5m'
}
];
}