* [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 inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal