From 678da1b8edf8b9f999c1c5b432dd807b8e22e4b3 Mon Sep 17 00:00:00 2001 From: Michal Fox Date: Sat, 3 Oct 2026 03:19:01 +0200 Subject: [PATCH] fix #5412: ui: backup now: add fleecing option Fleecing can be enabled for backup jobs, but not for a manual backup via 'Backup now', where only a default in the node's vzdump.conf applies. So a VM that needs fleecing to not stall during the backup can not be backed up safely from the UI without changing that default. Add the 'Fleecing' and 'Fleecing Storage' fields of the backup job editor, for VMs only, as fleecing is not used for containers. Like there, the storage is required if fleecing is enabled, so the 'Backup' button stays disabled until one is selected. The fields start with the node's defaults. Only send the fleecing option if it was changed. The API checks for 'Datastore.AllocateSpace' on the fleecing storage of the option, but not on the one of the node's default, so always sending it would make manual backups fail for users without that permission on nodes with fleecing enabled by default. Signed-off-by: Michal Fox --- www/manager6/window/Backup.js | 57 ++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/www/manager6/window/Backup.js b/www/manager6/window/Backup.js index 7c1c54d..648bf7a 100644 --- a/www/manager6/window/Backup.js +++ b/www/manager6/window/Backup.js @@ -189,6 +189,14 @@ Ext.define('PVE.window.Backup', { if (!initialDefaults && data.mode !== undefined) { modeSelector.setValue(data.mode); } + if (!initialDefaults && me.vmtype === 'qemu' && data.fleecing) { + fleecingStorageSelector.setValue(data.fleecing.storage); + fleecingCheckbox.setValue(data.fleecing.enabled); + // only changed fleecing settings are sent, so that the node's + // defaults keep working without permissions on their storage + fleecingStorageSelector.resetOriginalValue(); + fleecingCheckbox.resetOriginalValue(); + } if (!initialDefaults && (data['notes-template'] ?? false)) { me.down('field[name=notes-template]').setValue( PVE.Utils.unEscapeNotesTemplate(data['notes-template']), @@ -245,10 +253,45 @@ Ext.define('PVE.window.Backup', { padding: '0 0 1 0', }); + let fleecingStorageSelector = Ext.create('PVE.form.StorageSelector', { + nodename: me.nodename, + name: 'fleecing-storage', + fieldLabel: gettext('Fleecing Storage'), + storageContent: 'images', + allowBlank: false, + disabled: true, + hidden: me.vmtype !== 'qemu', + }); + + let fleecingCheckbox = Ext.create('Proxmox.form.Checkbox', { + name: 'fleecing-enabled', + checked: false, + hidden: me.vmtype !== 'qemu', + uncheckedValue: 0, + fieldLabel: gettext('Fleecing'), + autoEl: { + tag: 'div', + 'data-qtip': gettext( + 'Backup write cache that can reduce IO pressure inside guests (VMs only).', + ), + }, + handler: function (checkbox, value) { + fleecingStorageSelector.setDisabled(!value); + fleecingStorageSelector.validate(); + }, + }); + me.formPanel = Ext.create('Proxmox.panel.InputPanel', { bodyPadding: 10, border: false, - column1: [storagesel, modeSelector, protectedCheckbox, pbsChangeDetection], + column1: [ + storagesel, + modeSelector, + protectedCheckbox, + fleecingCheckbox, + fleecingStorageSelector, + pbsChangeDetection, + ], column2: [compressionSelector, notificationModeSelector, mailtoField, removeCheckbox], columnB: [ { @@ -331,6 +374,14 @@ Ext.define('PVE.window.Backup', { params.protected = values.protected; } + if (fleecingCheckbox.isDirty() || fleecingStorageSelector.isDirty()) { + let fleecing = { enabled: values['fleecing-enabled'] }; + if (values['fleecing-enabled']) { + fleecing.storage = values['fleecing-storage']; + } + params.fleecing = PVE.Parser.printPropertyString(fleecing); + } + if (values['pbs-change-detection-mode']) { params['pbs-change-detection-mode'] = values['pbs-change-detection-mode']; } @@ -369,6 +420,10 @@ Ext.define('PVE.window.Backup', { }, }); + fleecingStorageSelector.on('validitychange', (field, valid) => + submitBtn.setDisabled(!valid), + ); + var helpBtn = Ext.create('Proxmox.button.Help', { onlineHelp: 'chapter_vzdump', listenToGlobalEvent: false,