* [pve-devel] [PATCH storage/manager] allow upload & import of qcow2 in the web UI @ 2025-03-13 11:17 Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH storage 1/1] import: allow upload of qcow2 files into import storage Dominik Csapak ` (3 more replies) 0 siblings, 4 replies; 6+ messages in thread From: Dominik Csapak @ 2025-03-13 11:17 UTC (permalink / raw) To: pve-devel most of the building blocks are already there: * we can have qcow2 files in an import storage * we can import qcow2 files via the api from such a storage this series fills in the missing bits & pieces: * allow uploading qcow2 files into an import storage via the webgui * adding the possibility to select such a file when creating a vm/disk We could maybe also allow this for raw/vmdk if we want to, but IMHO we can start out with qcow2 and add the others as necssary. (if wanted, I can of course also add the others in a v2) pve-storage: Dominik Csapak (1): import: allow upload of qcow2 files into import storage src/PVE/API2/Storage/Status.pm | 17 ++++++++++++++++- src/PVE/Storage.pm | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) pve-manager: Dominik Csapak (3): ui: storage content: allow upload of qcow2 for import type ui: form: file selector: allow optional filter ui: qemu hd edit: allow importing a disk from the import storage www/manager6/form/FileSelector.js | 10 ++++ www/manager6/qemu/HDEdit.js | 72 +++++++++++++++++++++++++- www/manager6/window/UploadToStorage.js | 2 +- 3 files changed, 82 insertions(+), 2 deletions(-) -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [pve-devel] [PATCH storage 1/1] import: allow upload of qcow2 files into import storage 2025-03-13 11:17 [pve-devel] [PATCH storage/manager] allow upload & import of qcow2 in the web UI Dominik Csapak @ 2025-03-13 11:17 ` Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH manager 1/3] ui: storage content: allow upload of qcow2 for import type Dominik Csapak ` (2 subsequent siblings) 3 siblings, 0 replies; 6+ messages in thread From: Dominik Csapak @ 2025-03-13 11:17 UTC (permalink / raw) To: pve-devel so users can upload qcow2 files directly in the ui Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> --- src/PVE/API2/Storage/Status.pm | 17 ++++++++++++++++- src/PVE/Storage.pm | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/PVE/API2/Storage/Status.pm b/src/PVE/API2/Storage/Status.pm index c854b53..b253392 100644 --- a/src/PVE/API2/Storage/Status.pm +++ b/src/PVE/API2/Storage/Status.pm @@ -456,6 +456,7 @@ __PACKAGE__->register_method ({ my $path; my $isOva = 0; + my $isQcow2 = 0; if ($content eq 'iso') { if ($filename !~ m![^/]+$PVE::Storage::ISO_EXT_RE_0$!) { @@ -472,7 +473,12 @@ __PACKAGE__->register_method ({ raise_param_exc({ filename => "invalid filename or wrong extension" }); } - $isOva = 1; + if ($filename =~ m/\.ova$/) { + $isOva = 1; + } elsif ($filename =~ m/\.qcow2$/) { + $isQcow2 = 1; + } + $path = PVE::Storage::get_import_dir($cfg, $storage); } else { raise_param_exc({ content => "upload content type '$content' not allowed" }); @@ -543,6 +549,9 @@ __PACKAGE__->register_method ({ if ($isOva) { assert_ova_contents($tmpfilename); + } elsif ($isQcow2) { + # checks untrusted image + PVE::Storage::file_size_info($tmpfilename, 10, 'qcow2', 1); } }; if (my $err = $@) { @@ -667,6 +676,7 @@ __PACKAGE__->register_method({ my $path; my $isOva = 0; + my $isQcow2 = 0; if ($content eq 'iso') { if ($filename !~ m![^/]+$PVE::Storage::ISO_EXT_RE_0$!) { @@ -685,6 +695,8 @@ __PACKAGE__->register_method({ if ($filename =~ m/\.ova$/) { $isOva = 1; + } elsif ($filename =~ m/\.qcow2$/) { + $isQcow2 = 1; } $path = PVE::Storage::get_import_dir($cfg, $storage); @@ -717,6 +729,9 @@ __PACKAGE__->register_method({ if ($isOva) { assert_ova_contents($tmp_path); + } elsif ($isQcow2) { + # checks untrusted image + PVE::Storage::file_size_info($tmp_path, 10, 'qcow2', 1); } }; diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm index 3b4f041..deed73f 100755 --- a/src/PVE/Storage.pm +++ b/src/PVE/Storage.pm @@ -116,7 +116,7 @@ our $BACKUP_EXT_RE_2 = qr/\.(tgz|(?:tar|vma)(?:\.(${\PVE::Storage::Plugin::COMPR our $IMPORT_EXT_RE_1 = qr/\.(ova|ovf|qcow2|raw|vmdk)/; -our $UPLOAD_IMPORT_EXT_RE_1 = qr/\.(ova)/; +our $UPLOAD_IMPORT_EXT_RE_1 = qr/\.(ova|qcow2)/; our $SAFE_CHAR_CLASS_RE = qr/[a-zA-Z0-9\-\.\+\=\_]/; our $SAFE_CHAR_WITH_WHITESPACE_CLASS_RE = qr/[ a-zA-Z0-9\-\.\+\=\_]/; -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [pve-devel] [PATCH manager 1/3] ui: storage content: allow upload of qcow2 for import type 2025-03-13 11:17 [pve-devel] [PATCH storage/manager] allow upload & import of qcow2 in the web UI Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH storage 1/1] import: allow upload of qcow2 files into import storage Dominik Csapak @ 2025-03-13 11:17 ` Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH manager 2/3] ui: form: file selector: allow optional filter Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH manager 3/3] ui: qemu hd edit: allow importing a disk from the import storage Dominik Csapak 3 siblings, 0 replies; 6+ messages in thread From: Dominik Csapak @ 2025-03-13 11:17 UTC (permalink / raw) To: pve-devel partially fixes #2424 Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> --- www/manager6/window/UploadToStorage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js index cdf548a8..3ce2d1f5 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'], + 'import': ['.ova', '.qcow2'], iso: ['.img', '.iso'], vztmpl: ['.tar.gz', '.tar.xz', '.tar.zst'], }, -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [pve-devel] [PATCH manager 2/3] ui: form: file selector: allow optional filter 2025-03-13 11:17 [pve-devel] [PATCH storage/manager] allow upload & import of qcow2 in the web UI Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH storage 1/1] import: allow upload of qcow2 files into import storage Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH manager 1/3] ui: storage content: allow upload of qcow2 for import type Dominik Csapak @ 2025-03-13 11:17 ` Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH manager 3/3] ui: qemu hd edit: allow importing a disk from the import storage Dominik Csapak 3 siblings, 0 replies; 6+ messages in thread From: Dominik Csapak @ 2025-03-13 11:17 UTC (permalink / raw) To: pve-devel this sometimes comes in handy when we only want to show specific files. Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> --- www/manager6/form/FileSelector.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/www/manager6/form/FileSelector.js b/www/manager6/form/FileSelector.js index ef2bedf9..9db20711 100644 --- a/www/manager6/form/FileSelector.js +++ b/www/manager6/form/FileSelector.js @@ -43,6 +43,13 @@ Ext.define('PVE.form.FileSelector', { url: url, }); + if (Ext.isFunction(me.filter)) { + me.store.clearFilter(); + me.store.addFilter([me.filter]); + } else { + me.store.clearFilter(); + } + me.store.removeAll(); me.store.load(); }, @@ -60,6 +67,9 @@ Ext.define('PVE.form.FileSelector', { valueField: 'volid', displayField: 'text', + // An optional filter function + filter: undefined, + listConfig: { width: 600, columns: [ -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [pve-devel] [PATCH manager 3/3] ui: qemu hd edit: allow importing a disk from the import storage 2025-03-13 11:17 [pve-devel] [PATCH storage/manager] allow upload & import of qcow2 in the web UI Dominik Csapak ` (2 preceding siblings ...) 2025-03-13 11:17 ` [pve-devel] [PATCH manager 2/3] ui: form: file selector: allow optional filter Dominik Csapak @ 2025-03-13 11:17 ` Dominik Csapak 2025-03-17 11:05 ` Filip Schauer 3 siblings, 1 reply; 6+ messages in thread From: Dominik Csapak @ 2025-03-13 11:17 UTC (permalink / raw) To: pve-devel adds a checkbox 'import image' above the storage selector which: * hides the original storage selector * shows a 'source storage' selector * shows a 'import file' selector * shows a 'target storage' selector Since the wizard and the hd edit share this panel, this also works in the wizard. Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> --- www/manager6/qemu/HDEdit.js | 72 ++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/www/manager6/qemu/HDEdit.js b/www/manager6/qemu/HDEdit.js index b78647ec..a235815d 100644 --- a/www/manager6/qemu/HDEdit.js +++ b/www/manager6/qemu/HDEdit.js @@ -78,11 +78,17 @@ Ext.define('PVE.qemu.HDInputPanel', { if (values.hdimage) { me.drive.file = values.hdimage; } else { - me.drive.file = values.hdstorage + ":" + values.disksize; + let disksize = values.disksize; + if (values['import-from']) { + PVE.Utils.propertyStringSet(me.drive, values['import-from'], 'import-from'); + disksize = 0; + } + me.drive.file = `${values.hdstorage}:${disksize}`; } me.drive.format = values.diskformat; } + PVE.Utils.propertyStringSet(me.drive, !values.backup, 'backup', '0'); PVE.Utils.propertyStringSet(me.drive, values.noreplicate, 'replicate', 'no'); PVE.Utils.propertyStringSet(me.drive, values.discard, 'discard', 'on'); @@ -168,6 +174,11 @@ Ext.define('PVE.qemu.HDInputPanel', { var me = this; me.down('#hdstorage').setNodename(nodename); me.down('#hdimage').setStorage(undefined, nodename); + + me.lookup('new-disk').setNodename(nodename); + me.lookup('import-source').setNodename(nodename); + me.lookup('import-source-file').setNodename(nodename); + me.lookup('import-target').setNodename(nodename); }, hasAdvanced: true, @@ -221,12 +232,71 @@ Ext.define('PVE.qemu.HDInputPanel', { column1.push(me.unusedDisks); } else if (me.isCreate) { column1.push({ + xtype: 'proxmoxcheckbox', + isFormField: false, + fieldLabel: gettext("Import Image"), + listeners: { + change: function(_cb, value) { + me.lookup('new-disk').setVisible(!value); + me.lookup('new-disk').setDisabled(!!value); + + me.lookup('import-source').setVisible(!!value); + me.lookup('import-source').setDisabled(!value); + me.lookup('import-source-file').setVisible(!!value); + + me.lookup('import-target').setVisible(!!value); + me.lookup('import-target').setDisabled(!value); + }, + }, + }); + column1.push({ + reference: 'new-disk', xtype: 'pveDiskStorageSelector', storageContent: 'images', name: 'disk', nodename: me.nodename, autoSelect: me.insideWizard, }); + column1.push({ + xtype: 'pveStorageSelector', + reference: 'import-source', + fieldLabel: gettext('Import Storage'), + name: 'import-source-storage', + hidden: true, + disabled: true, + storageContent: 'import', + nodename: me.nodename, + autoSelect: me.insideWizard, + listeners: { + change: function(_selector, storage) { + me.lookup('import-source-file').setDisabled(!storage); + me.lookup('import-source-file').setStorage(storage); + }, + }, + }); + column1.push({ + xtype: 'pveFileSelector', + reference: 'import-source-file', + fieldLabel: gettext("Select Image"), + hidden: true, + disabled: true, + storageContent: 'import', + name: 'import-from', + filter: (rec) => rec?.data?.format === 'qcow2', + nodename: me.nodename, + }); + column1.push({ + xtype: 'pveDiskStorageSelector', + reference: 'import-target', + storageLabel: gettext('Target Storage'), + hidden: true, + disabled: true, + hideSize: true, + storageContent: 'images', + name: 'target', + nodename: me.nodename, + autoSelect: me.insideWizard, + }); } else { column1.push({ xtype: 'textfield', -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pve-devel] [PATCH manager 3/3] ui: qemu hd edit: allow importing a disk from the import storage 2025-03-13 11:17 ` [pve-devel] [PATCH manager 3/3] ui: qemu hd edit: allow importing a disk from the import storage Dominik Csapak @ 2025-03-17 11:05 ` Filip Schauer 0 siblings, 0 replies; 6+ messages in thread From: Filip Schauer @ 2025-03-17 11:05 UTC (permalink / raw) To: pve-devel I ran into a bug when creating a VM in the Web UI: On the "Disks" tab the wizard does not let me proceed with the default configuration. When selecting an image to import and then unchecking the "Import Image" box again, the wizard lets me proceed again. On the other hand, adding a qcow2 image to an existing VM via the Web UI works fine. On 13/03/2025 12:17, Dominik Csapak wrote: > adds a checkbox 'import image' above the storage selector which: > * hides the original storage selector > * shows a 'source storage' selector > * shows a 'import file' selector > * shows a 'target storage' selector > > Since the wizard and the hd edit share this panel, this also works in > the wizard. > > Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> > --- > www/manager6/qemu/HDEdit.js | 72 ++++++++++++++++++++++++++++++++++++- > 1 file changed, 71 insertions(+), 1 deletion(-) > > diff --git a/www/manager6/qemu/HDEdit.js b/www/manager6/qemu/HDEdit.js > index b78647ec..a235815d 100644 > --- a/www/manager6/qemu/HDEdit.js > +++ b/www/manager6/qemu/HDEdit.js > @@ -78,11 +78,17 @@ Ext.define('PVE.qemu.HDInputPanel', { > if (values.hdimage) { > me.drive.file = values.hdimage; > } else { > - me.drive.file = values.hdstorage + ":" + values.disksize; > + let disksize = values.disksize; > + if (values['import-from']) { > + PVE.Utils.propertyStringSet(me.drive, values['import-from'], 'import-from'); > + disksize = 0; > + } > + me.drive.file = `${values.hdstorage}:${disksize}`; > } > me.drive.format = values.diskformat; > } > > + > PVE.Utils.propertyStringSet(me.drive, !values.backup, 'backup', '0'); > PVE.Utils.propertyStringSet(me.drive, values.noreplicate, 'replicate', 'no'); > PVE.Utils.propertyStringSet(me.drive, values.discard, 'discard', 'on'); > @@ -168,6 +174,11 @@ Ext.define('PVE.qemu.HDInputPanel', { > var me = this; > me.down('#hdstorage').setNodename(nodename); > me.down('#hdimage').setStorage(undefined, nodename); > + > + me.lookup('new-disk').setNodename(nodename); > + me.lookup('import-source').setNodename(nodename); > + me.lookup('import-source-file').setNodename(nodename); > + me.lookup('import-target').setNodename(nodename); > }, > > hasAdvanced: true, > @@ -221,12 +232,71 @@ Ext.define('PVE.qemu.HDInputPanel', { > column1.push(me.unusedDisks); > } else if (me.isCreate) { > column1.push({ > + xtype: 'proxmoxcheckbox', > + isFormField: false, > + fieldLabel: gettext("Import Image"), > + listeners: { > + change: function(_cb, value) { > + me.lookup('new-disk').setVisible(!value); > + me.lookup('new-disk').setDisabled(!!value); > + > + me.lookup('import-source').setVisible(!!value); > + me.lookup('import-source').setDisabled(!value); > + me.lookup('import-source-file').setVisible(!!value); > + > + me.lookup('import-target').setVisible(!!value); > + me.lookup('import-target').setDisabled(!value); > + }, > + }, > + }); > + column1.push({ > + reference: 'new-disk', > xtype: 'pveDiskStorageSelector', > storageContent: 'images', > name: 'disk', > nodename: me.nodename, > autoSelect: me.insideWizard, > }); > + column1.push({ > + xtype: 'pveStorageSelector', > + reference: 'import-source', > + fieldLabel: gettext('Import Storage'), > + name: 'import-source-storage', > + hidden: true, > + disabled: true, > + storageContent: 'import', > + nodename: me.nodename, > + autoSelect: me.insideWizard, > + listeners: { > + change: function(_selector, storage) { > + me.lookup('import-source-file').setDisabled(!storage); > + me.lookup('import-source-file').setStorage(storage); > + }, > + }, > + }); > + column1.push({ > + xtype: 'pveFileSelector', > + reference: 'import-source-file', > + fieldLabel: gettext("Select Image"), > + hidden: true, > + disabled: true, > + storageContent: 'import', > + name: 'import-from', > + filter: (rec) => rec?.data?.format === 'qcow2', > + nodename: me.nodename, > + }); > + column1.push({ > + xtype: 'pveDiskStorageSelector', > + reference: 'import-target', > + storageLabel: gettext('Target Storage'), > + hidden: true, > + disabled: true, > + hideSize: true, > + storageContent: 'images', > + name: 'target', > + nodename: me.nodename, > + autoSelect: me.insideWizard, > + }); > } else { > column1.push({ > xtype: 'textfield', _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-03-17 11:05 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2025-03-13 11:17 [pve-devel] [PATCH storage/manager] allow upload & import of qcow2 in the web UI Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH storage 1/1] import: allow upload of qcow2 files into import storage Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH manager 1/3] ui: storage content: allow upload of qcow2 for import type Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH manager 2/3] ui: form: file selector: allow optional filter Dominik Csapak 2025-03-13 11:17 ` [pve-devel] [PATCH manager 3/3] ui: qemu hd edit: allow importing a disk from the import storage Dominik Csapak 2025-03-17 11:05 ` Filip Schauer
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inboxService provided by Proxmox Server Solutions GmbH | Privacy | Legal