* [PATCH 2/2] manager: add GUI support for backup export and import
2026-06-26 16:43 [PATCH 0/2] RFC: manager: add direct VM backup export/import support Mauro de Pascale
@ 2026-06-26 16:43 ` Mauro de Pascale
0 siblings, 0 replies; 2+ messages in thread
From: Mauro de Pascale @ 2026-06-26 16:43 UTC (permalink / raw)
To: pve-devel@lists.proxmox.com; +Cc: Mauro de Pascale
From: Mauro de Pascale <mauro.depascale.work@outlook.it>
Expose backup export from the backup dialog and allow uploading VMA
archives for restore directly from the storage view.
---
PVE/API2/VZDump.pm | 2 +-
www/manager6/Utils.js | 16 +++----
www/manager6/grid/BackupView.js | 24 +++++++++-
www/manager6/storage/BackupView.js | 29 +++++++++++-
www/manager6/window/Backup.js | 33 ++++++++++++-
www/manager6/window/UploadToStorage.js | 65 ++++++++++++++++++++++++--
6 files changed, 149 insertions(+), 20 deletions(-)
diff --git a/PVE/API2/VZDump.pm b/PVE/API2/VZDump.pm
index 84f42352..45c316c1 100644
--- a/PVE/API2/VZDump.pm
+++ b/PVE/API2/VZDump.pm
@@ -351,7 +351,7 @@ __PACKAGE__->register_method({
: $param->{compress} eq 'gzip' ? 'gz'
: $param->{compress} eq 'lzo' ? 'lzo'
: 'vma';
-
+
my $filename = "vzdump-qemu-$vmid.vma.$suffix";
my $cmd = ['/usr/bin/vzdump', $vmid, '--stdout','1','--compress',$compress];
diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index 040b5ae0..412fba0a 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -145,7 +145,7 @@ Ext.define('PVE.Utils', {
bvers = b.toString().split('.');
}
- for (;;) {
+ for (; ;) {
let av = avers.shift();
let bv = bvers.shift();
@@ -1272,8 +1272,7 @@ Ext.define('PVE.Utils', {
calculate_disk_usage: function (data) {
if (
!Ext.isNumeric(data.disk) ||
- data.type === 'qemu' ||
- (data.type === 'lxc' && data.uptime === 0) ||
+ ((data.type === 'qemu' || data.type === 'lxc') && data.uptime === 0) ||
data.maxdisk === 0
) {
return -1;
@@ -1298,8 +1297,7 @@ Ext.define('PVE.Utils', {
if (
!Ext.isNumeric(disk) ||
maxdisk === 0 ||
- type === 'qemu' ||
- (type === 'lxc' && record.data.uptime === 0)
+ ((type === 'qemu' || type === 'lxc') && record.data.uptime === 0)
) {
return '';
}
@@ -1810,9 +1808,6 @@ Ext.define('PVE.Utils', {
qemu_min_version: function (toCheck, minVersion) {
let i;
for (i = 0; i < toCheck.length && i < minVersion.length; i++) {
- if (toCheck[i] > minVersion[i]) {
- return true;
- }
if (toCheck[i] < minVersion[i]) {
return false;
}
@@ -1902,8 +1897,8 @@ Ext.define('PVE.Utils', {
container.mask(
Ext.String.format(
gettext('{0} not installed.') +
- ' ' +
- gettext('Log in as root to install.'),
+ ' ' +
+ gettext('Log in as root to install.'),
'Ceph',
),
['pve-static-mask'],
@@ -2185,6 +2180,7 @@ Ext.define('PVE.Utils', {
hastop: ['HA', gettext('Stop')],
imgcopy: ['', gettext('Copy data')],
imgdel: ['', gettext('Erase data')],
+ importvm: ['VM', gettext('Importing')],
lvmcreate: [gettext('LVM Storage'), gettext('Create')],
lvmremove: ['Volume Group', gettext('Remove')],
lvmthincreate: [gettext('LVM-Thin Storage'), gettext('Create')],
diff --git a/www/manager6/grid/BackupView.js b/www/manager6/grid/BackupView.js
index 406143dc..e63c7bdc 100644
--- a/www/manager6/grid/BackupView.js
+++ b/www/manager6/grid/BackupView.js
@@ -253,6 +253,27 @@ Ext.define('PVE.grid.BackupView', {
},
});
+ let import_btn = Ext.create('Proxmox.button.Button', {
+ text: 'Import',
+ iconCls: 'fa fa-upload',
+ tooltip: gettext('Import backup and restore VM from local machine'),
+ handler: function () {
+ Ext.create('PVE.window.UploadToStorage', {
+ nodename: nodename,
+ storage: storagesel.getValue(),
+ requiredPermissions: {
+ check: ['Datastore.Allocate', 'VM.Allocate'],
+ },
+ content: 'import',
+ listeners: {
+ destroy: () => reload(),
+ },
+ autoShow: true,
+ });
+ },
+ });
+
+
Ext.apply(me, {
selModel: sm,
tbar: {
@@ -260,6 +281,8 @@ Ext.define('PVE.grid.BackupView', {
items: [
backup_btn,
'-',
+ import_btn,
+ '-',
restore_btn,
file_restore_btn,
config_btn,
@@ -376,7 +399,6 @@ Ext.define('PVE.grid.BackupView', {
renderer: PVE.Utils.render_backup_encryption,
},
{
- // TRANSLATORS: The state of the verification task
header: gettext('Verify State'),
dataIndex: 'verification',
renderer: PVE.Utils.render_backup_verification,
diff --git a/www/manager6/storage/BackupView.js b/www/manager6/storage/BackupView.js
index 8c91ec99..f36030b9 100644
--- a/www/manager6/storage/BackupView.js
+++ b/www/manager6/storage/BackupView.js
@@ -10,6 +10,9 @@ Ext.define('PVE.storage.BackupView', {
initComponent: function () {
let me = this;
+ let content = me.pveSelNode.data.content?.split(',') ?? [];
+ me.hasBackupContent = content.includes('backup');
+
let nodename = (me.nodename = me.pveSelNode.data.node);
if (!nodename) {
throw 'no node name specified';
@@ -79,6 +82,30 @@ Ext.define('PVE.storage.BackupView', {
let isPBS = me.pluginType === 'pbs';
me.tbar = [
+ {
+ xtype: 'proxmoxButton',
+ text: gettext('Import'),
+ iconCls: 'fa fa-upload',
+ //disabled: false,
+ selModel: null,
+ disabled: !me.hasBackupContent,
+ requiredPermissions: {
+ check: ['Datastore.Allocate', 'VM.Allocate'],
+ },
+ tooltip: gettext('Import backup and restore VM from local machine'),
+ handler: function () {
+ let win = Ext.create('PVE.window.UploadToStorage', {
+ nodename: me.nodename,
+ storage: me.storage,
+ content: 'import',
+ listeners: {
+ destroy: () => me.store.load(),
+ },
+ });
+ win.show();
+ },
+ },
+ '-',
{
xtype: 'proxmoxButton',
text: gettext('Restore'),
@@ -106,7 +133,7 @@ Ext.define('PVE.storage.BackupView', {
},
});
},
- },
+ }
];
if (isPBS) {
me.tbar.push({
diff --git a/www/manager6/window/Backup.js b/www/manager6/window/Backup.js
index 7c1c54de..d8837d6b 100644
--- a/www/manager6/window/Backup.js
+++ b/www/manager6/window/Backup.js
@@ -41,7 +41,6 @@ Ext.define('PVE.window.Backup', {
xtype: 'proxmoxKVComboBox',
comboItems: [
['notification-system', gettext('Use global settings')],
- // TRANSLATORS: sendmail is a piece of software
['legacy-sendmail', gettext('Use sendmail (legacy)')],
],
fieldLabel: gettext('Notification'),
@@ -245,11 +244,34 @@ Ext.define('PVE.window.Backup', {
padding: '0 0 1 0',
});
+ let exportCheckbox = Ext.create('Proxmox.form.Checkbox', {
+ name: 'export',
+ checked: false,
+ uncheckedValue: 0,
+ tooltip: gettext('If selected Backup is saved to local machine'),
+ fieldLabel: gettext('Export'),
+ // Tiny amount of padding to stop the UI from shifting
+ // when the 'mailto' field is shown.
+ padding: '0 0 1 0',
+ listeners: {
+ change: function (f, checked) {
+
+ //console.log('clicked "export"');
+ storagesel.setDisabled(checked);
+ protectedCheckbox.setHidden(checked);
+ notificationModeSelector.setDisabled(checked);
+ if (checked) {
+ protectedCheckbox.setValue(false);
+ }
+ },
+ },
+ });
+
me.formPanel = Ext.create('Proxmox.panel.InputPanel', {
bodyPadding: 10,
border: false,
column1: [storagesel, modeSelector, protectedCheckbox, pbsChangeDetection],
- column2: [compressionSelector, notificationModeSelector, mailtoField, removeCheckbox],
+ column2: [compressionSelector, notificationModeSelector, exportCheckbox, mailtoField, removeCheckbox],
columnB: [
{
xtype: 'textareafield',
@@ -341,6 +363,13 @@ Ext.define('PVE.window.Backup', {
);
}
+ if (values.export) {
+ let url = `/api2/json/nodes/${me.nodename}/vzdump/export?vmid=${me.vmid}&compress=${values.compress}`;
+ window.open(url, '_blank');
+ me.close();
+ return;
+ }
+
Proxmox.Utils.API2Request({
url: '/nodes/' + me.nodename + '/vzdump',
params: params,
diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
index cc53596d..7d4bace9 100644
--- a/www/manager6/window/UploadToStorage.js
+++ b/www/manager6/window/UploadToStorage.js
@@ -9,7 +9,7 @@ Ext.define('PVE.window.UploadToStorage', {
title: gettext('Upload'),
acceptedExtensions: {
- import: ['.ova', '.qcow2', '.raw', '.vmdk'],
+ import: ['.ova', '.qcow2', '.raw', '.vmdk', '.vma', '.vma.zst'],
iso: ['.img', '.iso'],
vztmpl: ['.tar.gz', '.tar.xz', '.tar.zst'],
},
@@ -24,14 +24,15 @@ Ext.define('PVE.window.UploadToStorage', {
cbindData: function (initialConfig) {
const me = this;
const ext = me.acceptedExtensions[me.content] || [];
-
+ let fRegex = new RegExp('^.*(?:' + ext.map(e => e.replace(/\./g, '\\.')).join('|') + ')$', 'i');
me.url = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
let fileSelectorExt = ext.concat(Object.keys(me.extensionAliases[me.content] ?? {}));
+
return {
extensions: fileSelectorExt.join(', '),
- filenameRegex: new RegExp('^.*(?:' + ext.join('|').replaceAll('.', '\\.') + ')$', 'i'),
+ filenameRegex: fRegex,
};
},
@@ -45,6 +46,17 @@ Ext.define('PVE.window.UploadToStorage', {
controller: {
submit: function (button) {
+
+ let doUpload = function () {
+ xhr.open('POST', `/api2/json${url}`, true);
+
+ if (window.Proxmox && Proxmox.CSRFPreventionToken) {
+ xhr.setRequestHeader('CSRFPreventionToken', Proxmox.CSRFPreventionToken);
+ }
+
+ xhr.send(fd);
+ };
+
const view = this.getView();
const form = this.lookup('formPanel').getForm();
const abortBtn = this.lookup('abortBtn');
@@ -141,8 +153,49 @@ Ext.define('PVE.window.UploadToStorage', {
false,
);
- xhr.open('POST', `/api2/json${view.url}`, true);
- xhr.send(fd);
+ let url = view.url;
+
+ if (view.content === 'import') {
+ let vmid = null;
+
+ // estrai vmid dal filename tipo vzdump-qemu-100.vma.zst
+ let match = filename.match(/-(\d+)\./);
+ if (match) {
+ vmid = match[1];
+ }
+
+ if (vmid) {
+ Proxmox.Utils.API2Request({
+ url: `/nodes/${view.nodename}/qemu/${vmid}/status/current`,
+ method: 'GET',
+
+ success: function () {
+ // VM esiste → chiedi conferma
+ Ext.Msg.confirm(
+ gettext('Confirm Restore'),
+ Ext.String.format(
+ gettext('VM {0} already exists. Overwrite?'),
+ vmid
+ ),
+ function (btn) {
+ if (btn === 'yes') {
+ doUpload();
+ }
+ }
+ );
+ },
+
+ failure: function () {
+ // VM non esiste → procedi direttamente
+ doUpload();
+ },
+ });
+
+ return; // blocca flusso normale
+ }
+ }
+
+ doUpload();
},
validitychange: function (f, valid) {
@@ -327,4 +380,6 @@ Ext.define('PVE.window.UploadToStorage', {
me.callParent();
},
+
+
});
--
2.47.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 2/2] manager: add GUI support for backup export and import
@ 2026-09-23 12:25 Mauro de Pascale
0 siblings, 0 replies; 2+ messages in thread
From: Mauro de Pascale @ 2026-09-23 12:25 UTC (permalink / raw)
To: pve-devel@lists.proxmox.com
From: Mauro de Pascale <mauro.depascale.work@outlook.it>
Expose backup export from the backup dialog and allow uploading VMA
archives for restore directly from the storage view.
________________________________
PVE/API2/VZDump.pm | 2 +-
www/manager6/Utils.js | 16 +++----
www/manager6/grid/BackupView.js | 24 +++++++++-
www/manager6/storage/BackupView.js | 29 +++++++++++-
www/manager6/window/Backup.js | 33 ++++++++++++-
www/manager6/window/UploadToStorage.js | 65 ++++++++++++++++++++++++--
6 files changed, 149 insertions(+), 20 deletions(-)
diff --git a/PVE/API2/VZDump.pm b/PVE/API2/VZDump.pm
index 84f42352..45c316c1 100644
--- a/PVE/API2/VZDump.pm
+++ b/PVE/API2/VZDump.pm
@@ -351,7 +351,7 @@ __PACKAGE__->register_method({
: $param->{compress} eq 'gzip' ? 'gz'
: $param->{compress} eq 'lzo' ? 'lzo'
: 'vma';
*
*
*
my $filename = "vzdump-qemu-$vmid.vma.$suffix";
my $cmd = ['/usr/bin/vzdump', $vmid, '--stdout','1','--compress',$compress];
diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index 040b5ae0..412fba0a 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -145,7 +145,7 @@ Ext.define('PVE.Utils', {
bvers = b.toString().split('.');
}
*
for (;;) {
*
for (; ;) {
*
let av = avers.shift();
let bv = bvers.shift();
@@ -1272,8 +1272,7 @@ Ext.define('PVE.Utils', {
calculate_disk_usage: function (data) {
if (
!Ext.isNumeric(data.disk) ||
*
data.type === 'qemu' ||
*
(data.type === 'lxc' && data.uptime === 0) ||
*
((data.type === 'qemu' || data.type === 'lxc') && data.uptime === 0) ||
*
data.maxdisk === 0
) {
return -1;
*
@@ -1298,8 +1297,7 @@ Ext.define('PVE.Utils', {
*
if (
!Ext.isNumeric(disk) ||
maxdisk === 0 ||
*
type === 'qemu' ||
*
(type === 'lxc' && record.data.uptime === 0)
*
((type === 'qemu' || type === 'lxc') && record.data.uptime === 0)
*
) {
return '';
}
*
@@ -1810,9 +1808,6 @@ Ext.define('PVE.Utils', {
*
qemu_min_version: function (toCheck, minVersion) {
let i;
for (i = 0; i < toCheck.length && i < minVersion.length; i++) {
*
if (toCheck[i] > minVersion[i]) {
*
return true;
*
}
*
if (toCheck[i] < minVersion[i]) {
return false;
}
*
@@ -1902,8 +1897,8 @@ Ext.define('PVE.Utils', {
*
container.mask(
Ext.String.format(
gettext('{0} not installed.') +
*
' ' +
*
gettext('Log in as root to install.'),
*
' ' +
*
gettext('Log in as root to install.'),
*
'Ceph',
),
['pve-static-mask'],
*
@@ -2185,6 +2180,7 @@ Ext.define('PVE.Utils', {
*
hastop: ['HA', gettext('Stop')],
imgcopy: ['', gettext('Copy data')],
imgdel: ['', gettext('Erase data')],
*
importvm: ['VM', gettext('Importing')],
*
lvmcreate: [gettext('LVM Storage'), gettext('Create')],
lvmremove: ['Volume Group', gettext('Remove')],
lvmthincreate: [gettext('LVM-Thin Storage'), gettext('Create')],
*
diff --git a/www/manager6/grid/BackupView.js b/www/manager6/grid/BackupView.js
index 406143dc..e63c7bdc 100644
--- a/www/manager6/grid/BackupView.js
+++ b/www/manager6/grid/BackupView.js
@@ -253,6 +253,27 @@ Ext.define('PVE.grid.BackupView', {
*
},
});
*
let import_btn = Ext.create('Proxmox.button.Button', {
*
text: 'Import',
*
iconCls: 'fa fa-upload',
*
tooltip: gettext('Import backup and restore VM from local machine'),
*
handler: function () {
*
Ext.create('PVE.window.UploadToStorage', {
*
nodename: nodename,
*
storage: storagesel.getValue(),
*
requiredPermissions: {
*
check: ['Datastore.Allocate', 'VM.Allocate'],
*
},
*
content: 'import',
*
listeners: {
*
destroy: () => reload(),
*
},
*
autoShow: true,
*
});
*
},
*
});
*
*
*
Ext.apply(me, {
selModel: sm,
tbar: {
*
@@ -260,6 +281,8 @@ Ext.define('PVE.grid.BackupView', {
*
items: [
backup_btn,
'-',
*
import_btn,
*
'-',
*
restore_btn,
file_restore_btn,
config_btn,
*
@@ -376,7 +399,6 @@ Ext.define('PVE.grid.BackupView', {
*
renderer: PVE.Utils.render_backup_encryption,
},
{
*
// TRANSLATORS: The state of the verification task
*
header: gettext('Verify State'),
dataIndex: 'verification',
renderer: PVE.Utils.render_backup_verification,
*
diff --git a/www/manager6/storage/BackupView.js b/www/manager6/storage/BackupView.js
index 8c91ec99..f36030b9 100644
--- a/www/manager6/storage/BackupView.js
+++ b/www/manager6/storage/BackupView.js
@@ -10,6 +10,9 @@ Ext.define('PVE.storage.BackupView', {
*
initComponent: function () {
let me = this;
*
let content = me.pveSelNode.data.content?.split(',') ?? [];
*
me.hasBackupContent = content.includes('backup');
*
*
let nodename = (me.nodename = me.pveSelNode.data.node);
if (!nodename) {
throw 'no node name specified';
*
@@ -79,6 +82,30 @@ Ext.define('PVE.storage.BackupView', {
*
let isPBS = me.pluginType === 'pbs';
me.tbar = [
*
{
*
xtype: 'proxmoxButton',
*
text: gettext('Import'),
*
iconCls: 'fa fa-upload',
*
//disabled: false,
*
selModel: null,
*
disabled: !me.hasBackupContent,
*
requiredPermissions: {
*
check: ['Datastore.Allocate', 'VM.Allocate'],
*
},
*
tooltip: gettext('Import backup and restore VM from local machine'),
*
handler: function () {
*
let win = Ext.create('PVE.window.UploadToStorage', {
*
nodename: me.nodename,
*
storage: me.storage,
*
content: 'import',
*
listeners: {
*
destroy: () => me.store.load(),
*
},
*
});
*
win.show();
*
},
*
},
*
'-',
*
{
xtype: 'proxmoxButton',
text: gettext('Restore'),
*
@@ -106,7 +133,7 @@ Ext.define('PVE.storage.BackupView', {
*
},
});
},
*
},
*
}
*
];
if (isPBS) {
me.tbar.push({
*
diff --git a/www/manager6/window/Backup.js b/www/manager6/window/Backup.js
index 7c1c54de..d8837d6b 100644
--- a/www/manager6/window/Backup.js
+++ b/www/manager6/window/Backup.js
@@ -41,7 +41,6 @@ Ext.define('PVE.window.Backup', {
*
xtype: 'proxmoxKVComboBox',
comboItems: [
['notification-system', gettext('Use global settings')],
*
// TRANSLATORS: sendmail is a piece of software
*
['legacy-sendmail', gettext('Use sendmail (legacy)')],
],
fieldLabel: gettext('Notification'),
*
@@ -245,11 +244,34 @@ Ext.define('PVE.window.Backup', {
*
padding: '0 0 1 0',
});
*
let exportCheckbox = Ext.create('Proxmox.form.Checkbox', {
*
name: 'export',
*
checked: false,
*
uncheckedValue: 0,
*
tooltip: gettext('If selected Backup is saved to local machine'),
*
fieldLabel: gettext('Export'),
*
// Tiny amount of padding to stop the UI from shifting
*
// when the 'mailto' field is shown.
*
padding: '0 0 1 0',
*
listeners: {
*
change: function (f, checked) {
*
*
//console.log('clicked "export"');
*
storagesel.setDisabled(checked);
*
protectedCheckbox.setHidden(checked);
*
notificationModeSelector.setDisabled(checked);
*
if (checked) {
*
protectedCheckbox.setValue(false);
*
}
*
},
*
},
*
});
*
*
me.formPanel = Ext.create('Proxmox.panel.InputPanel', {
bodyPadding: 10,
border: false,
column1: [storagesel, modeSelector, protectedCheckbox, pbsChangeDetection],
*
column2: [compressionSelector, notificationModeSelector, mailtoField, removeCheckbox],
*
column2: [compressionSelector, notificationModeSelector, exportCheckbox, mailtoField, removeCheckbox],
*
columnB: [
{
xtype: 'textareafield',
*
@@ -341,6 +363,13 @@ Ext.define('PVE.window.Backup', {
*
);
}
*
if (values.export) {
*
let url = `/api2/json/nodes/${me.nodename}/vzdump/export?vmid=${me.vmid}&compress=${values.compress}`;
*
window.open(url, '_blank');
*
me.close();
*
return;
*
}
*
*
Proxmox.Utils.API2Request({
url: '/nodes/' + me.nodename + '/vzdump',
params: params,
*
diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
index cc53596d..7d4bace9 100644
--- a/www/manager6/window/UploadToStorage.js
+++ b/www/manager6/window/UploadToStorage.js
@@ -9,7 +9,7 @@ Ext.define('PVE.window.UploadToStorage', {
*
title: gettext('Upload'),
acceptedExtensions: {
*
import: ['.ova', '.qcow2', '.raw', '.vmdk'],
*
import: ['.ova', '.qcow2', '.raw', '.vmdk', '.vma', '.vma.zst'],
*
iso: ['.img', '.iso'],
vztmpl: ['.tar.gz', '.tar.xz', '.tar.zst'],
},
*
@@ -24,14 +24,15 @@ Ext.define('PVE.window.UploadToStorage', {
*
cbindData: function (initialConfig) {
const me = this;
const ext = me.acceptedExtensions[me.content] || [];
*
*
let fRegex = new RegExp('^.(?:' + ext.map(e => e.replace(/./g, '\.')).join('|') + ')$', 'i');
*
me.url = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
let fileSelectorExt = ext.concat(Object.keys(me.extensionAliases[me.content] ?? {}));
+
return {
extensions: fileSelectorExt.join(', '),
*
filenameRegex: new RegExp('^.(?:' + ext.join('|').replaceAll('.', '\.') + ')$', 'i'),
*
filenameRegex: fRegex,
*
};
},
@@ -45,6 +46,17 @@ Ext.define('PVE.window.UploadToStorage', {
controller: {
submit: function (button) {
+
*
let doUpload = function () {
*
xhr.open('POST', `/api2/json${url}`, true);
*
*
if (window.Proxmox && Proxmox.CSRFPreventionToken) {
*
xhr.setRequestHeader('CSRFPreventionToken', Proxmox.CSRFPreventionToken);
*
}
*
*
xhr.send(fd);
*
};
*
*
const view = this.getView();
const form = this.lookup('formPanel').getForm();
const abortBtn = this.lookup('abortBtn');
*
@@ -141,8 +153,49 @@ Ext.define('PVE.window.UploadToStorage', {
*
false,
);
*
xhr.open('POST', `/api2/json${view.url}`, true);
*
xhr.send(fd);
*
let url = view.url;
*
*
if (view.content === 'import') {
*
let vmid = null;
*
*
// estrai vmid dal filename tipo vzdump-qemu-100.vma.zst
*
let match = filename.match(/-(\d+)./);
*
if (match) {
*
vmid = match[1];
*
}
*
*
if (vmid) {
*
Proxmox.Utils.API2Request({
*
url: `/nodes/${view.nodename}/qemu/${vmid}/status/current`,
*
method: 'GET',
*
*
success: function () {
*
// VM esiste → chiedi conferma
*
Ext.Msg.confirm(
*
gettext('Confirm Restore'),
*
Ext.String.format(
*
gettext('VM {0} already exists. Overwrite?'),
*
vmid
*
),
*
function (btn) {
*
if (btn === 'yes') {
*
doUpload();
*
}
*
}
*
);
*
},
*
*
failure: function () {
*
// VM non esiste → procedi direttamente
*
doUpload();
*
},
*
});
*
*
return; // blocca flusso normale
*
}
*
}
*
*
doUpload();
*
},
validitychange: function (f, valid) {
@@ -327,4 +380,6 @@ Ext.define('PVE.window.UploadToStorage', {
me.callParent();
},
+
+
});
--
2.47.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-28 7:15 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-23 12:25 [PATCH 2/2] manager: add GUI support for backup export and import Mauro de Pascale
-- strict thread matches above, loose matches on Subject: below --
2026-06-26 16:43 [PATCH 0/2] RFC: manager: add direct VM backup export/import support Mauro de Pascale
2026-06-26 16:43 ` [PATCH 2/2] manager: add GUI support for backup export and import Mauro de Pascale
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.