public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages
@ 2024-05-24 13:21 Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 01/12] copy OVF.pm from qemu-server Dominik Csapak
                   ` (25 more replies)
  0 siblings, 26 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

This series enables importing ova/ovf from directory based storages,
inclusive upload/download via the webui (ova only).

It also improves the ovf importer by parsing the ostype, nics, bootorder
(and firmware from vmware exported files).

I opted to move the OVF.pm to pve-storage, since there is no
real other place where we could put it. I put it in a new module
'GuestImport'

We now extract the images into either a given target storage or in the
import storage in the 'images' dir so accidentally left over images
are discoverable by the ui/cli.

changes from v3:
* fixed dependencies in control file
* removed unnecessary use statements
* removed unnecessary remove helper
* moved 'needs_extract' helper to qemu-server
* removed import storage param from PUT call
* check down/uploaded ova filename more strictly (same as listing)
* improved filepath checking in ovf
* forbid importing when extracted image references a base/backing file
* instead of trying to manually create a proper filename, use 'alloc' to
  create a small (1M) file with the same format and overwrite it with
  renaming. this also solves the cluster locking issue
* prefer using PVE::Storage functions instead of plugin methods in
  ova extraction code
* use $vollist for cleaning up extracted images in qemu-server and
  add manual cleanup for the success case

changes from v2:
* use better 'format' values for embedded images (e.g. ova+vmdk)
* use this format to decide if images should be extracted
* consistent use of the 'safe character' classes when listing
  and parsing
* also list vmdk/qcow2/raw images in content listing
  (this will be useful when we have a gui for the 'import-from'
  in the wizard/disk edit for vms)
* a few gui adaptions


changes from v1:
* move ovf code to GuestImport
* move extract/checking code to GuestImport
* don't return 'image' types from import volumes
* use allow 'safe' characters for filenames of ova/ovfs and inside
* check for non-regular files (e.g. symlinks) after extraction
* add new 'import-extraction-storage' for import
* rename panel in gui for directory storages
* typo fixes
* and probably more, see the individual patches for details

pve-storage:

Dominik Csapak (12):
  copy OVF.pm from qemu-server
  plugin: dir: implement import content type
  plugin: dir: handle ova files for import
  ovf: improve and simplify path checking code
  ovf: implement parsing the ostype
  ovf: implement parsing out firmware type
  ovf: implement rudimentary boot order
  ovf: implement parsing nics
  api: allow ova upload/download
  plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs
  add 'import' content type to 'check_volume_access'
  plugin: file_size_info: don't ignore base path with whitespace

 debian/control                                |   2 +
 src/PVE/API2/Storage/Status.pm                |  19 +-
 src/PVE/GuestImport.pm                        |  77 ++++
 src/PVE/GuestImport/Makefile                  |   3 +
 src/PVE/GuestImport/OVF.pm                    | 383 ++++++++++++++++++
 src/PVE/Makefile                              |   2 +
 src/PVE/Storage.pm                            |  23 +-
 src/PVE/Storage/BTRFSPlugin.pm                |   5 +
 src/PVE/Storage/CIFSPlugin.pm                 |   6 +-
 src/PVE/Storage/CephFSPlugin.pm               |   6 +-
 src/PVE/Storage/DirPlugin.pm                  |  52 ++-
 src/PVE/Storage/GlusterfsPlugin.pm            |   6 +-
 src/PVE/Storage/Makefile                      |   1 +
 src/PVE/Storage/NFSPlugin.pm                  |   6 +-
 src/PVE/Storage/Plugin.pm                     |  17 +-
 src/test/Makefile                             |   5 +-
 src/test/ovf_manifests/Win10-Liz-disk1.vmdk   | Bin 0 -> 65536 bytes
 src/test/ovf_manifests/Win10-Liz.ovf          | 142 +++++++
 .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 143 +++++++
 .../ovf_manifests/Win_2008_R2_two-disks.ovf   | 145 +++++++
 src/test/ovf_manifests/disk1.vmdk             | Bin 0 -> 65536 bytes
 src/test/ovf_manifests/disk2.vmdk             | Bin 0 -> 65536 bytes
 src/test/parse_volname_test.pm                |  33 ++
 src/test/path_to_volume_id_test.pm            |  21 +
 src/test/run_ovf_tests.pl                     |  85 ++++
 25 files changed, 1169 insertions(+), 13 deletions(-)
 create mode 100644 src/PVE/GuestImport.pm
 create mode 100644 src/PVE/GuestImport/Makefile
 create mode 100644 src/PVE/GuestImport/OVF.pm
 create mode 100644 src/test/ovf_manifests/Win10-Liz-disk1.vmdk
 create mode 100755 src/test/ovf_manifests/Win10-Liz.ovf
 create mode 100755 src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
 create mode 100755 src/test/ovf_manifests/Win_2008_R2_two-disks.ovf
 create mode 100644 src/test/ovf_manifests/disk1.vmdk
 create mode 100644 src/test/ovf_manifests/disk2.vmdk
 create mode 100755 src/test/run_ovf_tests.pl

qemu-server:

Dominik Csapak (4):
  api: delete unused OVF.pm
  use OVF from Storage
  api: create: implement extracting disks when needed for import-from
  api: create: add 'import-extraction-storage' parameter

 PVE/API2/Qemu.pm                              |  91 +++++--
 PVE/API2/Qemu/Makefile                        |   2 +-
 PVE/API2/Qemu/OVF.pm                          |  53 ----
 PVE/CLI/qm.pm                                 |   4 +-
 PVE/QemuServer.pm                             |  12 +
 PVE/QemuServer/Helpers.pm                     |   5 +
 PVE/QemuServer/Makefile                       |   1 -
 PVE/QemuServer/OVF.pm                         | 242 ------------------
 debian/control                                |   2 -
 test/Makefile                                 |   5 +-
 test/ovf_manifests/Win10-Liz-disk1.vmdk       | Bin 65536 -> 0 bytes
 test/ovf_manifests/Win10-Liz.ovf              | 142 ----------
 .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 142 ----------
 test/ovf_manifests/Win_2008_R2_two-disks.ovf  | 145 -----------
 test/ovf_manifests/disk1.vmdk                 | Bin 65536 -> 0 bytes
 test/ovf_manifests/disk2.vmdk                 | Bin 65536 -> 0 bytes
 test/run_ovf_tests.pl                         |  71 -----
 17 files changed, 97 insertions(+), 820 deletions(-)
 delete mode 100644 PVE/API2/Qemu/OVF.pm
 delete mode 100644 PVE/QemuServer/OVF.pm
 delete mode 100644 test/ovf_manifests/Win10-Liz-disk1.vmdk
 delete mode 100755 test/ovf_manifests/Win10-Liz.ovf
 delete mode 100755 test/ovf_manifests/Win10-Liz_no_default_ns.ovf
 delete mode 100755 test/ovf_manifests/Win_2008_R2_two-disks.ovf
 delete mode 100644 test/ovf_manifests/disk1.vmdk
 delete mode 100644 test/ovf_manifests/disk2.vmdk
 delete mode 100755 test/run_ovf_tests.pl

pve-manager:

Dominik Csapak (9):
  ui: fix special 'import' icon for non-esxi storages
  ui: guest import: add ova-needs-extracting warning text
  ui: enable import content type for relevant storages
  ui: enable upload/download/remove buttons for 'import' type storages
  ui: disable 'import' button for non importable formats
  ui: import: improve rendering of volume names
  ui: guest import: add storage selector for ova extraction storage
  ui: guest import: change icon/text for non-esxi import storage
  ui: import: show size for dir-based storages

 www/manager6/Utils.js                    | 11 +++++++++--
 www/manager6/form/ContentTypeSelector.js |  2 +-
 www/manager6/storage/Browser.js          | 25 ++++++++++++++++++------
 www/manager6/storage/CephFSEdit.js       |  2 +-
 www/manager6/storage/GlusterFsEdit.js    |  2 +-
 www/manager6/window/GuestImport.js       | 24 +++++++++++++++++++++++
 www/manager6/window/UploadToStorage.js   |  1 +
 7 files changed, 56 insertions(+), 11 deletions(-)

-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 01/12] copy OVF.pm from qemu-server
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 02/12] plugin: dir: implement import content type Dominik Csapak
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

copies the OVF.pm and relevant ovf tests from qemu-server.
We need it here, and it uses PVE::Storage already, and since there is no
intermediary package/repository we could put it, it seems fitting in
here.

Put it in a new GuestImport module

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 debian/control                                |   2 +
 src/PVE/GuestImport/Makefile                  |   3 +
 src/PVE/GuestImport/OVF.pm                    | 241 ++++++++++++++++++
 src/PVE/Makefile                              |   1 +
 src/PVE/Storage/Makefile                      |   1 +
 src/test/Makefile                             |   5 +-
 src/test/ovf_manifests/Win10-Liz-disk1.vmdk   | Bin 0 -> 65536 bytes
 src/test/ovf_manifests/Win10-Liz.ovf          | 142 +++++++++++
 .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 142 +++++++++++
 .../ovf_manifests/Win_2008_R2_two-disks.ovf   | 145 +++++++++++
 src/test/ovf_manifests/disk1.vmdk             | Bin 0 -> 65536 bytes
 src/test/ovf_manifests/disk2.vmdk             | Bin 0 -> 65536 bytes
 src/test/run_ovf_tests.pl                     |  71 ++++++
 13 files changed, 752 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/GuestImport/Makefile
 create mode 100644 src/PVE/GuestImport/OVF.pm
 create mode 100644 src/test/ovf_manifests/Win10-Liz-disk1.vmdk
 create mode 100755 src/test/ovf_manifests/Win10-Liz.ovf
 create mode 100755 src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
 create mode 100755 src/test/ovf_manifests/Win_2008_R2_two-disks.ovf
 create mode 100644 src/test/ovf_manifests/disk1.vmdk
 create mode 100644 src/test/ovf_manifests/disk2.vmdk
 create mode 100755 src/test/run_ovf_tests.pl

diff --git a/debian/control b/debian/control
index d7afa98..dac68ef 100644
--- a/debian/control
+++ b/debian/control
@@ -10,6 +10,7 @@ Build-Depends: debhelper-compat (= 13),
                libpve-common-perl (>= 6.3-2),
                librados2-perl,
                libtest-mockmodule-perl,
+               libxml-libxml-perl,
                lintian,
                perl,
                pve-cluster (>= 5.0-32),
@@ -38,6 +39,7 @@ Depends: ceph-common (>= 12.2~),
          libpve-cluster-perl (>= 8.0.6),
          libpve-common-perl (>= 8.1.1),
          librados2-perl,
+         libxml-libxml-perl,
          lvm2,
          nfs-common,
          proxmox-backup-client (>= 2.1.10~),
diff --git a/src/PVE/GuestImport/Makefile b/src/PVE/GuestImport/Makefile
new file mode 100644
index 0000000..5948384
--- /dev/null
+++ b/src/PVE/GuestImport/Makefile
@@ -0,0 +1,3 @@
+.PHONY: install
+install:
+	install -D -m 0644 OVF.pm ${DESTDIR}${PERLDIR}/PVE/GuestImport/OVF.pm
diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
new file mode 100644
index 0000000..04f8531
--- /dev/null
+++ b/src/PVE/GuestImport/OVF.pm
@@ -0,0 +1,241 @@
+# Open Virtualization Format import routines
+# https://www.dmtf.org/standards/ovf
+package PVE::GuestImport::OVF;
+
+use strict;
+use warnings;
+
+use XML::LibXML;
+use File::Spec;
+use File::Basename;
+use Cwd 'realpath';
+
+use PVE::Tools;
+use PVE::Storage;
+
+# map OVF resources types to descriptive strings
+# this will allow us to explore the xml tree without using magic numbers
+# http://schemas.dmtf.org/wbem/cim-html/2/CIM_ResourceAllocationSettingData.html
+my @resources = (
+    { id => 1, dtmf_name => 'Other' },
+    { id => 2, dtmf_name => 'Computer System' },
+    { id => 3, dtmf_name => 'Processor' },
+    { id => 4, dtmf_name => 'Memory' },
+    { id => 5, dtmf_name => 'IDE Controller', pve_type => 'ide' },
+    { id => 6, dtmf_name => 'Parallel SCSI HBA', pve_type => 'scsi' },
+    { id => 7, dtmf_name => 'FC HBA' },
+    { id => 8, dtmf_name => 'iSCSI HBA' },
+    { id => 9, dtmf_name => 'IB HCA' },
+    { id => 10, dtmf_name => 'Ethernet Adapter' },
+    { id => 11, dtmf_name => 'Other Network Adapter' },
+    { id => 12, dtmf_name => 'I/O Slot' },
+    { id => 13, dtmf_name => 'I/O Device' },
+    { id => 14, dtmf_name => 'Floppy Drive' },
+    { id => 15, dtmf_name => 'CD Drive' },
+    { id => 16, dtmf_name => 'DVD drive' },
+    { id => 17, dtmf_name => 'Disk Drive' },
+    { id => 18, dtmf_name => 'Tape Drive' },
+    { id => 19, dtmf_name => 'Storage Extent' },
+    { id => 20, dtmf_name => 'Other storage device', pve_type => 'sata'},
+    { id => 21, dtmf_name => 'Serial port' },
+    { id => 22, dtmf_name => 'Parallel port' },
+    { id => 23, dtmf_name => 'USB Controller' },
+    { id => 24, dtmf_name => 'Graphics controller' },
+    { id => 25, dtmf_name => 'IEEE 1394 Controller' },
+    { id => 26, dtmf_name => 'Partitionable Unit' },
+    { id => 27, dtmf_name => 'Base Partitionable Unit' },
+    { id => 28, dtmf_name => 'Power' },
+    { id => 29, dtmf_name => 'Cooling Capacity' },
+    { id => 30, dtmf_name => 'Ethernet Switch Port' },
+    { id => 31, dtmf_name => 'Logical Disk' },
+    { id => 32, dtmf_name => 'Storage Volume' },
+    { id => 33, dtmf_name => 'Ethernet Connection' },
+    { id => 34, dtmf_name => 'DMTF reserved' },
+    { id => 35, dtmf_name => 'Vendor Reserved'}
+);
+
+sub find_by {
+    my ($key, $param) = @_;
+    foreach my $resource (@resources) {
+	if ($resource->{$key} eq $param) {
+	    return ($resource);
+	}
+    }
+    return;
+}
+
+sub dtmf_name_to_id {
+    my ($dtmf_name) = @_;
+    my $found = find_by('dtmf_name', $dtmf_name);
+    if ($found) {
+	return $found->{id};
+    } else {
+	return;
+    }
+}
+
+sub id_to_pve {
+    my ($id) = @_;
+    my $resource = find_by('id', $id);
+    if ($resource) {
+	return $resource->{pve_type};
+    } else {
+	return;
+    }
+}
+
+# returns two references, $qm which holds qm.conf style key/values, and \@disks
+sub parse_ovf {
+    my ($ovf, $debug) = @_;
+
+    my $dom = XML::LibXML->load_xml(location => $ovf, no_blanks => 1);
+
+    # register the xml namespaces in a xpath context object
+    # 'ovf' is the default namespace so it will prepended to each xml element
+    my $xpc = XML::LibXML::XPathContext->new($dom);
+    $xpc->registerNs('ovf', 'http://schemas.dmtf.org/ovf/envelope/1');
+    $xpc->registerNs('rasd', 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData');
+    $xpc->registerNs('vssd', 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData');
+
+
+    # hash to save qm.conf parameters
+    my $qm;
+
+    #array to save a disk list
+    my @disks;
+
+    # easy xpath
+    # walk down the dom until we find the matching XML element
+    my $xpath_find_name = "/ovf:Envelope/ovf:VirtualSystem/ovf:Name";
+    my $ovf_name = $xpc->findvalue($xpath_find_name);
+
+    if ($ovf_name) {
+	# PVE::QemuServer::confdesc requires a valid DNS name
+	($qm->{name} = $ovf_name) =~ s/[^a-zA-Z0-9\-\.]//g;
+    } else {
+	warn "warning: unable to parse the VM name in this OVF manifest, generating a default value\n";
+    }
+
+    # middle level xpath
+    # element[child] search the elements which have this [child]
+    my $processor_id = dtmf_name_to_id('Processor');
+    my $xpath_find_vcpu_count = "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${processor_id}]/rasd:VirtualQuantity";
+    $qm->{'cores'} = $xpc->findvalue($xpath_find_vcpu_count);
+
+    my $memory_id = dtmf_name_to_id('Memory');
+    my $xpath_find_memory = ("/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${memory_id}]/rasd:VirtualQuantity");
+    $qm->{'memory'} = $xpc->findvalue($xpath_find_memory);
+
+    # middle level xpath
+    # here we expect multiple results, so we do not read the element value with
+    # findvalue() but store multiple elements with findnodes()
+    my $disk_id = dtmf_name_to_id('Disk Drive');
+    my $xpath_find_disks="/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${disk_id}]";
+    my @disk_items = $xpc->findnodes($xpath_find_disks);
+
+    # disks metadata is split in four different xml elements:
+    # * as an Item node of type DiskDrive in the VirtualHardwareSection
+    # * as an Disk node in the DiskSection
+    # * as a File node in the References section
+    # * each Item node also holds a reference to its owning controller
+    #
+    # we iterate over the list of Item nodes of type disk drive, and for each item,
+    # find the corresponding Disk node, and File node and owning controller
+    # when all the nodes has been found out, we copy the relevant information to
+    # a $pve_disk hash ref, which we push to @disks;
+
+    foreach my $item_node (@disk_items) {
+
+	my $disk_node;
+	my $file_node;
+	my $controller_node;
+	my $pve_disk;
+
+	print "disk item:\n", $item_node->toString(1), "\n" if $debug;
+
+	# from Item, find corresponding Disk node
+	# here the dot means the search should start from the current element in dom
+	my $host_resource = $xpc->findvalue('rasd:HostResource', $item_node);
+	my $disk_section_path;
+	my $disk_id;
+
+	# RFC 3986 "2.3.  Unreserved Characters"
+	my $valid_uripath_chars = qr/[[:alnum:]]|[\-\._~]/;
+
+	if ($host_resource =~ m|^ovf:/(${valid_uripath_chars}+)/(${valid_uripath_chars}+)$|) {
+	    $disk_section_path = $1;
+	    $disk_id = $2;
+	} else {
+	   warn "invalid host ressource $host_resource, skipping\n";
+	   next;
+	}
+	printf "disk section path: $disk_section_path and disk id: $disk_id\n" if $debug;
+
+	# tricky xpath
+	# @ means we filter the result query based on a the value of an item attribute ( @ = attribute)
+	# @ needs to be escaped to prevent Perl double quote interpolation
+	my $xpath_find_fileref = sprintf("/ovf:Envelope/ovf:DiskSection/\
+ovf:Disk[\@ovf:diskId='%s']/\@ovf:fileRef", $disk_id);
+	my $fileref = $xpc->findvalue($xpath_find_fileref);
+
+	my $valid_url_chars = qr@${valid_uripath_chars}|/@;
+	if (!$fileref || $fileref !~ m/^${valid_url_chars}+$/) {
+	    warn "invalid host ressource $host_resource, skipping\n";
+	    next;
+	}
+
+	# from Disk Node, find corresponding filepath
+	my $xpath_find_filepath = sprintf("/ovf:Envelope/ovf:References/ovf:File[\@ovf:id='%s']/\@ovf:href", $fileref);
+	my $filepath = $xpc->findvalue($xpath_find_filepath);
+	if (!$filepath) {
+	    warn "invalid file reference $fileref, skipping\n";
+	    next;
+	}
+	print "file path: $filepath\n" if $debug;
+
+	# from Item, find owning Controller type
+	my $controller_id = $xpc->findvalue('rasd:Parent', $item_node);
+	my $xpath_find_parent_type = sprintf("/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/\
+ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
+	my $controller_type = $xpc->findvalue($xpath_find_parent_type);
+	if (!$controller_type) {
+	    warn "invalid or missing controller: $controller_type, skipping\n";
+	    next;
+	}
+	print "owning controller type: $controller_type\n" if $debug;
+
+	# extract corresponding Controller node details
+	my $adress_on_controller = $xpc->findvalue('rasd:AddressOnParent', $item_node);
+	my $pve_disk_address = id_to_pve($controller_type) . $adress_on_controller;
+
+	# resolve symlinks and relative path components
+	# and die if the diskimage is not somewhere under the $ovf path
+	my $ovf_dir = realpath(dirname(File::Spec->rel2abs($ovf)));
+	my $backing_file_path = realpath(join ('/', $ovf_dir, $filepath));
+	if ($backing_file_path !~ /^\Q${ovf_dir}\E/) {
+	    die "error parsing $filepath, are you using a symlink ?\n";
+	}
+
+	if (!-e $backing_file_path) {
+	    die "error parsing $filepath, file seems not to exist at $backing_file_path\n";
+	}
+
+	($backing_file_path) = $backing_file_path =~ m|^(/.*)|; # untaint
+
+	my $virtual_size = PVE::Storage::file_size_info($backing_file_path);
+	die "error parsing $backing_file_path, cannot determine file size\n"
+	    if !$virtual_size;
+
+	$pve_disk = {
+	    disk_address => $pve_disk_address,
+	    backing_file => $backing_file_path,
+	    virtual_size => $virtual_size
+	};
+	push @disks, $pve_disk;
+
+    }
+
+    return {qm => $qm, disks => \@disks};
+}
+
+1;
diff --git a/src/PVE/Makefile b/src/PVE/Makefile
index d438804..e15a275 100644
--- a/src/PVE/Makefile
+++ b/src/PVE/Makefile
@@ -6,6 +6,7 @@ install:
 	install -D -m 0644 Diskmanage.pm ${DESTDIR}${PERLDIR}/PVE/Diskmanage.pm
 	install -D -m 0644 CephConfig.pm ${DESTDIR}${PERLDIR}/PVE/CephConfig.pm
 	make -C Storage install
+	make -C GuestImport install
 	make -C API2 install
 	make -C CLI install
 
diff --git a/src/PVE/Storage/Makefile b/src/PVE/Storage/Makefile
index d5cc942..2daa0da 100644
--- a/src/PVE/Storage/Makefile
+++ b/src/PVE/Storage/Makefile
@@ -14,6 +14,7 @@ SOURCES= \
 	PBSPlugin.pm \
 	BTRFSPlugin.pm \
 	LvmThinPlugin.pm \
+	OVF.pm \
 	ESXiPlugin.pm
 
 .PHONY: install
diff --git a/src/test/Makefile b/src/test/Makefile
index c54b10f..12991da 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -1,6 +1,6 @@
 all: test
 
-test: test_zfspoolplugin test_disklist test_bwlimit test_plugin
+test: test_zfspoolplugin test_disklist test_bwlimit test_plugin test_ovf
 
 test_zfspoolplugin: run_test_zfspoolplugin.pl
 	./run_test_zfspoolplugin.pl
@@ -13,3 +13,6 @@ test_bwlimit: run_bwlimit_tests.pl
 
 test_plugin: run_plugin_tests.pl
 	./run_plugin_tests.pl
+
+test_ovf: run_ovf_tests.pl
+	./run_ovf_tests.pl
diff --git a/src/test/ovf_manifests/Win10-Liz-disk1.vmdk b/src/test/ovf_manifests/Win10-Liz-disk1.vmdk
new file mode 100644
index 0000000000000000000000000000000000000000..662354a3d1333a2f6c4364005e53bfe7cd8b9044
GIT binary patch
literal 65536
zcmeIvy>HV%7zbeUF`dK)46s<q+^B&Pi6H|eMIb;zP1VdMK8V$P$u?2T)IS|NNta69
zSXw<No$qu%-}$}AUq|21A0<ihr0Gwa-nQ%QGfCR@wmshsN%A;JUhL<u_T%+U7Sd<o
zW^TMU0^M{}R2S(eR@1Ur*Q@eVF^^#r%c@u{hyC#J%V?Nq?*?z)=UG^1Wn9+n(yx6B
z(=ujtJiA)QVP~;guI5EOE2iV-%_??6=%y!^b+aeU_aA6Z4X2azC>{U!a5_FoJCkDB
zKRozW{5{B<Li)YUBEQ&fJe$RRZCRbA$5|CacQiT<A<uvIHbq(g$>yIY=etVNVcI$B
zY@^?CwTN|j)tg?;i)G&AZFqPqoW(5P2K~XUq>9sqVVe!!?y@Y;)^#k~TefEvd2_XU
z^M@5mfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk4^iOdL%ftb
z5g<T-009C72;3>~`p!f^fB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
p0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK;Zui`~%h>XmtPp

literal 0
HcmV?d00001

diff --git a/src/test/ovf_manifests/Win10-Liz.ovf b/src/test/ovf_manifests/Win10-Liz.ovf
new file mode 100755
index 0000000..bf4b41a
--- /dev/null
+++ b/src/test/ovf_manifests/Win10-Liz.ovf
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--Generated by VMware ovftool 4.1.0 (build-2982904), UTC time: 2017-02-07T13:50:15.265014Z-->
+<Envelope vmw:buildId="build-2982904" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <References>
+    <File ovf:href="Win10-Liz-disk1.vmdk" ovf:id="file1" ovf:size="9155243008"/>
+  </References>
+  <DiskSection>
+    <Info>Virtual disk information</Info>
+    <Disk ovf:capacity="128" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="16798056448"/>
+  </DiskSection>
+  <NetworkSection>
+    <Info>The list of logical networks</Info>
+    <Network ovf:name="bridged">
+      <Description>The bridged network</Description>
+    </Network>
+  </NetworkSection>
+  <VirtualSystem ovf:id="vm">
+    <Info>A virtual machine</Info>
+    <Name>Win10-Liz</Name>
+    <OperatingSystemSection ovf:id="1" vmw:osType="windows9_64Guest">
+      <Info>The kind of installed guest operating system</Info>
+    </OperatingSystemSection>
+    <VirtualHardwareSection>
+      <Info>Virtual hardware requirements</Info>
+      <System>
+        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
+        <vssd:InstanceID>0</vssd:InstanceID>
+        <vssd:VirtualSystemIdentifier>Win10-Liz</vssd:VirtualSystemIdentifier>
+        <vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
+      </System>
+      <Item>
+        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
+        <rasd:Description>Number of Virtual CPUs</rasd:Description>
+        <rasd:ElementName>4 virtual CPU(s)</rasd:ElementName>
+        <rasd:InstanceID>1</rasd:InstanceID>
+        <rasd:ResourceType>3</rasd:ResourceType>
+        <rasd:VirtualQuantity>4</rasd:VirtualQuantity>
+      </Item>
+      <Item>
+        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
+        <rasd:Description>Memory Size</rasd:Description>
+        <rasd:ElementName>6144MB of memory</rasd:ElementName>
+        <rasd:InstanceID>2</rasd:InstanceID>
+        <rasd:ResourceType>4</rasd:ResourceType>
+        <rasd:VirtualQuantity>6144</rasd:VirtualQuantity>
+      </Item>
+      <Item>
+        <rasd:Address>0</rasd:Address>
+        <rasd:Description>SATA Controller</rasd:Description>
+        <rasd:ElementName>sataController0</rasd:ElementName>
+        <rasd:InstanceID>3</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.sata.ahci</rasd:ResourceSubType>
+        <rasd:ResourceType>20</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:Address>0</rasd:Address>
+        <rasd:Description>USB Controller (XHCI)</rasd:Description>
+        <rasd:ElementName>usb3</rasd:ElementName>
+        <rasd:InstanceID>4</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.usb.xhci</rasd:ResourceSubType>
+        <rasd:ResourceType>23</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:Address>0</rasd:Address>
+        <rasd:Description>USB Controller (EHCI)</rasd:Description>
+        <rasd:ElementName>usb</rasd:ElementName>
+        <rasd:InstanceID>5</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.usb.ehci</rasd:ResourceSubType>
+        <rasd:ResourceType>23</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="ehciEnabled" vmw:value="true"/>
+      </Item>
+      <Item>
+        <rasd:Address>0</rasd:Address>
+        <rasd:Description>SCSI Controller</rasd:Description>
+        <rasd:ElementName>scsiController0</rasd:ElementName>
+        <rasd:InstanceID>6</rasd:InstanceID>
+        <rasd:ResourceSubType>lsilogicsas</rasd:ResourceSubType>
+        <rasd:ResourceType>6</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+        <rasd:ElementName>serial0</rasd:ElementName>
+        <rasd:InstanceID>7</rasd:InstanceID>
+        <rasd:ResourceType>21</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="yieldOnPoll" vmw:value="false"/>
+      </Item>
+      <Item>
+        <rasd:AddressOnParent>0</rasd:AddressOnParent>
+        <rasd:ElementName>disk0</rasd:ElementName>
+        <rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
+        <rasd:InstanceID>8</rasd:InstanceID>
+        <rasd:Parent>6</rasd:Parent>
+        <rasd:ResourceType>17</rasd:ResourceType>
+      </Item>
+      <Item>
+        <rasd:AddressOnParent>2</rasd:AddressOnParent>
+        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+        <rasd:Connection>bridged</rasd:Connection>
+        <rasd:Description>E1000e ethernet adapter on &quot;bridged&quot;</rasd:Description>
+        <rasd:ElementName>ethernet0</rasd:ElementName>
+        <rasd:InstanceID>9</rasd:InstanceID>
+        <rasd:ResourceSubType>E1000e</rasd:ResourceSubType>
+        <rasd:ResourceType>10</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="false"/>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+        <rasd:ElementName>sound</rasd:ElementName>
+        <rasd:InstanceID>10</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.soundcard.hdaudio</rasd:ResourceSubType>
+        <rasd:ResourceType>1</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+        <rasd:ElementName>video</rasd:ElementName>
+        <rasd:InstanceID>11</rasd:InstanceID>
+        <rasd:ResourceType>24</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="true"/>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+        <rasd:ElementName>vmci</rasd:ElementName>
+        <rasd:InstanceID>12</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
+        <rasd:ResourceType>1</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AddressOnParent>1</rasd:AddressOnParent>
+        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+        <rasd:ElementName>cdrom0</rasd:ElementName>
+        <rasd:InstanceID>13</rasd:InstanceID>
+        <rasd:Parent>3</rasd:Parent>
+        <rasd:ResourceType>15</rasd:ResourceType>
+      </Item>
+      <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="true"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
+    </VirtualHardwareSection>
+  </VirtualSystem>
+</Envelope>
diff --git a/src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf b/src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
new file mode 100755
index 0000000..b93540f
--- /dev/null
+++ b/src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--Generated by VMware ovftool 4.1.0 (build-2982904), UTC time: 2017-02-07T13:50:15.265014Z-->
+<Envelope vmw:buildId="build-2982904" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <References>
+    <File ovf:href="Win10-Liz-disk1.vmdk" ovf:id="file1" ovf:size="9155243008"/>
+  </References>
+  <DiskSection>
+    <Info>Virtual disk information</Info>
+    <Disk ovf:capacity="128" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="16798056448"/>
+  </DiskSection>
+  <NetworkSection>
+    <Info>The list of logical networks</Info>
+    <Network ovf:name="bridged">
+      <Description>The bridged network</Description>
+    </Network>
+  </NetworkSection>
+  <VirtualSystem ovf:id="vm">
+    <Info>A virtual machine</Info>
+    <Name>Win10-Liz</Name>
+    <OperatingSystemSection ovf:id="1" vmw:osType="windows9_64Guest">
+      <Info>The kind of installed guest operating system</Info>
+    </OperatingSystemSection>
+    <VirtualHardwareSection>
+      <Info>Virtual hardware requirements</Info>
+      <System>
+        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
+        <vssd:InstanceID>0</vssd:InstanceID>
+        <vssd:VirtualSystemIdentifier>Win10-Liz</vssd:VirtualSystemIdentifier>
+        <vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
+      </System>
+      <Item>
+        <rasd:AllocationUnits xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">hertz * 10^6</rasd:AllocationUnits>
+        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">Number of Virtual CPUs</rasd:Description>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">4 virtual CPU(s)</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">1</rasd:InstanceID>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">3</rasd:ResourceType>
+        <rasd:VirtualQuantity xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">4</rasd:VirtualQuantity>
+      </Item>
+      <Item>
+        <rasd:AllocationUnits xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">byte * 2^20</rasd:AllocationUnits>
+        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">Memory Size</rasd:Description>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6144MB of memory</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">2</rasd:InstanceID>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">4</rasd:ResourceType>
+        <rasd:VirtualQuantity xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6144</rasd:VirtualQuantity>
+      </Item>
+      <Item>
+        <rasd:Address xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">0</rasd:Address>
+        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">SATA Controller</rasd:Description>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">sataController0</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">3</rasd:InstanceID>
+        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.sata.ahci</rasd:ResourceSubType>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">20</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:Address xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">0</rasd:Address>
+        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">USB Controller (XHCI)</rasd:Description>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">usb3</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">4</rasd:InstanceID>
+        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.usb.xhci</rasd:ResourceSubType>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">23</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:Address xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">0</rasd:Address>
+        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">USB Controller (EHCI)</rasd:Description>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">usb</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">5</rasd:InstanceID>
+        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.usb.ehci</rasd:ResourceSubType>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">23</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="ehciEnabled" vmw:value="true"/>
+      </Item>
+      <Item>
+        <rasd:Address xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">0</rasd:Address>
+        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">SCSI Controller</rasd:Description>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">scsiController0</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6</rasd:InstanceID>
+        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">lsilogicsas</rasd:ResourceSubType>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">true</rasd:AutomaticAllocation>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">serial0</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">7</rasd:InstanceID>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">21</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="yieldOnPoll" vmw:value="false"/>
+      </Item>
+      <Item>
+        <rasd:AddressOnParent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" >0</rasd:AddressOnParent>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" >disk0</rasd:ElementName>
+        <rasd:HostResource xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" >ovf:/disk/vmdisk1</rasd:HostResource>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">8</rasd:InstanceID>
+        <rasd:Parent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6</rasd:Parent>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">17</rasd:ResourceType>
+      </Item>
+      <Item>
+        <rasd:AddressOnParent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">2</rasd:AddressOnParent>
+        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">true</rasd:AutomaticAllocation>
+        <rasd:Connection xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">bridged</rasd:Connection>
+        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">E1000e ethernet adapter on &quot;bridged&quot;</rasd:Description>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">ethernet0</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">9</rasd:InstanceID>
+        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">E1000e</rasd:ResourceSubType>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">10</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="false"/>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">false</rasd:AutomaticAllocation>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">sound</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">10</rasd:InstanceID>
+        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.soundcard.hdaudio</rasd:ResourceSubType>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">1</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">false</rasd:AutomaticAllocation>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">video</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">11</rasd:InstanceID>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">24</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="true"/>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">false</rasd:AutomaticAllocation>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmci</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">12</rasd:InstanceID>
+        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.vmci</rasd:ResourceSubType>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">1</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AddressOnParent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">1</rasd:AddressOnParent>
+        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">false</rasd:AutomaticAllocation>
+        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">cdrom0</rasd:ElementName>
+        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">13</rasd:InstanceID>
+        <rasd:Parent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">3</rasd:Parent>
+        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">15</rasd:ResourceType>
+      </Item>
+      <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="true"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
+    </VirtualHardwareSection>
+  </VirtualSystem>
+</Envelope>
diff --git a/src/test/ovf_manifests/Win_2008_R2_two-disks.ovf b/src/test/ovf_manifests/Win_2008_R2_two-disks.ovf
new file mode 100755
index 0000000..a563aab
--- /dev/null
+++ b/src/test/ovf_manifests/Win_2008_R2_two-disks.ovf
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--Generated by VMware ovftool 4.1.0 (build-2982904), UTC time: 2017-02-27T15:09:29.768974Z-->
+<Envelope vmw:buildId="build-2982904" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <References>
+    <File ovf:href="disk1.vmdk" ovf:id="file1" ovf:size="3481968640"/>
+    <File ovf:href="disk2.vmdk" ovf:id="file2" ovf:size="68096"/>
+  </References>
+  <DiskSection>
+    <Info>Virtual disk information</Info>
+    <Disk ovf:capacity="40" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="7684882432"/>
+    <Disk ovf:capacity="1" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk2" ovf:fileRef="file2" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="0"/>
+  </DiskSection>
+  <NetworkSection>
+    <Info>The list of logical networks</Info>
+    <Network ovf:name="bridged">
+      <Description>The bridged network</Description>
+    </Network>
+  </NetworkSection>
+  <VirtualSystem ovf:id="vm">
+    <Info>A virtual machine</Info>
+    <Name>Win_2008-R2x64</Name>
+    <OperatingSystemSection ovf:id="103" vmw:osType="windows7Server64Guest">
+      <Info>The kind of installed guest operating system</Info>
+    </OperatingSystemSection>
+    <VirtualHardwareSection>
+      <Info>Virtual hardware requirements</Info>
+      <System>
+        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
+        <vssd:InstanceID>0</vssd:InstanceID>
+        <vssd:VirtualSystemIdentifier>Win_2008-R2x64</vssd:VirtualSystemIdentifier>
+        <vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
+      </System>
+      <Item>
+        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
+        <rasd:Description>Number of Virtual CPUs</rasd:Description>
+        <rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
+        <rasd:InstanceID>1</rasd:InstanceID>
+        <rasd:ResourceType>3</rasd:ResourceType>
+        <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
+      </Item>
+      <Item>
+        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
+        <rasd:Description>Memory Size</rasd:Description>
+        <rasd:ElementName>2048MB of memory</rasd:ElementName>
+        <rasd:InstanceID>2</rasd:InstanceID>
+        <rasd:ResourceType>4</rasd:ResourceType>
+        <rasd:VirtualQuantity>2048</rasd:VirtualQuantity>
+      </Item>
+      <Item>
+        <rasd:Address>0</rasd:Address>
+        <rasd:Description>SATA Controller</rasd:Description>
+        <rasd:ElementName>sataController0</rasd:ElementName>
+        <rasd:InstanceID>3</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.sata.ahci</rasd:ResourceSubType>
+        <rasd:ResourceType>20</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:Address>0</rasd:Address>
+        <rasd:Description>USB Controller (EHCI)</rasd:Description>
+        <rasd:ElementName>usb</rasd:ElementName>
+        <rasd:InstanceID>4</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.usb.ehci</rasd:ResourceSubType>
+        <rasd:ResourceType>23</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="ehciEnabled" vmw:value="true"/>
+      </Item>
+      <Item>
+        <rasd:Address>0</rasd:Address>
+        <rasd:Description>SCSI Controller</rasd:Description>
+        <rasd:ElementName>scsiController0</rasd:ElementName>
+        <rasd:InstanceID>5</rasd:InstanceID>
+        <rasd:ResourceSubType>lsilogicsas</rasd:ResourceSubType>
+        <rasd:ResourceType>6</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+        <rasd:ElementName>serial0</rasd:ElementName>
+        <rasd:InstanceID>6</rasd:InstanceID>
+        <rasd:ResourceType>21</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="yieldOnPoll" vmw:value="false"/>
+      </Item>
+      <Item>
+        <rasd:AddressOnParent>0</rasd:AddressOnParent>
+        <rasd:ElementName>disk0</rasd:ElementName>
+        <rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
+        <rasd:InstanceID>7</rasd:InstanceID>
+        <rasd:Parent>5</rasd:Parent>
+        <rasd:ResourceType>17</rasd:ResourceType>
+      </Item>
+      <Item>
+        <rasd:AddressOnParent>1</rasd:AddressOnParent>
+        <rasd:ElementName>disk1</rasd:ElementName>
+        <rasd:HostResource>ovf:/disk/vmdisk2</rasd:HostResource>
+        <rasd:InstanceID>8</rasd:InstanceID>
+        <rasd:Parent>5</rasd:Parent>
+        <rasd:ResourceType>17</rasd:ResourceType>
+      </Item>
+      <Item>
+        <rasd:AddressOnParent>2</rasd:AddressOnParent>
+        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+        <rasd:Connection>bridged</rasd:Connection>
+        <rasd:Description>E1000 ethernet adapter on &quot;bridged&quot;</rasd:Description>
+        <rasd:ElementName>ethernet0</rasd:ElementName>
+        <rasd:InstanceID>9</rasd:InstanceID>
+        <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
+        <rasd:ResourceType>10</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="false"/>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+        <rasd:ElementName>sound</rasd:ElementName>
+        <rasd:InstanceID>10</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.soundcard.hdaudio</rasd:ResourceSubType>
+        <rasd:ResourceType>1</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+        <rasd:ElementName>video</rasd:ElementName>
+        <rasd:InstanceID>11</rasd:InstanceID>
+        <rasd:ResourceType>24</rasd:ResourceType>
+        <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="true"/>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+        <rasd:ElementName>vmci</rasd:ElementName>
+        <rasd:InstanceID>12</rasd:InstanceID>
+        <rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
+        <rasd:ResourceType>1</rasd:ResourceType>
+      </Item>
+      <Item ovf:required="false">
+        <rasd:AddressOnParent>1</rasd:AddressOnParent>
+        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+        <rasd:ElementName>cdrom0</rasd:ElementName>
+        <rasd:InstanceID>13</rasd:InstanceID>
+        <rasd:Parent>3</rasd:Parent>
+        <rasd:ResourceType>15</rasd:ResourceType>
+      </Item>
+      <vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="true"/>
+      <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="true"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="soft"/>
+      <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
+    </VirtualHardwareSection>
+  </VirtualSystem>
+</Envelope>
diff --git a/src/test/ovf_manifests/disk1.vmdk b/src/test/ovf_manifests/disk1.vmdk
new file mode 100644
index 0000000000000000000000000000000000000000..8660602343a1a955f9bcf2e6beaed99316dd8167
GIT binary patch
literal 65536
zcmeIvy>HV%7zbeUF`dK)46s<q9ua7|WuUkSgpg2EwX+*viPe0`HWAtSr(-ASlAWQ|
zbJF?F{+-YFKK_yYyn2=-$&0qXY<t)4ch@B8o_Fo_en^t%N%H0}e|H$~AF`0X3J-JR
zqY>z*Sy|tuS*)j3xo%d~*K!`iCRTO1T8@X|%lB-2b2}Q2@{gmi&a1d=x<|K%7N%9q
zn|Qfh$8m45TCV10Gb^W)c4ZxVA@tMpzfJp2S{y#m?iwzx)01@a>+{9rJna?j=ZAyM
zqPW{FznsOxiSi~-&+<BkewLkuP!u<VO<6U6^7*&xtNr=XaoRiS?V{gtwTMl%9Za|L
za#^%_7k)SjXE85!!SM7bspGUQewUqo+Glx@ubWtPwRL-yMO)CL`L7O2fB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pkPg~&a(=JbS1PBlyK!5-N0!ISx
zkM7+PAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
d1PBlyK!5-N0t5&UAV7cs0RjXF5cr=0{{Ra(Wheju

literal 0
HcmV?d00001

diff --git a/src/test/ovf_manifests/disk2.vmdk b/src/test/ovf_manifests/disk2.vmdk
new file mode 100644
index 0000000000000000000000000000000000000000..c4634513348b392202898374f1c8d2d51d565b27
GIT binary patch
literal 65536
zcmeIvy>HV%7zbeUF`dK)46s<q9#IIDI%J@v2!xPOQ?;`jUy0Rx$u<$$`lr`+(j_}X
ztLLQio&7tX?|uAp{Oj^rk|Zyh{<7(9yX&q=(mrq7>)ntf&y(cMe*SJh-aTX?eH9+&
z#z!O2Psc@dn~q~OEsJ%%D!&!;7&fu2iq&#-6u$l#k8ZBB&%=0f64qH6mv#5(X4k^B
zj9DEow(B_REmq6byr^fzbkeM>VlRY#diJkw-bwTQ2bx{O`BgehC%?a(PtMX_-hBS!
zV6(_?yX6<NxIa-=XX$BH#n2y*PeaJ_>%pcd>%ZCj`_<*{eCa6d4SQYmC$1K;F1Lf}
zc3v#=CU3(J2jMJcc^4cVA0$<rHpO?@@uyvu<=MK9Wm{XjSCKabJ(~aOpacjIAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAn>#W-ahT}R7ZdS0RjXF5Fl_M
z@c!W5Edc@q2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
eAV7cs0RjXF5FkK+009C72oNAZfB=F2DR2*l=VfOA

literal 0
HcmV?d00001

diff --git a/src/test/run_ovf_tests.pl b/src/test/run_ovf_tests.pl
new file mode 100755
index 0000000..5a80ab2
--- /dev/null
+++ b/src/test/run_ovf_tests.pl
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use lib qw(..); # prepend .. to @INC so we use the local version of PVE packages
+
+use FindBin '$Bin';
+use PVE::GuestImport::OVF;
+use Test::More;
+
+use Data::Dumper;
+
+my $test_manifests = join ('/', $Bin, 'ovf_manifests');
+
+print "parsing ovfs\n";
+
+my $win2008 = eval { PVE::GuestImport::OVF::parse_ovf("$test_manifests/Win_2008_R2_two-disks.ovf") };
+if (my $err = $@) {
+    fail('parse win2008');
+    warn("error: $err\n");
+} else {
+    ok('parse win2008');
+}
+my $win10 = eval { PVE::GuestImport::OVF::parse_ovf("$test_manifests/Win10-Liz.ovf") };
+if (my $err = $@) {
+    fail('parse win10');
+    warn("error: $err\n");
+} else {
+    ok('parse win10');
+}
+my $win10noNs = eval { PVE::GuestImport::OVF::parse_ovf("$test_manifests/Win10-Liz_no_default_ns.ovf") };
+if (my $err = $@) {
+    fail("parse win10 no default rasd NS");
+    warn("error: $err\n");
+} else {
+    ok('parse win10 no default rasd NS');
+}
+
+print "testing disks\n";
+
+is($win2008->{disks}->[0]->{disk_address}, 'scsi0', 'multidisk vm has the correct first disk controller');
+is($win2008->{disks}->[0]->{backing_file}, "$test_manifests/disk1.vmdk", 'multidisk vm has the correct first disk backing device');
+is($win2008->{disks}->[0]->{virtual_size}, 2048, 'multidisk vm has the correct first disk size');
+
+is($win2008->{disks}->[1]->{disk_address}, 'scsi1', 'multidisk vm has the correct second disk controller');
+is($win2008->{disks}->[1]->{backing_file}, "$test_manifests/disk2.vmdk", 'multidisk vm has the correct second disk backing device');
+is($win2008->{disks}->[1]->{virtual_size}, 2048, 'multidisk vm has the correct second disk size');
+
+is($win10->{disks}->[0]->{disk_address}, 'scsi0', 'single disk vm has the correct disk controller');
+is($win10->{disks}->[0]->{backing_file}, "$test_manifests/Win10-Liz-disk1.vmdk", 'single disk vm has the correct disk backing device');
+is($win10->{disks}->[0]->{virtual_size}, 2048, 'single disk vm has the correct size');
+
+is($win10noNs->{disks}->[0]->{disk_address}, 'scsi0', 'single disk vm (no default rasd NS) has the correct disk controller');
+is($win10noNs->{disks}->[0]->{backing_file}, "$test_manifests/Win10-Liz-disk1.vmdk", 'single disk vm (no default rasd NS) has the correct disk backing device');
+is($win10noNs->{disks}->[0]->{virtual_size}, 2048, 'single disk vm (no default rasd NS) has the correct size');
+
+print "\ntesting vm.conf extraction\n";
+
+is($win2008->{qm}->{name}, 'Win2008-R2x64', 'win2008 VM name is correct');
+is($win2008->{qm}->{memory}, '2048', 'win2008 VM memory is correct');
+is($win2008->{qm}->{cores}, '1', 'win2008 VM cores are correct');
+
+is($win10->{qm}->{name}, 'Win10-Liz', 'win10 VM name is correct');
+is($win10->{qm}->{memory}, '6144', 'win10 VM memory is correct');
+is($win10->{qm}->{cores}, '4', 'win10 VM cores are correct');
+
+is($win10noNs->{qm}->{name}, 'Win10-Liz', 'win10 VM (no default rasd NS) name is correct');
+is($win10noNs->{qm}->{memory}, '6144', 'win10 VM (no default rasd NS) memory is correct');
+is($win10noNs->{qm}->{cores}, '4', 'win10 VM (no default rasd NS) cores are correct');
+
+done_testing();
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 02/12] plugin: dir: implement import content type
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 01/12] copy OVF.pm from qemu-server Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 03/12] plugin: dir: handle ova files for import Dominik Csapak
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

in DirPlugin and not Plugin (because of cyclic dependency of
Plugin -> OVF -> Storage -> Plugin otherwise)

only ovf is currently supported (though ova will be shown in import
listing), expects the files to not be in a subdir, and adjacent to the
ovf file.

listed will be all ovf/qcow2/raw/vmdk files.
ovf because it can be imported, and the rest because they can be used
in the 'import-from' part of qemu-server.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/GuestImport/OVF.pm         |  5 ++++-
 src/PVE/Storage.pm                 |  8 +++++++
 src/PVE/Storage/DirPlugin.pm       | 36 +++++++++++++++++++++++++++++-
 src/PVE/Storage/Plugin.pm          | 11 ++++++++-
 src/test/parse_volname_test.pm     | 13 +++++++++++
 src/test/path_to_volume_id_test.pm | 13 +++++++++++
 6 files changed, 83 insertions(+), 3 deletions(-)

diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
index 04f8531..899cfdc 100644
--- a/src/PVE/GuestImport/OVF.pm
+++ b/src/PVE/GuestImport/OVF.pm
@@ -221,6 +221,8 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
 	}
 
 	($backing_file_path) = $backing_file_path =~ m|^(/.*)|; # untaint
+	($filepath) = $filepath =~ m|^(${PVE::Storage::SAFE_CHAR_CLASS_RE}+)$|; # untaint & check no sub/parent dirs
+	die "invalid path\n" if !$filepath;
 
 	my $virtual_size = PVE::Storage::file_size_info($backing_file_path);
 	die "error parsing $backing_file_path, cannot determine file size\n"
@@ -229,7 +231,8 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
 	$pve_disk = {
 	    disk_address => $pve_disk_address,
 	    backing_file => $backing_file_path,
-	    virtual_size => $virtual_size
+	    virtual_size => $virtual_size,
+	    relative_path => $filepath,
 	};
 	push @disks, $pve_disk;
 
diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm
index f19a115..1ed91c2 100755
--- a/src/PVE/Storage.pm
+++ b/src/PVE/Storage.pm
@@ -114,6 +114,10 @@ our $VZTMPL_EXT_RE_1 = qr/\.tar\.(gz|xz|zst)/i;
 
 our $BACKUP_EXT_RE_2 = qr/\.(tgz|(?:tar|vma)(?:\.(${\PVE::Storage::Plugin::COMPRESSOR_RE}))?)/;
 
+our $IMPORT_EXT_RE_1 = qr/\.(ovf|qcow2|raw|vmdk)/;
+
+our $SAFE_CHAR_CLASS_RE = qr/[a-zA-Z0-9\-\.\+\=\_]/;
+
 # FIXME remove with PVE 8.0, add versioned breaks for pve-manager
 our $vztmpl_extension_re = $VZTMPL_EXT_RE_1;
 
@@ -612,6 +616,7 @@ sub path_to_volume_id {
 	my $backupdir = $plugin->get_subdir($scfg, 'backup');
 	my $privatedir = $plugin->get_subdir($scfg, 'rootdir');
 	my $snippetsdir = $plugin->get_subdir($scfg, 'snippets');
+	my $importdir = $plugin->get_subdir($scfg, 'import');
 
 	if ($path =~ m!^$imagedir/(\d+)/([^/\s]+)$!) {
 	    my $vmid = $1;
@@ -640,6 +645,9 @@ sub path_to_volume_id {
 	} elsif ($path =~ m!^$snippetsdir/([^/]+)$!) {
 	    my $name = $1;
 	    return ('snippets', "$sid:snippets/$name");
+	} elsif ($path =~ m!^$importdir/(${SAFE_CHAR_CLASS_RE}+${IMPORT_EXT_RE_1})$!) {
+	    my $name = $1;
+	    return ('import', "$sid:import/$name");
 	}
     }
 
diff --git a/src/PVE/Storage/DirPlugin.pm b/src/PVE/Storage/DirPlugin.pm
index 2efa8d5..3e3b1e7 100644
--- a/src/PVE/Storage/DirPlugin.pm
+++ b/src/PVE/Storage/DirPlugin.pm
@@ -10,6 +10,7 @@ use IO::File;
 use POSIX;
 
 use PVE::Storage::Plugin;
+use PVE::GuestImport::OVF;
 use PVE::JSONSchema qw(get_standard_option);
 
 use base qw(PVE::Storage::Plugin);
@@ -22,7 +23,7 @@ sub type {
 
 sub plugindata {
     return {
-	content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, none => 1 },
+	content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, none => 1, import => 1 },
 		     { images => 1,  rootdir => 1 }],
 	format => [ { raw => 1, qcow2 => 1, vmdk => 1, subvol => 1 } , 'raw' ],
     };
@@ -247,4 +248,37 @@ sub check_config {
     return $opts;
 }
 
+sub get_import_metadata {
+    my ($class, $scfg, $volname, $storeid) = @_;
+
+    my ($vtype, $name, undef, undef, undef, undef, $fmt) = $class->parse_volname($volname);
+    die "invalid content type '$vtype'\n" if $vtype ne 'import';
+    die "invalid format\n" if $fmt ne 'ova' && $fmt ne 'ovf';
+
+    # NOTE: all types of warnings must be added to the return schema of the import-metadata API endpoint
+    my $warnings = [];
+
+    my $path = $class->path($scfg, $volname, $storeid, undef);
+    my $res = PVE::GuestImport::OVF::parse_ovf($path);
+    my $disks = {};
+    for my $disk ($res->{disks}->@*) {
+	my $id = $disk->{disk_address};
+	my $size = $disk->{virtual_size};
+	my $path = $disk->{relative_path};
+	$disks->{$id} = {
+	    volid => "$storeid:import/$path",
+	    defined($size) ? (size => $size) : (),
+	};
+    }
+
+    return {
+	type => 'vm',
+	source => $volname,
+	'create-args' => $res->{qm},
+	'disks' => $disks,
+	warnings => $warnings,
+	net => [],
+    };
+}
+
 1;
diff --git a/src/PVE/Storage/Plugin.pm b/src/PVE/Storage/Plugin.pm
index b5a54c1..fc30894 100644
--- a/src/PVE/Storage/Plugin.pm
+++ b/src/PVE/Storage/Plugin.pm
@@ -654,6 +654,8 @@ sub parse_volname {
 	return ('backup', $fn);
     } elsif ($volname =~ m!^snippets/([^/]+)$!) {
 	return ('snippets', $1);
+    } elsif ($volname =~ m!^import/(${PVE::Storage::SAFE_CHAR_CLASS_RE}+$PVE::Storage::IMPORT_EXT_RE_1)$!) {
+	return ('import', $1, undef, undef, undef, undef, $2);
     }
 
     die "unable to parse directory volume name '$volname'\n";
@@ -666,6 +668,7 @@ my $vtype_subdirs = {
     vztmpl => 'template/cache',
     backup => 'dump',
     snippets => 'snippets',
+    import => 'import',
 };
 
 sub get_vtype_subdirs {
@@ -1230,7 +1233,7 @@ sub list_images {
     return $res;
 }
 
-# list templates ($tt = <iso|vztmpl|backup|snippets>)
+# list templates ($tt = <iso|vztmpl|backup|snippets|import>)
 my $get_subdir_files = sub {
     my ($sid, $path, $tt, $vmid) = @_;
 
@@ -1286,6 +1289,10 @@ my $get_subdir_files = sub {
 		volid => "$sid:snippets/". basename($fn),
 		format => 'snippet',
 	    };
+	} elsif ($tt eq 'import') {
+	    next if $fn !~ m!/(${PVE::Storage::SAFE_CHAR_CLASS_RE}+$PVE::Storage::IMPORT_EXT_RE_1)$!i;
+
+	    $info = { volid => "$sid:import/$1", format => "$2" };
 	}
 
 	$info->{size} = $st->size;
@@ -1320,6 +1327,8 @@ sub list_volumes {
 		$data = $get_subdir_files->($storeid, $path, 'backup', $vmid);
 	    } elsif ($type eq 'snippets') {
 		$data = $get_subdir_files->($storeid, $path, 'snippets');
+	    } elsif ($type eq 'import') {
+		$data = $get_subdir_files->($storeid, $path, 'import');
 	    }
 	}
 
diff --git a/src/test/parse_volname_test.pm b/src/test/parse_volname_test.pm
index d6ac885..0367b83 100644
--- a/src/test/parse_volname_test.pm
+++ b/src/test/parse_volname_test.pm
@@ -81,6 +81,14 @@ my $tests = [
 	expected    => ['snippets', 'hookscript.pl'],
     },
     #
+    # Import
+    #
+    {
+	description => "Import, ovf",
+	volname     => 'import/import.ovf',
+	expected    => ['import', 'import.ovf', undef, undef, undef ,undef, 'ovf'],
+    },
+    #
     # failed matches
     #
     {
@@ -123,6 +131,11 @@ my $tests = [
 	volname     => "$vmid/base-$vmid-disk-0.qcow2/ssss/vm-$vmid-disk-0.qcow2",
 	expected    => "unable to parse volume filename 'base-$vmid-disk-0.qcow2/ssss/vm-$vmid-disk-0.qcow2'\n",
     },
+    {
+	description => "Failed match: import dir but no ova/ovf/disk image",
+	volname	    => "import/test.foo",
+	expected    => "unable to parse directory volume name 'import/test.foo'\n",
+    },
 ];
 
 # create more test cases for VM disk images matches
diff --git a/src/test/path_to_volume_id_test.pm b/src/test/path_to_volume_id_test.pm
index 8149c88..0e0c15f 100644
--- a/src/test/path_to_volume_id_test.pm
+++ b/src/test/path_to_volume_id_test.pm
@@ -174,6 +174,14 @@ my @tests = (
 	    'local:vztmpl/debian-10.0-standard_10.0-1_amd64.tar.xz',
 	],
     },
+    {
+	description => 'Import, ovf',
+	volname     => "$storage_dir/import/import.ovf",
+	expected    => [
+	    'import',
+	    'local:import/import.ovf',
+	],
+    },
 
     # no matches, path or files with failures
     {
@@ -231,6 +239,11 @@ my @tests = (
 	volname     => "$storage_dir/images/ssss/vm-1234-disk-0.qcow2",
 	expected    => [''],
     },
+    {
+	description => 'Import, non ova/ovf/disk image in import dir',
+	volname     => "$storage_dir/import/test.foo",
+	expected    => [''],
+    },
 );
 
 plan tests => scalar @tests + 1;
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 03/12] plugin: dir: handle ova files for import
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 01/12] copy OVF.pm from qemu-server Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 02/12] plugin: dir: implement import content type Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-06-12 15:56   ` Max Carrara
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 04/12] ovf: improve and simplify path checking code Dominik Csapak
                   ` (22 subsequent siblings)
  25 siblings, 1 reply; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

since we want to handle ova files (which are only ovf+images bundled in
a tar file) for import, add code that handles that.

we introduce a valid volname for files contained in ovas like this:

 storage:import/archive.ova/disk-1.vmdk

by basically treating the last part of the path as the name for the
contained disk we want.

in that case we return 'import' as type with 'vmdk/qcow2/raw' as format
(we cannot use something like 'ova+vmdk' without extending the 'format'
parsing to that for all storages/formats. This is because it runs
though a verify format check at least once)

we then provide 3 functions to use for that:

* copy_needs_extraction: determines from the given volid (like above) if
  that needs extraction to copy it, currently only 'import' vtype + a
  volid with the above format returns true

* extract_disk_from_import_file: this actually extracts the file from
  the archive. Currently only ova is supported, so the extraction with
  'tar' is hardcoded, but again we can easily extend/modify that should
  we need to.

  we currently extract into the either the import storage or a given
  target storage in the images directory so if the cleanup does not
  happen, the user can still see and interact with the image via
  api/cli/gui

* cleanup_extracted_image: intended to cleanup the extracted images from
  above

we have to modify the `parse_ovf` a bit to handle the missing disk
images, and we parse the size out of the ovf part (since this is
informal only, it should be no problem if we cannot parse it sometimes)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/API2/Storage/Status.pm     |  1 +
 src/PVE/GuestImport.pm             | 77 ++++++++++++++++++++++++++++++
 src/PVE/GuestImport/OVF.pm         | 52 +++++++++++++++++---
 src/PVE/Makefile                   |  1 +
 src/PVE/Storage.pm                 |  4 +-
 src/PVE/Storage/DirPlugin.pm       | 15 +++++-
 src/PVE/Storage/Plugin.pm          |  4 ++
 src/test/parse_volname_test.pm     | 20 ++++++++
 src/test/path_to_volume_id_test.pm |  8 ++++
 9 files changed, 173 insertions(+), 9 deletions(-)
 create mode 100644 src/PVE/GuestImport.pm

diff --git a/src/PVE/API2/Storage/Status.pm b/src/PVE/API2/Storage/Status.pm
index dc6cc69..acde730 100644
--- a/src/PVE/API2/Storage/Status.pm
+++ b/src/PVE/API2/Storage/Status.pm
@@ -749,6 +749,7 @@ __PACKAGE__->register_method({
 				'efi-state-lost',
 				'guest-is-running',
 				'nvme-unsupported',
+				'ova-needs-extracting',
 				'ovmf-with-lsi-unsupported',
 				'serial-port-socket-only',
 			    ],
diff --git a/src/PVE/GuestImport.pm b/src/PVE/GuestImport.pm
new file mode 100644
index 0000000..988d1f6
--- /dev/null
+++ b/src/PVE/GuestImport.pm
@@ -0,0 +1,77 @@
+package PVE::GuestImport;
+
+use strict;
+use warnings;
+
+use File::Path;
+
+use PVE::Storage;
+use PVE::Tools qw(run_command);
+
+sub extract_disk_from_import_file {
+    my ($volid, $vmid, $target_storeid) = @_;
+
+    my ($source_storeid, $volname) = PVE::Storage::parse_volume_id($volid);
+    $target_storeid //= $source_storeid;
+    my $cfg = PVE::Storage::config();
+
+    my ($vtype, $name, undef, undef, undef, undef, $fmt) =
+	PVE::Storage::parse_volname($cfg, $volid);
+
+    die "only files with content type 'import' can be extracted\n"
+	if $vtype ne 'import' || $fmt !~ m/^ova\+/;
+
+    # extract the inner file from the name
+    my $archive_volid;
+    my $inner_file;
+    my $inner_fmt;
+    if ($name =~ m!^(.*\.ova)/(${PVE::Storage::SAFE_CHAR_CLASS_RE}+)$!) {
+	$archive_volid = "$source_storeid:import/$1";
+	$inner_file = $2;
+	($inner_fmt) = $fmt =~ /^ova\+(.*)$/;
+    } else {
+	die "cannot extract $volid - invalid volname $volname\n";
+    }
+
+    my $ova_path = PVE::Storage::path($cfg, $archive_volid);
+
+    my $tmpdir = PVE::Storage::get_image_dir($cfg, $target_storeid, $vmid);
+    my $pid = $$;
+    $tmpdir .= "/tmp_${pid}_${vmid}";
+    mkpath $tmpdir;
+
+    ($ova_path) = $ova_path =~ m|^(.*)$|; # untaint
+
+    my $source_path = "$tmpdir/$inner_file";
+    my $target_path;
+    my $target_volid;
+    eval {
+	run_command(['tar', '-x', '--force-local', '-C', $tmpdir, '-f', $ova_path, $inner_file]);
+
+	# check for symlinks and other non regular files
+	if (-l $source_path || ! -f $source_path) {
+	    die "only regular files are allowed\n";
+	}
+
+	# TODO check for base images in file
+
+	# create temporary 1M image that will get overwritten by the rename
+	# to reserve the filename and take care of locking
+	$target_volid = PVE::Storage::vdisk_alloc($cfg, $target_storeid, $vmid, $inner_fmt, undef, 1024);
+	$target_path = PVE::Storage::path($cfg, $target_volid);
+
+	print "renaming $source_path to $target_path\n";
+
+	rename($source_path, $target_path) or die "unable to move - $!\n";
+    };
+    if (my $err = $@) {
+	File::Path::remove_tree($tmpdir);
+	die "error during extraction: $err\n";
+    }
+
+    File::Path::remove_tree($tmpdir);
+
+    return $target_volid;
+}
+
+1;
diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
index 899cfdc..0728684 100644
--- a/src/PVE/GuestImport/OVF.pm
+++ b/src/PVE/GuestImport/OVF.pm
@@ -84,11 +84,37 @@ sub id_to_pve {
     }
 }
 
+# technically defined in DSP0004 (https://www.dmtf.org/dsp/DSP0004) as an ABNF
+# but realistically this always takes the form of 'byte * base^exponent'
+sub try_parse_capacity_unit {
+    my ($unit_text) = @_;
+
+    if ($unit_text =~ m/^\s*byte\s*\*\s*([0-9]+)\s*\^\s*([0-9]+)\s*$/) {
+	my $base = $1;
+	my $exp = $2;
+	return $base ** $exp;
+    }
+
+    return undef;
+}
+
 # returns two references, $qm which holds qm.conf style key/values, and \@disks
 sub parse_ovf {
-    my ($ovf, $debug) = @_;
+    my ($ovf, $isOva, $debug) = @_;
+
+    # we have to ignore missing disk images for ova
+    my $dom;
+    if ($isOva) {
+	my $raw = "";
+	PVE::Tools::run_command(['tar', '-xO', '--wildcards', '--occurrence=1', '-f', $ovf, '*.ovf'], outfunc => sub {
+	    my $line = shift;
+	    $raw .= $line;
+	});
+	$dom = XML::LibXML->load_xml(string => $raw, no_blanks => 1);
+    } else {
+	$dom = XML::LibXML->load_xml(location => $ovf, no_blanks => 1);
+    }
 
-    my $dom = XML::LibXML->load_xml(location => $ovf, no_blanks => 1);
 
     # register the xml namespaces in a xpath context object
     # 'ovf' is the default namespace so it will prepended to each xml element
@@ -176,7 +202,17 @@ sub parse_ovf {
 	# @ needs to be escaped to prevent Perl double quote interpolation
 	my $xpath_find_fileref = sprintf("/ovf:Envelope/ovf:DiskSection/\
 ovf:Disk[\@ovf:diskId='%s']/\@ovf:fileRef", $disk_id);
+	my $xpath_find_capacity = sprintf("/ovf:Envelope/ovf:DiskSection/\
+ovf:Disk[\@ovf:diskId='%s']/\@ovf:capacity", $disk_id);
+	my $xpath_find_capacity_unit = sprintf("/ovf:Envelope/ovf:DiskSection/\
+ovf:Disk[\@ovf:diskId='%s']/\@ovf:capacityAllocationUnits", $disk_id);
 	my $fileref = $xpc->findvalue($xpath_find_fileref);
+	my $capacity = $xpc->findvalue($xpath_find_capacity);
+	my $capacity_unit = $xpc->findvalue($xpath_find_capacity_unit);
+	my $virtual_size;
+	if (my $factor = try_parse_capacity_unit($capacity_unit)) {
+	    $virtual_size = $capacity * $factor;
+	}
 
 	my $valid_url_chars = qr@${valid_uripath_chars}|/@;
 	if (!$fileref || $fileref !~ m/^${valid_url_chars}+$/) {
@@ -216,7 +252,7 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
 	    die "error parsing $filepath, are you using a symlink ?\n";
 	}
 
-	if (!-e $backing_file_path) {
+	if (!-e $backing_file_path && !$isOva) {
 	    die "error parsing $filepath, file seems not to exist at $backing_file_path\n";
 	}
 
@@ -224,16 +260,20 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
 	($filepath) = $filepath =~ m|^(${PVE::Storage::SAFE_CHAR_CLASS_RE}+)$|; # untaint & check no sub/parent dirs
 	die "invalid path\n" if !$filepath;
 
-	my $virtual_size = PVE::Storage::file_size_info($backing_file_path);
-	die "error parsing $backing_file_path, cannot determine file size\n"
-	    if !$virtual_size;
+	if (!$isOva) {
+	    my $size = PVE::Storage::file_size_info($backing_file_path);
+	    die "error parsing $backing_file_path, cannot determine file size\n"
+		if !$size;
 
+	    $virtual_size = $size;
+	}
 	$pve_disk = {
 	    disk_address => $pve_disk_address,
 	    backing_file => $backing_file_path,
 	    virtual_size => $virtual_size,
 	    relative_path => $filepath,
 	};
+	$pve_disk->{virtual_size} = $virtual_size if defined($virtual_size);
 	push @disks, $pve_disk;
 
     }
diff --git a/src/PVE/Makefile b/src/PVE/Makefile
index e15a275..0af3081 100644
--- a/src/PVE/Makefile
+++ b/src/PVE/Makefile
@@ -5,6 +5,7 @@ install:
 	install -D -m 0644 Storage.pm ${DESTDIR}${PERLDIR}/PVE/Storage.pm
 	install -D -m 0644 Diskmanage.pm ${DESTDIR}${PERLDIR}/PVE/Diskmanage.pm
 	install -D -m 0644 CephConfig.pm ${DESTDIR}${PERLDIR}/PVE/CephConfig.pm
+	install -D -m 0644 GuestImport.pm ${DESTDIR}${PERLDIR}/PVE/GuestImport.pm
 	make -C Storage install
 	make -C GuestImport install
 	make -C API2 install
diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm
index 1ed91c2..bd6c4df 100755
--- a/src/PVE/Storage.pm
+++ b/src/PVE/Storage.pm
@@ -114,10 +114,12 @@ our $VZTMPL_EXT_RE_1 = qr/\.tar\.(gz|xz|zst)/i;
 
 our $BACKUP_EXT_RE_2 = qr/\.(tgz|(?:tar|vma)(?:\.(${\PVE::Storage::Plugin::COMPRESSOR_RE}))?)/;
 
-our $IMPORT_EXT_RE_1 = qr/\.(ovf|qcow2|raw|vmdk)/;
+our $IMPORT_EXT_RE_1 = qr/\.(ova|ovf|qcow2|raw|vmdk)/;
 
 our $SAFE_CHAR_CLASS_RE = qr/[a-zA-Z0-9\-\.\+\=\_]/;
 
+our $OVA_CONTENT_RE_1 = qr/${SAFE_CHAR_CLASS_RE}+\.(qcow2|raw|vmdk)/;
+
 # FIXME remove with PVE 8.0, add versioned breaks for pve-manager
 our $vztmpl_extension_re = $VZTMPL_EXT_RE_1;
 
diff --git a/src/PVE/Storage/DirPlugin.pm b/src/PVE/Storage/DirPlugin.pm
index 3e3b1e7..ea89464 100644
--- a/src/PVE/Storage/DirPlugin.pm
+++ b/src/PVE/Storage/DirPlugin.pm
@@ -258,15 +258,26 @@ sub get_import_metadata {
     # NOTE: all types of warnings must be added to the return schema of the import-metadata API endpoint
     my $warnings = [];
 
+    my $isOva = 0;
+    if ($name =~ m/\.ova$/) {
+	$isOva = 1;
+	push @$warnings, { type => 'ova-needs-extracting' };
+    }
     my $path = $class->path($scfg, $volname, $storeid, undef);
-    my $res = PVE::GuestImport::OVF::parse_ovf($path);
+    my $res = PVE::GuestImport::OVF::parse_ovf($path, $isOva);
     my $disks = {};
     for my $disk ($res->{disks}->@*) {
 	my $id = $disk->{disk_address};
 	my $size = $disk->{virtual_size};
 	my $path = $disk->{relative_path};
+	my $volid;
+	if ($isOva) {
+	    $volid = "$storeid:$volname/$path";
+	} else {
+	    $volid = "$storeid:import/$path",
+	}
 	$disks->{$id} = {
-	    volid => "$storeid:import/$path",
+	    volid => $volid,
 	    defined($size) ? (size => $size) : (),
 	};
     }
diff --git a/src/PVE/Storage/Plugin.pm b/src/PVE/Storage/Plugin.pm
index fc30894..848e053 100644
--- a/src/PVE/Storage/Plugin.pm
+++ b/src/PVE/Storage/Plugin.pm
@@ -654,6 +654,10 @@ sub parse_volname {
 	return ('backup', $fn);
     } elsif ($volname =~ m!^snippets/([^/]+)$!) {
 	return ('snippets', $1);
+    } elsif ($volname =~ m!^import/(${PVE::Storage::SAFE_CHAR_CLASS_RE}+\.ova\/${PVE::Storage::OVA_CONTENT_RE_1})$!) {
+	my $archive = $1;
+	my $format = $2;
+	return ('import', $archive, undef, undef, undef, undef, "ova+$format");
     } elsif ($volname =~ m!^import/(${PVE::Storage::SAFE_CHAR_CLASS_RE}+$PVE::Storage::IMPORT_EXT_RE_1)$!) {
 	return ('import', $1, undef, undef, undef, undef, $2);
     }
diff --git a/src/test/parse_volname_test.pm b/src/test/parse_volname_test.pm
index 0367b83..bc7b4e8 100644
--- a/src/test/parse_volname_test.pm
+++ b/src/test/parse_volname_test.pm
@@ -83,11 +83,31 @@ my $tests = [
     #
     # Import
     #
+    {
+	description => "Import, ova",
+	volname     => 'import/import.ova',
+	expected    => ['import', 'import.ova', undef, undef, undef ,undef, 'ova'],
+    },
     {
 	description => "Import, ovf",
 	volname     => 'import/import.ovf',
 	expected    => ['import', 'import.ovf', undef, undef, undef ,undef, 'ovf'],
     },
+    {
+	description => "Import, innner file of ova",
+	volname     => 'import/import.ova/disk.qcow2',
+	expected    => ['import', 'import.ova/disk.qcow2', undef, undef, undef, undef, 'ova+qcow2'],
+    },
+    {
+	description => "Import, innner file of ova",
+	volname     => 'import/import.ova/disk.vmdk',
+	expected    => ['import', 'import.ova/disk.vmdk', undef, undef, undef, undef, 'ova+vmdk'],
+    },
+    {
+	description => "Import, innner file of ova",
+	volname     => 'import/import.ova/disk.raw',
+	expected    => ['import', 'import.ova/disk.raw', undef, undef, undef, undef, 'ova+raw'],
+    },
     #
     # failed matches
     #
diff --git a/src/test/path_to_volume_id_test.pm b/src/test/path_to_volume_id_test.pm
index 0e0c15f..0d238f9 100644
--- a/src/test/path_to_volume_id_test.pm
+++ b/src/test/path_to_volume_id_test.pm
@@ -174,6 +174,14 @@ my @tests = (
 	    'local:vztmpl/debian-10.0-standard_10.0-1_amd64.tar.xz',
 	],
     },
+    {
+	description => 'Import, ova',
+	volname     => "$storage_dir/import/import.ova",
+	expected    => [
+	    'import',
+	    'local:import/import.ova',
+	],
+    },
     {
 	description => 'Import, ovf',
 	volname     => "$storage_dir/import/import.ovf",
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 04/12] ovf: improve and simplify path checking code
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (2 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 03/12] plugin: dir: handle ova files for import Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 05/12] ovf: implement parsing the ostype Dominik Csapak
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

moves the filepath code a bit more closer to where it's actually used
checks the contained path before trying to find it's absolute path
properly add error handling to realpath

instead of checking the combined ovf_path + filepath, just make sure
filepath can't point to anythign besides a file in this directory
by checking for '.' and '..' (slashes are not allowed in SAFE_CHAR_CLASS_RE)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/GuestImport/OVF.pm | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
index 0728684..ed1c870 100644
--- a/src/PVE/GuestImport/OVF.pm
+++ b/src/PVE/GuestImport/OVF.pm
@@ -220,15 +220,6 @@ ovf:Disk[\@ovf:diskId='%s']/\@ovf:capacityAllocationUnits", $disk_id);
 	    next;
 	}
 
-	# from Disk Node, find corresponding filepath
-	my $xpath_find_filepath = sprintf("/ovf:Envelope/ovf:References/ovf:File[\@ovf:id='%s']/\@ovf:href", $fileref);
-	my $filepath = $xpc->findvalue($xpath_find_filepath);
-	if (!$filepath) {
-	    warn "invalid file reference $fileref, skipping\n";
-	    next;
-	}
-	print "file path: $filepath\n" if $debug;
-
 	# from Item, find owning Controller type
 	my $controller_id = $xpc->findvalue('rasd:Parent', $item_node);
 	my $xpath_find_parent_type = sprintf("/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/\
@@ -244,22 +235,31 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
 	my $adress_on_controller = $xpc->findvalue('rasd:AddressOnParent', $item_node);
 	my $pve_disk_address = id_to_pve($controller_type) . $adress_on_controller;
 
+	# from Disk Node, find corresponding filepath
+	my $xpath_find_filepath = sprintf("/ovf:Envelope/ovf:References/ovf:File[\@ovf:id='%s']/\@ovf:href", $fileref);
+	my $filepath = $xpc->findvalue($xpath_find_filepath);
+	if (!$filepath) {
+	    warn "invalid file reference $fileref, skipping\n";
+	    next;
+	}
+	print "file path: $filepath\n" if $debug;
+	my $original_filepath = $filepath;
+	($filepath) = $filepath =~ m|^(${PVE::Storage::SAFE_CHAR_CLASS_RE}+)$|; # untaint & check no sub/parent dirs
+	die "referenced path '$original_filepath' is invalid\n" if !$filepath || $filepath eq "." || $filepath eq "..";
+
 	# resolve symlinks and relative path components
 	# and die if the diskimage is not somewhere under the $ovf path
-	my $ovf_dir = realpath(dirname(File::Spec->rel2abs($ovf)));
-	my $backing_file_path = realpath(join ('/', $ovf_dir, $filepath));
-	if ($backing_file_path !~ /^\Q${ovf_dir}\E/) {
-	    die "error parsing $filepath, are you using a symlink ?\n";
-	}
+	my $ovf_dir = realpath(dirname(File::Spec->rel2abs($ovf)))
+	    or die "could not get absolute path of $ovf: $!\n";
+	my $backing_file_path = realpath(join ('/', $ovf_dir, $filepath))
+	    or die "could not get absolute path of $filepath: $!\n";
+
+	($backing_file_path) = $backing_file_path =~ m|^(/.*)|; # untaint
 
 	if (!-e $backing_file_path && !$isOva) {
 	    die "error parsing $filepath, file seems not to exist at $backing_file_path\n";
 	}
 
-	($backing_file_path) = $backing_file_path =~ m|^(/.*)|; # untaint
-	($filepath) = $filepath =~ m|^(${PVE::Storage::SAFE_CHAR_CLASS_RE}+)$|; # untaint & check no sub/parent dirs
-	die "invalid path\n" if !$filepath;
-
 	if (!$isOva) {
 	    my $size = PVE::Storage::file_size_info($backing_file_path);
 	    die "error parsing $backing_file_path, cannot determine file size\n"
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 05/12] ovf: implement parsing the ostype
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (3 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 04/12] ovf: improve and simplify path checking code Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 06/12] ovf: implement parsing out firmware type Dominik Csapak
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

use the standards info about the ostypes to map to our own
(see comment for link to the relevant part of the dmtf schema)

every type that is not listed we map to 'other', so no need to have it
in a list.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/GuestImport/OVF.pm | 69 ++++++++++++++++++++++++++++++++++++++
 src/test/run_ovf_tests.pl  |  5 +++
 2 files changed, 74 insertions(+)

diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
index ed1c870..a17d591 100644
--- a/src/PVE/GuestImport/OVF.pm
+++ b/src/PVE/GuestImport/OVF.pm
@@ -54,6 +54,71 @@ my @resources = (
     { id => 35, dtmf_name => 'Vendor Reserved'}
 );
 
+# see https://schemas.dmtf.org/wbem/cim-html/2.55.0+/CIM_OperatingSystem.html
+my $ostype_ids = {
+    18 => 'winxp', # 'WINNT',
+    29 => 'solaris', # 'Solaris',
+    36 => 'l26', # 'LINUX',
+    58 => 'w2k', # 'Windows 2000',
+    67 => 'wxp', #'Windows XP',
+    69 => 'w2k3', # 'Microsoft Windows Server 2003',
+    70 => 'w2k3', # 'Microsoft Windows Server 2003 64-Bit',
+    71 => 'wxp', # 'Windows XP 64-Bit',
+    72 => 'wxp', # 'Windows XP Embedded',
+    73 => 'wvista', # 'Windows Vista',
+    74 => 'wvista', # 'Windows Vista 64-Bit',
+    75 => 'wxp', # 'Windows Embedded for Point of Service', ??
+    76 => 'w2k8', # 'Microsoft Windows Server 2008',
+    77 => 'w2k8', # 'Microsoft Windows Server 2008 64-Bit',
+    79 => 'l26', # 'RedHat Enterprise Linux',
+    80 => 'l26', # 'RedHat Enterprise Linux 64-Bit',
+    81 => 'solaris', #'Solaris 64-Bit',
+    82 => 'l26', # 'SUSE',
+    83 => 'l26', # 'SUSE 64-Bit',
+    84 => 'l26', # 'SLES',
+    85 => 'l26', # 'SLES 64-Bit',
+    87 => 'l26', # 'Novell Linux Desktop',
+    89 => 'l26', # 'Mandriva',
+    90 => 'l26', # 'Mandriva 64-Bit',
+    91 => 'l26', # 'TurboLinux',
+    92 => 'l26', # 'TurboLinux 64-Bit',
+    93 => 'l26', # 'Ubuntu',
+    94 => 'l26', # 'Ubuntu 64-Bit',
+    95 => 'l26', # 'Debian',
+    96 => 'l26', # 'Debian 64-Bit',
+    97 => 'l24', # 'Linux 2.4.x',
+    98 => 'l24', # 'Linux 2.4.x 64-Bit',
+    99 => 'l26', # 'Linux 2.6.x',
+    100 => 'l26', # 'Linux 2.6.x 64-Bit',
+    101 => 'l26', # 'Linux 64-Bit',
+    103 => 'win7', # 'Microsoft Windows Server 2008 R2',
+    105 => 'win7', # 'Microsoft Windows 7',
+    106 => 'l26', # 'CentOS 32-bit',
+    107 => 'l26', # 'CentOS 64-bit',
+    108 => 'l26', # 'Oracle Linux 32-bit',
+    109 => 'l26', # 'Oracle Linux 64-bit',
+    111 => 'win8', # 'Microsoft Windows Server 2011', ??
+    112 => 'win8', # 'Microsoft Windows Server 2012',
+    113 => 'win8', # 'Microsoft Windows 8',
+    114 => 'win8', # 'Microsoft Windows 8 64-bit',
+    115 => 'win8', # 'Microsoft Windows Server 2012 R2',
+    116 => 'win10', # 'Microsoft Windows Server 2016',
+    117 => 'win8', # 'Microsoft Windows 8.1',
+    118 => 'win8', # 'Microsoft Windows 8.1 64-bit',
+    119 => 'win10', # 'Microsoft Windows 10',
+    120 => 'win10', # 'Microsoft Windows 10 64-bit',
+    121 => 'win10', # 'Microsoft Windows Server 2019',
+    122 => 'win11', # 'Microsoft Windows 11 64-bit',
+    123 => 'win11', # 'Microsoft Windows Server 2022',
+    # others => 'other',
+};
+
+sub get_ostype {
+    my ($id) = @_;
+
+    return $ostype_ids->{$id} // 'other';
+}
+
 sub find_by {
     my ($key, $param) = @_;
     foreach my $resource (@resources) {
@@ -159,6 +224,10 @@ sub parse_ovf {
     my $xpath_find_disks="/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${disk_id}]";
     my @disk_items = $xpc->findnodes($xpath_find_disks);
 
+    my $xpath_find_ostype_id = "/ovf:Envelope/ovf:VirtualSystem/ovf:OperatingSystemSection/\@ovf:id";
+    my $ostype_id = $xpc->findvalue($xpath_find_ostype_id);
+    $qm->{ostype} = get_ostype($ostype_id);
+
     # disks metadata is split in four different xml elements:
     # * as an Item node of type DiskDrive in the VirtualHardwareSection
     # * as an Disk node in the DiskSection
diff --git a/src/test/run_ovf_tests.pl b/src/test/run_ovf_tests.pl
index 5a80ab2..c433c9d 100755
--- a/src/test/run_ovf_tests.pl
+++ b/src/test/run_ovf_tests.pl
@@ -59,13 +59,18 @@ print "\ntesting vm.conf extraction\n";
 is($win2008->{qm}->{name}, 'Win2008-R2x64', 'win2008 VM name is correct');
 is($win2008->{qm}->{memory}, '2048', 'win2008 VM memory is correct');
 is($win2008->{qm}->{cores}, '1', 'win2008 VM cores are correct');
+is($win2008->{qm}->{ostype}, 'win7', 'win2008 VM ostype is correcty');
 
 is($win10->{qm}->{name}, 'Win10-Liz', 'win10 VM name is correct');
 is($win10->{qm}->{memory}, '6144', 'win10 VM memory is correct');
 is($win10->{qm}->{cores}, '4', 'win10 VM cores are correct');
+# older esxi/ovf standard used 'other' for windows10
+is($win10->{qm}->{ostype}, 'other', 'win10 VM ostype is correct');
 
 is($win10noNs->{qm}->{name}, 'Win10-Liz', 'win10 VM (no default rasd NS) name is correct');
 is($win10noNs->{qm}->{memory}, '6144', 'win10 VM (no default rasd NS) memory is correct');
 is($win10noNs->{qm}->{cores}, '4', 'win10 VM (no default rasd NS) cores are correct');
+# older esxi/ovf standard used 'other' for windows10
+is($win10noNs->{qm}->{ostype}, 'other', 'win10 VM (no default rasd NS) ostype is correct');
 
 done_testing();
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 06/12] ovf: implement parsing out firmware type
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (4 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 05/12] ovf: implement parsing the ostype Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 07/12] ovf: implement rudimentary boot order Dominik Csapak
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

it seems there is no part of the ovf standard that handles which type of
bios there is (at least i could not find it). Every ovf/ova i tested
either has no info about it, or has it in a vmware specific property
which we parse here.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/GuestImport/OVF.pm                         | 5 +++++
 src/PVE/Storage/DirPlugin.pm                       | 5 +++++
 src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf | 1 +
 src/test/run_ovf_tests.pl                          | 1 +
 4 files changed, 12 insertions(+)

diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
index a17d591..e050667 100644
--- a/src/PVE/GuestImport/OVF.pm
+++ b/src/PVE/GuestImport/OVF.pm
@@ -228,6 +228,11 @@ sub parse_ovf {
     my $ostype_id = $xpc->findvalue($xpath_find_ostype_id);
     $qm->{ostype} = get_ostype($ostype_id);
 
+    # vmware specific firmware config, seems to not be standardized in ovf ?
+    my $xpath_find_firmware = "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/vmw:Config[\@vmw:key=\"firmware\"]/\@vmw:value";
+    my $firmware = $xpc->findvalue($xpath_find_firmware) || 'seabios';
+    $qm->{bios} = 'ovmf' if $firmware eq 'efi';
+
     # disks metadata is split in four different xml elements:
     # * as an Item node of type DiskDrive in the VirtualHardwareSection
     # * as an Disk node in the DiskSection
diff --git a/src/PVE/Storage/DirPlugin.pm b/src/PVE/Storage/DirPlugin.pm
index ea89464..b98b603 100644
--- a/src/PVE/Storage/DirPlugin.pm
+++ b/src/PVE/Storage/DirPlugin.pm
@@ -282,6 +282,11 @@ sub get_import_metadata {
 	};
     }
 
+    if (defined($res->{qm}->{bios}) && $res->{qm}->{bios} eq 'ovmf') {
+	$disks->{efidisk0} = 1;
+	push @$warnings, { type => 'efi-state-lost', key => 'bios', value => 'ovmf' };
+    }
+
     return {
 	type => 'vm',
 	source => $volname,
diff --git a/src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf b/src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
index b93540f..10ccaf1 100755
--- a/src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
+++ b/src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
@@ -137,6 +137,7 @@
       <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
       <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="soft"/>
       <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
+      <vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="efi"/>
     </VirtualHardwareSection>
   </VirtualSystem>
 </Envelope>
diff --git a/src/test/run_ovf_tests.pl b/src/test/run_ovf_tests.pl
index c433c9d..e92258d 100755
--- a/src/test/run_ovf_tests.pl
+++ b/src/test/run_ovf_tests.pl
@@ -72,5 +72,6 @@ is($win10noNs->{qm}->{memory}, '6144', 'win10 VM (no default rasd NS) memory is
 is($win10noNs->{qm}->{cores}, '4', 'win10 VM (no default rasd NS) cores are correct');
 # older esxi/ovf standard used 'other' for windows10
 is($win10noNs->{qm}->{ostype}, 'other', 'win10 VM (no default rasd NS) ostype is correct');
+is($win10noNs->{qm}->{bios}, 'ovmf', 'win10 VM (no default rasd NS) bios is correct');
 
 done_testing();
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 07/12] ovf: implement rudimentary boot order
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (5 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 06/12] ovf: implement parsing out firmware type Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 08/12] ovf: implement parsing nics Dominik Csapak
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

simply add all parsed disks to the boot order in the order we encounter
them (similar to the esxi plugin).

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/GuestImport/OVF.pm | 6 +++++-
 src/test/run_ovf_tests.pl  | 3 +++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
index e050667..11dea73 100644
--- a/src/PVE/GuestImport/OVF.pm
+++ b/src/PVE/GuestImport/OVF.pm
@@ -244,6 +244,8 @@ sub parse_ovf {
     # when all the nodes has been found out, we copy the relevant information to
     # a $pve_disk hash ref, which we push to @disks;
 
+    my $boot_order = [];
+
     foreach my $item_node (@disk_items) {
 
 	my $disk_node;
@@ -349,9 +351,11 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
 	};
 	$pve_disk->{virtual_size} = $virtual_size if defined($virtual_size);
 	push @disks, $pve_disk;
-
+	push @$boot_order, $pve_disk_address;
     }
 
+    $qm->{boot} = "order=" . join(';', @$boot_order) if scalar(@$boot_order) > 0;
+
     return {qm => $qm, disks => \@disks};
 }
 
diff --git a/src/test/run_ovf_tests.pl b/src/test/run_ovf_tests.pl
index e92258d..3b04100 100755
--- a/src/test/run_ovf_tests.pl
+++ b/src/test/run_ovf_tests.pl
@@ -56,17 +56,20 @@ is($win10noNs->{disks}->[0]->{virtual_size}, 2048, 'single disk vm (no default r
 
 print "\ntesting vm.conf extraction\n";
 
+is($win2008->{qm}->{boot}, 'order=scsi0;scsi1', 'win2008 VM boot is correct');
 is($win2008->{qm}->{name}, 'Win2008-R2x64', 'win2008 VM name is correct');
 is($win2008->{qm}->{memory}, '2048', 'win2008 VM memory is correct');
 is($win2008->{qm}->{cores}, '1', 'win2008 VM cores are correct');
 is($win2008->{qm}->{ostype}, 'win7', 'win2008 VM ostype is correcty');
 
+is($win10->{qm}->{boot}, 'order=scsi0', 'win10 VM boot is correct');
 is($win10->{qm}->{name}, 'Win10-Liz', 'win10 VM name is correct');
 is($win10->{qm}->{memory}, '6144', 'win10 VM memory is correct');
 is($win10->{qm}->{cores}, '4', 'win10 VM cores are correct');
 # older esxi/ovf standard used 'other' for windows10
 is($win10->{qm}->{ostype}, 'other', 'win10 VM ostype is correct');
 
+is($win10noNs->{qm}->{boot}, 'order=scsi0', 'win10 VM (no default rasd NS) boot is correct');
 is($win10noNs->{qm}->{name}, 'Win10-Liz', 'win10 VM (no default rasd NS) name is correct');
 is($win10noNs->{qm}->{memory}, '6144', 'win10 VM (no default rasd NS) memory is correct');
 is($win10noNs->{qm}->{cores}, '4', 'win10 VM (no default rasd NS) cores are correct');
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 08/12] ovf: implement parsing nics
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (6 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 07/12] ovf: implement rudimentary boot order Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 09/12] api: allow ova upload/download Dominik Csapak
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

by iterating over the relevant parts and trying to parse out the
'ResourceSubType'. The content of that is not standardized, but I only
ever found examples that are compatible with vmware, meaning it's
either 'e1000', 'e1000e' or 'vmxnet3' (in various capitalizations; thus
the `lc()`)

As a fallback i used e1000, since that is our default too, and should
work for most guest operating systems.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/GuestImport/OVF.pm   | 23 ++++++++++++++++++++++-
 src/PVE/Storage/DirPlugin.pm |  2 +-
 src/test/run_ovf_tests.pl    |  5 +++++
 3 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
index 11dea73..7dc4938 100644
--- a/src/PVE/GuestImport/OVF.pm
+++ b/src/PVE/GuestImport/OVF.pm
@@ -119,6 +119,12 @@ sub get_ostype {
     return $ostype_ids->{$id} // 'other';
 }
 
+my $allowed_nic_models = [
+    'e1000',
+    'e1000e',
+    'vmxnet3',
+];
+
 sub find_by {
     my ($key, $param) = @_;
     foreach my $resource (@resources) {
@@ -356,7 +362,22 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
 
     $qm->{boot} = "order=" . join(';', @$boot_order) if scalar(@$boot_order) > 0;
 
-    return {qm => $qm, disks => \@disks};
+    my $nic_id = dtmf_name_to_id('Ethernet Adapter');
+    my $xpath_find_nics = "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${nic_id}]";
+    my @nic_items = $xpc->findnodes($xpath_find_nics);
+
+    my $net = {};
+
+    my $net_count = 0;
+    for my $item_node (@nic_items) {
+	my $model = $xpc->findvalue('rasd:ResourceSubType', $item_node);
+	$model = lc($model);
+	$model = 'e1000' if ! grep { $_ eq $model } @$allowed_nic_models;
+	$net->{"net${net_count}"} = { model => $model };
+	$net_count++;
+    }
+
+    return {qm => $qm, disks => \@disks, net => $net};
 }
 
 1;
diff --git a/src/PVE/Storage/DirPlugin.pm b/src/PVE/Storage/DirPlugin.pm
index b98b603..6a6b5e9 100644
--- a/src/PVE/Storage/DirPlugin.pm
+++ b/src/PVE/Storage/DirPlugin.pm
@@ -293,7 +293,7 @@ sub get_import_metadata {
 	'create-args' => $res->{qm},
 	'disks' => $disks,
 	warnings => $warnings,
-	net => [],
+	net => $res->{net},
     };
 }
 
diff --git a/src/test/run_ovf_tests.pl b/src/test/run_ovf_tests.pl
index 3b04100..b8fa4b1 100755
--- a/src/test/run_ovf_tests.pl
+++ b/src/test/run_ovf_tests.pl
@@ -54,6 +54,11 @@ is($win10noNs->{disks}->[0]->{disk_address}, 'scsi0', 'single disk vm (no defaul
 is($win10noNs->{disks}->[0]->{backing_file}, "$test_manifests/Win10-Liz-disk1.vmdk", 'single disk vm (no default rasd NS) has the correct disk backing device');
 is($win10noNs->{disks}->[0]->{virtual_size}, 2048, 'single disk vm (no default rasd NS) has the correct size');
 
+print "testing nics\n";
+is($win2008->{net}->{net0}->{model}, 'e1000', 'win2008 has correct nic model');
+is($win10->{net}->{net0}->{model}, 'e1000e', 'win10 has correct nic model');
+is($win10noNs->{net}->{net0}->{model}, 'e1000e', 'win10 (no default rasd NS) has correct nic model');
+
 print "\ntesting vm.conf extraction\n";
 
 is($win2008->{qm}->{boot}, 'order=scsi0;scsi1', 'win2008 VM boot is correct');
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 09/12] api: allow ova upload/download
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (7 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 08/12] ovf: implement parsing nics Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 10/12] plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs Dominik Csapak
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

introducing a separate regex that only contains ova, since
upload/downloading ovfs does not make sense (since the disks are then
missing).

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/API2/Storage/Status.pm | 18 ++++++++++++++----
 src/PVE/Storage.pm             | 11 +++++++++++
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/src/PVE/API2/Storage/Status.pm b/src/PVE/API2/Storage/Status.pm
index acde730..d51a9af 100644
--- a/src/PVE/API2/Storage/Status.pm
+++ b/src/PVE/API2/Storage/Status.pm
@@ -369,7 +369,7 @@ __PACKAGE__->register_method ({
     name => 'upload',
     path => '{storage}/upload',
     method => 'POST',
-    description => "Upload templates and ISO images.",
+    description => "Upload templates, ISO images and OVAs.",
     permissions => {
 	check => ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
     },
@@ -382,7 +382,7 @@ __PACKAGE__->register_method ({
 	    content => {
 		description => "Content type.",
 		type => 'string', format => 'pve-storage-content',
-		enum => ['iso', 'vztmpl'],
+		enum => ['iso', 'vztmpl', 'import'],
 	    },
 	    filename => {
 		description => "The name of the file to create. Caution: This will be normalized!",
@@ -448,6 +448,11 @@ __PACKAGE__->register_method ({
 		raise_param_exc({ filename => "wrong file extension" });
 	    }
 	    $path = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage});
+	} elsif ($content eq 'import') {
+	    if ($filename !~ m!${PVE::Storage::SAFE_CHAR_CLASS_RE}+$PVE::Storage::UPLOAD_IMPORT_EXT_RE_1$!) {
+		raise_param_exc({ filename => "invalid filename or wrong extension" });
+	    }
+	    $path = PVE::Storage::get_import_dir($cfg, $param->{storage});
 	} else {
 	    raise_param_exc({ content => "upload content type '$content' not allowed" });
 	}
@@ -544,7 +549,7 @@ __PACKAGE__->register_method({
     name => 'download_url',
     path => '{storage}/download-url',
     method => 'POST',
-    description => "Download templates and ISO images by using an URL.",
+    description => "Download templates, ISO images and OVAs by using an URL.",
     proxyto => 'node',
     permissions => {
 	description => 'Requires allocation access on the storage and as this allows one to probe'
@@ -572,7 +577,7 @@ __PACKAGE__->register_method({
 	    content => {
 		description => "Content type.", # TODO: could be optional & detected in most cases
 		type => 'string', format => 'pve-storage-content',
-		enum => ['iso', 'vztmpl'],
+		enum => ['iso', 'vztmpl', 'import'],
 	    },
 	    filename => {
 		description => "The name of the file to create. Caution: This will be normalized!",
@@ -642,6 +647,11 @@ __PACKAGE__->register_method({
 		raise_param_exc({ filename => "wrong file extension" });
 	    }
 	    $path = PVE::Storage::get_vztmpl_dir($cfg, $storage);
+	} elsif ($content eq 'import') {
+	    if ($filename !~ m!${PVE::Storage::SAFE_CHAR_CLASS_RE}+$PVE::Storage::UPLOAD_IMPORT_EXT_RE_1$!) {
+		raise_param_exc({ filename => "invalid filename or wrong extension" });
+	    }
+	    $path = PVE::Storage::get_import_dir($cfg, $param->{storage});
 	} else {
 	    raise_param_exc({ content => "upload content-type '$content' is not allowed" });
 	}
diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm
index bd6c4df..7c43701 100755
--- a/src/PVE/Storage.pm
+++ b/src/PVE/Storage.pm
@@ -116,6 +116,8 @@ 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 $SAFE_CHAR_CLASS_RE = qr/[a-zA-Z0-9\-\.\+\=\_]/;
 
 our $OVA_CONTENT_RE_1 = qr/${SAFE_CHAR_CLASS_RE}+\.(qcow2|raw|vmdk)/;
@@ -466,6 +468,15 @@ sub get_iso_dir {
     return $plugin->get_subdir($scfg, 'iso');
 }
 
+sub get_import_dir {
+    my ($cfg, $storeid) = @_;
+
+    my $scfg = storage_config($cfg, $storeid);
+    my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+
+    return $plugin->get_subdir($scfg, 'import');
+}
+
 sub get_vztmpl_dir {
     my ($cfg, $storeid) = @_;
 
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 10/12] plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (8 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 09/12] api: allow ova upload/download Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-06-12 15:57   ` Max Carrara
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 11/12] add 'import' content type to 'check_volume_access' Dominik Csapak
                   ` (15 subsequent siblings)
  25 siblings, 1 reply; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

and reuse the DirPlugin implementation

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/Storage/BTRFSPlugin.pm     | 5 +++++
 src/PVE/Storage/CIFSPlugin.pm      | 6 +++++-
 src/PVE/Storage/CephFSPlugin.pm    | 6 +++++-
 src/PVE/Storage/GlusterfsPlugin.pm | 6 +++++-
 src/PVE/Storage/NFSPlugin.pm       | 6 +++++-
 5 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/src/PVE/Storage/BTRFSPlugin.pm b/src/PVE/Storage/BTRFSPlugin.pm
index 42815cb..b7e3f82 100644
--- a/src/PVE/Storage/BTRFSPlugin.pm
+++ b/src/PVE/Storage/BTRFSPlugin.pm
@@ -40,6 +40,7 @@ sub plugindata {
 		backup => 1,
 		snippets => 1,
 		none => 1,
+		import => 1,
 	    },
 	    { images => 1, rootdir => 1 },
 	],
@@ -930,4 +931,8 @@ sub volume_import {
     return "$storeid:$volname";
 }
 
+sub get_import_metadata {
+    return PVE::Storage::DirPlugin::get_import_metadata(@_);
+}
+
 1
diff --git a/src/PVE/Storage/CIFSPlugin.pm b/src/PVE/Storage/CIFSPlugin.pm
index 2184471..475065a 100644
--- a/src/PVE/Storage/CIFSPlugin.pm
+++ b/src/PVE/Storage/CIFSPlugin.pm
@@ -99,7 +99,7 @@ sub type {
 sub plugindata {
     return {
 	content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1,
-		   backup => 1, snippets => 1}, { images => 1 }],
+		   backup => 1, snippets => 1, import => 1}, { images => 1 }],
 	format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
     };
 }
@@ -314,4 +314,8 @@ sub update_volume_attribute {
     return PVE::Storage::DirPlugin::update_volume_attribute(@_);
 }
 
+sub get_import_metadata {
+    return PVE::Storage::DirPlugin::get_import_metadata(@_);
+}
+
 1;
diff --git a/src/PVE/Storage/CephFSPlugin.pm b/src/PVE/Storage/CephFSPlugin.pm
index 8aad518..36c64ea 100644
--- a/src/PVE/Storage/CephFSPlugin.pm
+++ b/src/PVE/Storage/CephFSPlugin.pm
@@ -116,7 +116,7 @@ sub type {
 
 sub plugindata {
     return {
-	content => [ { vztmpl => 1, iso => 1, backup => 1, snippets => 1},
+	content => [ { vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1 },
 		     { backup => 1 }],
     };
 }
@@ -261,4 +261,8 @@ sub update_volume_attribute {
     return PVE::Storage::DirPlugin::update_volume_attribute(@_);
 }
 
+sub get_import_metadata {
+    return PVE::Storage::DirPlugin::get_import_metadata(@_);
+}
+
 1;
diff --git a/src/PVE/Storage/GlusterfsPlugin.pm b/src/PVE/Storage/GlusterfsPlugin.pm
index 2b7f9e1..9d17180 100644
--- a/src/PVE/Storage/GlusterfsPlugin.pm
+++ b/src/PVE/Storage/GlusterfsPlugin.pm
@@ -97,7 +97,7 @@ sub type {
 
 sub plugindata {
     return {
-	content => [ { images => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1},
+	content => [ { images => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1},
 		     { images => 1 }],
 	format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
     };
@@ -352,4 +352,8 @@ sub check_connection {
     return defined($server) ? 1 : 0;
 }
 
+sub get_import_metadata {
+    return PVE::Storage::DirPlugin::get_import_metadata(@_);
+}
+
 1;
diff --git a/src/PVE/Storage/NFSPlugin.pm b/src/PVE/Storage/NFSPlugin.pm
index f2e4c0d..72e9c6d 100644
--- a/src/PVE/Storage/NFSPlugin.pm
+++ b/src/PVE/Storage/NFSPlugin.pm
@@ -53,7 +53,7 @@ sub type {
 
 sub plugindata {
     return {
-	content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1 },
+	content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1 },
 		     { images => 1 }],
 	format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
     };
@@ -223,4 +223,8 @@ sub update_volume_attribute {
     return PVE::Storage::DirPlugin::update_volume_attribute(@_);
 }
 
+sub get_import_metadata {
+    return PVE::Storage::DirPlugin::get_import_metadata(@_);
+}
+
 1;
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 11/12] add 'import' content type to 'check_volume_access'
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (9 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 10/12] plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 12/12] plugin: file_size_info: don't ignore base path with whitespace Dominik Csapak
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

in the same branch as 'vztmpl' and 'iso'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/Storage.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm
index 7c43701..8b9d21b 100755
--- a/src/PVE/Storage.pm
+++ b/src/PVE/Storage.pm
@@ -542,7 +542,7 @@ sub check_volume_access {
 
 	return if $rpcenv->check($user, "/storage/$sid", ['Datastore.Allocate'], 1);
 
-	if ($vtype eq 'iso' || $vtype eq 'vztmpl') {
+	if ($vtype eq 'iso' || $vtype eq 'vztmpl' || $vtype eq 'import') {
 	    # require at least read access to storage, (custom) templates/ISOs could be sensitive
 	    $rpcenv->check_any($user, "/storage/$sid", ['Datastore.AllocateSpace', 'Datastore.Audit']);
 	} elsif (defined($ownervm) && defined($vmid) && ($ownervm == $vmid)) {
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH storage v4 12/12] plugin: file_size_info: don't ignore base path with whitespace
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (10 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 11/12] add 'import' content type to 'check_volume_access' Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 1/4] api: delete unused OVF.pm Dominik Csapak
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

if the base image (parent) of an image contains whitespace in it's path
(e.g. a space), the current untainting would not match and it would seem
there was no parent.

Fix that by adapting the untaint regex

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PVE/Storage/Plugin.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/PVE/Storage/Plugin.pm b/src/PVE/Storage/Plugin.pm
index 848e053..b317e14 100644
--- a/src/PVE/Storage/Plugin.pm
+++ b/src/PVE/Storage/Plugin.pm
@@ -992,7 +992,7 @@ sub file_size_info {
     ($format) = ($format =~ /^(\S+)$/); # untaint
     die "format '$format' includes whitespace\n" if !defined($format);
     if (defined($parent)) {
-	($parent) = ($parent =~ /^(\S+)$/); # untaint
+	($parent) = ($parent =~ /^(.*)$/); # untaint
     }
     return wantarray ? ($size, $format, $used, $parent, $st->ctime) : $size;
 }
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH qemu-server v4 1/4] api: delete unused OVF.pm
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (11 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 12/12] plugin: file_size_info: don't ignore base path with whitespace Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 2/4] use OVF from Storage Dominik Csapak
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

the api part was never in use by anything

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 PVE/API2/Qemu/Makefile |  2 +-
 PVE/API2/Qemu/OVF.pm   | 53 ------------------------------------------
 2 files changed, 1 insertion(+), 54 deletions(-)
 delete mode 100644 PVE/API2/Qemu/OVF.pm

diff --git a/PVE/API2/Qemu/Makefile b/PVE/API2/Qemu/Makefile
index bdd4762b..5d4abda6 100644
--- a/PVE/API2/Qemu/Makefile
+++ b/PVE/API2/Qemu/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Agent.pm CPU.pm Machine.pm OVF.pm
+SOURCES=Agent.pm CPU.pm Machine.pm
 
 .PHONY: install
 install:
diff --git a/PVE/API2/Qemu/OVF.pm b/PVE/API2/Qemu/OVF.pm
deleted file mode 100644
index cc0ef2da..00000000
--- a/PVE/API2/Qemu/OVF.pm
+++ /dev/null
@@ -1,53 +0,0 @@
-package PVE::API2::Qemu::OVF;
-
-use strict;
-use warnings;
-
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::QemuServer::OVF;
-use PVE::RESTHandler;
-
-use base qw(PVE::RESTHandler);
-
-__PACKAGE__->register_method ({
-    name => 'readovf',
-    path => '',
-    method => 'GET',
-    proxyto => 'node',
-    description => "Read an .ovf manifest.",
-    protected => 1,
-    parameters => {
-	additionalProperties => 0,
-	properties => {
-	    node => get_standard_option('pve-node'),
-	    manifest => {
-		description => "Path to .ovf manifest.",
-		type => 'string',
-	    },
-	},
-    },
-    returns => {
-	type => 'object',
-	additionalProperties => 1,
-	properties => PVE::QemuServer::json_ovf_properties(),
-	description => "VM config according to .ovf manifest.",
-    },
-    code => sub {
-	my ($param) = @_;
-
-	my $manifest = $param->{manifest};
-	die "check for file $manifest failed - $!\n" if !-f $manifest;
-
-	my $parsed = PVE::QemuServer::OVF::parse_ovf($manifest);
-	my $result;
-	$result->{cores} = $parsed->{qm}->{cores};
-	$result->{name} =  $parsed->{qm}->{name};
-	$result->{memory} = $parsed->{qm}->{memory};
-	my $disks = $parsed->{disks};
-	for my $disk (@$disks) {
-	    $result->{$disk->{disk_address}} = $disk->{backing_file};
-	}
-	return $result;
-    }});
-
-1;
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH qemu-server v4 2/4] use OVF from Storage
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (12 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 1/4] api: delete unused OVF.pm Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 3/4] api: create: implement extracting disks when needed for import-from Dominik Csapak
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

and delete it here (incl tests; they live in pve-storage now).

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 PVE/CLI/qm.pm                                 |   4 +-
 PVE/QemuServer/Makefile                       |   1 -
 PVE/QemuServer/OVF.pm                         | 242 ------------------
 debian/control                                |   2 -
 test/Makefile                                 |   5 +-
 test/ovf_manifests/Win10-Liz-disk1.vmdk       | Bin 65536 -> 0 bytes
 test/ovf_manifests/Win10-Liz.ovf              | 142 ----------
 .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 142 ----------
 test/ovf_manifests/Win_2008_R2_two-disks.ovf  | 145 -----------
 test/ovf_manifests/disk1.vmdk                 | Bin 65536 -> 0 bytes
 test/ovf_manifests/disk2.vmdk                 | Bin 65536 -> 0 bytes
 test/run_ovf_tests.pl                         |  71 -----
 12 files changed, 3 insertions(+), 751 deletions(-)
 delete mode 100644 PVE/QemuServer/OVF.pm
 delete mode 100644 test/ovf_manifests/Win10-Liz-disk1.vmdk
 delete mode 100755 test/ovf_manifests/Win10-Liz.ovf
 delete mode 100755 test/ovf_manifests/Win10-Liz_no_default_ns.ovf
 delete mode 100755 test/ovf_manifests/Win_2008_R2_two-disks.ovf
 delete mode 100644 test/ovf_manifests/disk1.vmdk
 delete mode 100644 test/ovf_manifests/disk2.vmdk
 delete mode 100755 test/run_ovf_tests.pl

diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm
index b105830f..2b85d072 100755
--- a/PVE/CLI/qm.pm
+++ b/PVE/CLI/qm.pm
@@ -28,13 +28,13 @@ use PVE::Tools qw(extract_param file_get_contents);
 
 use PVE::API2::Qemu::Agent;
 use PVE::API2::Qemu;
+use PVE::GuestImport::OVF;
 use PVE::QemuConfig;
 use PVE::QemuServer::Drive;
 use PVE::QemuServer::Helpers;
 use PVE::QemuServer::Agent qw(agent_available);
 use PVE::QemuServer::ImportDisk;
 use PVE::QemuServer::Monitor qw(mon_cmd);
-use PVE::QemuServer::OVF;
 use PVE::QemuServer;
 
 use PVE::CLIHandler;
@@ -729,7 +729,7 @@ __PACKAGE__->register_method ({
 	my $storecfg = PVE::Storage::config();
 	PVE::Storage::storage_check_enabled($storecfg, $storeid);
 
-	my $parsed = PVE::QemuServer::OVF::parse_ovf($ovf_file);
+	my $parsed = PVE::GuestImport::OVF::parse_ovf($ovf_file);
 
 	if ($dryrun) {
 	    print to_json($parsed, { pretty => 1, canonical => 1});
diff --git a/PVE/QemuServer/Makefile b/PVE/QemuServer/Makefile
index ac26e56f..89d12091 100644
--- a/PVE/QemuServer/Makefile
+++ b/PVE/QemuServer/Makefile
@@ -2,7 +2,6 @@ SOURCES=PCI.pm		\
 	USB.pm		\
 	Memory.pm	\
 	ImportDisk.pm	\
-	OVF.pm		\
 	Cloudinit.pm	\
 	Agent.pm	\
 	Helpers.pm	\
diff --git a/PVE/QemuServer/OVF.pm b/PVE/QemuServer/OVF.pm
deleted file mode 100644
index b97b0520..00000000
--- a/PVE/QemuServer/OVF.pm
+++ /dev/null
@@ -1,242 +0,0 @@
-# Open Virtualization Format import routines
-# https://www.dmtf.org/standards/ovf
-package PVE::QemuServer::OVF;
-
-use strict;
-use warnings;
-
-use XML::LibXML;
-use File::Spec;
-use File::Basename;
-use Data::Dumper;
-use Cwd 'realpath';
-
-use PVE::Tools;
-use PVE::Storage;
-
-# map OVF resources types to descriptive strings
-# this will allow us to explore the xml tree without using magic numbers
-# http://schemas.dmtf.org/wbem/cim-html/2/CIM_ResourceAllocationSettingData.html
-my @resources = (
-    { id => 1, dtmf_name => 'Other' },
-    { id => 2, dtmf_name => 'Computer System' },
-    { id => 3, dtmf_name => 'Processor' },
-    { id => 4, dtmf_name => 'Memory' },
-    { id => 5, dtmf_name => 'IDE Controller', pve_type => 'ide' },
-    { id => 6, dtmf_name => 'Parallel SCSI HBA', pve_type => 'scsi' },
-    { id => 7, dtmf_name => 'FC HBA' },
-    { id => 8, dtmf_name => 'iSCSI HBA' },
-    { id => 9, dtmf_name => 'IB HCA' },
-    { id => 10, dtmf_name => 'Ethernet Adapter' },
-    { id => 11, dtmf_name => 'Other Network Adapter' },
-    { id => 12, dtmf_name => 'I/O Slot' },
-    { id => 13, dtmf_name => 'I/O Device' },
-    { id => 14, dtmf_name => 'Floppy Drive' },
-    { id => 15, dtmf_name => 'CD Drive' },
-    { id => 16, dtmf_name => 'DVD drive' },
-    { id => 17, dtmf_name => 'Disk Drive' },
-    { id => 18, dtmf_name => 'Tape Drive' },
-    { id => 19, dtmf_name => 'Storage Extent' },
-    { id => 20, dtmf_name => 'Other storage device', pve_type => 'sata'},
-    { id => 21, dtmf_name => 'Serial port' },
-    { id => 22, dtmf_name => 'Parallel port' },
-    { id => 23, dtmf_name => 'USB Controller' },
-    { id => 24, dtmf_name => 'Graphics controller' },
-    { id => 25, dtmf_name => 'IEEE 1394 Controller' },
-    { id => 26, dtmf_name => 'Partitionable Unit' },
-    { id => 27, dtmf_name => 'Base Partitionable Unit' },
-    { id => 28, dtmf_name => 'Power' },
-    { id => 29, dtmf_name => 'Cooling Capacity' },
-    { id => 30, dtmf_name => 'Ethernet Switch Port' },
-    { id => 31, dtmf_name => 'Logical Disk' },
-    { id => 32, dtmf_name => 'Storage Volume' },
-    { id => 33, dtmf_name => 'Ethernet Connection' },
-    { id => 34, dtmf_name => 'DMTF reserved' },
-    { id => 35, dtmf_name => 'Vendor Reserved'}
-);
-
-sub find_by {
-    my ($key, $param) = @_;
-    foreach my $resource (@resources) {
-	if ($resource->{$key} eq $param) {
-	    return ($resource);
-	}
-    }
-    return;
-}
-
-sub dtmf_name_to_id {
-    my ($dtmf_name) = @_;
-    my $found = find_by('dtmf_name', $dtmf_name);
-    if ($found) {
-	return $found->{id};
-    } else {
-	return;
-    }
-}
-
-sub id_to_pve {
-    my ($id) = @_;
-    my $resource = find_by('id', $id);
-    if ($resource) {
-	return $resource->{pve_type};
-    } else {
-	return;
-    }
-}
-
-# returns two references, $qm which holds qm.conf style key/values, and \@disks
-sub parse_ovf {
-    my ($ovf, $debug) = @_;
-
-    my $dom = XML::LibXML->load_xml(location => $ovf, no_blanks => 1);
-
-    # register the xml namespaces in a xpath context object
-    # 'ovf' is the default namespace so it will prepended to each xml element
-    my $xpc = XML::LibXML::XPathContext->new($dom);
-    $xpc->registerNs('ovf', 'http://schemas.dmtf.org/ovf/envelope/1');
-    $xpc->registerNs('rasd', 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData');
-    $xpc->registerNs('vssd', 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData');
-
-
-    # hash to save qm.conf parameters
-    my $qm;
-
-    #array to save a disk list
-    my @disks;
-
-    # easy xpath
-    # walk down the dom until we find the matching XML element
-    my $xpath_find_name = "/ovf:Envelope/ovf:VirtualSystem/ovf:Name";
-    my $ovf_name = $xpc->findvalue($xpath_find_name);
-
-    if ($ovf_name) {
-	# PVE::QemuServer::confdesc requires a valid DNS name
-	($qm->{name} = $ovf_name) =~ s/[^a-zA-Z0-9\-\.]//g;
-    } else {
-	warn "warning: unable to parse the VM name in this OVF manifest, generating a default value\n";
-    }
-
-    # middle level xpath
-    # element[child] search the elements which have this [child]
-    my $processor_id = dtmf_name_to_id('Processor');
-    my $xpath_find_vcpu_count = "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${processor_id}]/rasd:VirtualQuantity";
-    $qm->{'cores'} = $xpc->findvalue($xpath_find_vcpu_count);
-
-    my $memory_id = dtmf_name_to_id('Memory');
-    my $xpath_find_memory = ("/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${memory_id}]/rasd:VirtualQuantity");
-    $qm->{'memory'} = $xpc->findvalue($xpath_find_memory);
-
-    # middle level xpath
-    # here we expect multiple results, so we do not read the element value with
-    # findvalue() but store multiple elements with findnodes()
-    my $disk_id = dtmf_name_to_id('Disk Drive');
-    my $xpath_find_disks="/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType=${disk_id}]";
-    my @disk_items = $xpc->findnodes($xpath_find_disks);
-
-    # disks metadata is split in four different xml elements:
-    # * as an Item node of type DiskDrive in the VirtualHardwareSection
-    # * as an Disk node in the DiskSection
-    # * as a File node in the References section
-    # * each Item node also holds a reference to its owning controller
-    #
-    # we iterate over the list of Item nodes of type disk drive, and for each item,
-    # find the corresponding Disk node, and File node and owning controller
-    # when all the nodes has been found out, we copy the relevant information to
-    # a $pve_disk hash ref, which we push to @disks;
-
-    foreach my $item_node (@disk_items) {
-
-	my $disk_node;
-	my $file_node;
-	my $controller_node;
-	my $pve_disk;
-
-	print "disk item:\n", $item_node->toString(1), "\n" if $debug;
-
-	# from Item, find corresponding Disk node
-	# here the dot means the search should start from the current element in dom
-	my $host_resource = $xpc->findvalue('rasd:HostResource', $item_node);
-	my $disk_section_path;
-	my $disk_id;
-
-	# RFC 3986 "2.3.  Unreserved Characters"
-	my $valid_uripath_chars = qr/[[:alnum:]]|[\-\._~]/;
-
-	if ($host_resource =~ m|^ovf:/(${valid_uripath_chars}+)/(${valid_uripath_chars}+)$|) {
-	    $disk_section_path = $1;
-	    $disk_id = $2;
-	} else {
-	   warn "invalid host ressource $host_resource, skipping\n";
-	   next;
-	}
-	printf "disk section path: $disk_section_path and disk id: $disk_id\n" if $debug;
-
-	# tricky xpath
-	# @ means we filter the result query based on a the value of an item attribute ( @ = attribute)
-	# @ needs to be escaped to prevent Perl double quote interpolation
-	my $xpath_find_fileref = sprintf("/ovf:Envelope/ovf:DiskSection/\
-ovf:Disk[\@ovf:diskId='%s']/\@ovf:fileRef", $disk_id);
-	my $fileref = $xpc->findvalue($xpath_find_fileref);
-
-	my $valid_url_chars = qr@${valid_uripath_chars}|/@;
-	if (!$fileref || $fileref !~ m/^${valid_url_chars}+$/) {
-	    warn "invalid host ressource $host_resource, skipping\n";
-	    next;
-	}
-
-	# from Disk Node, find corresponding filepath
-	my $xpath_find_filepath = sprintf("/ovf:Envelope/ovf:References/ovf:File[\@ovf:id='%s']/\@ovf:href", $fileref);
-	my $filepath = $xpc->findvalue($xpath_find_filepath);
-	if (!$filepath) {
-	    warn "invalid file reference $fileref, skipping\n";
-	    next;
-	}
-	print "file path: $filepath\n" if $debug;
-
-	# from Item, find owning Controller type
-	my $controller_id = $xpc->findvalue('rasd:Parent', $item_node);
-	my $xpath_find_parent_type = sprintf("/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/\
-ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
-	my $controller_type = $xpc->findvalue($xpath_find_parent_type);
-	if (!$controller_type) {
-	    warn "invalid or missing controller: $controller_type, skipping\n";
-	    next;
-	}
-	print "owning controller type: $controller_type\n" if $debug;
-
-	# extract corresponding Controller node details
-	my $adress_on_controller = $xpc->findvalue('rasd:AddressOnParent', $item_node);
-	my $pve_disk_address = id_to_pve($controller_type) . $adress_on_controller;
-
-	# resolve symlinks and relative path components
-	# and die if the diskimage is not somewhere under the $ovf path
-	my $ovf_dir = realpath(dirname(File::Spec->rel2abs($ovf)));
-	my $backing_file_path = realpath(join ('/', $ovf_dir, $filepath));
-	if ($backing_file_path !~ /^\Q${ovf_dir}\E/) {
-	    die "error parsing $filepath, are you using a symlink ?\n";
-	}
-
-	if (!-e $backing_file_path) {
-	    die "error parsing $filepath, file seems not to exist at $backing_file_path\n";
-	}
-
-	($backing_file_path) = $backing_file_path =~ m|^(/.*)|; # untaint
-
-	my $virtual_size = PVE::Storage::file_size_info($backing_file_path);
-	die "error parsing $backing_file_path, cannot determine file size\n"
-	    if !$virtual_size;
-
-	$pve_disk = {
-	    disk_address => $pve_disk_address,
-	    backing_file => $backing_file_path,
-	    virtual_size => $virtual_size
-	};
-	push @disks, $pve_disk;
-
-    }
-
-    return {qm => $qm, disks => \@disks};
-}
-
-1;
diff --git a/debian/control b/debian/control
index 2b5c8e3b..c098ef41 100644
--- a/debian/control
+++ b/debian/control
@@ -13,7 +13,6 @@ Build-Depends: debhelper-compat (= 13),
                libpve-storage-perl (>= 6.1-7),
                libtest-mockmodule-perl,
                libuuid-perl,
-               libxml-libxml-perl,
                lintian,
                perl,
                pkg-config,
@@ -42,7 +41,6 @@ Depends: dbus,
          libpve-storage-perl (>= 7.2-10),
          libterm-readline-gnu-perl,
          libuuid-perl,
-         libxml-libxml-perl,
          perl (>= 5.10.0-19),
          proxmox-websocket-tunnel,
          pve-cluster,
diff --git a/test/Makefile b/test/Makefile
index 9e6d39e8..65ed7bc4 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,14 +1,11 @@
 all: test
 
-test: test_snapshot test_ovf test_cfg_to_cmd test_pci_addr_conflicts test_qemu_img_convert test_migration test_restore_config
+test: test_snapshot test_cfg_to_cmd test_pci_addr_conflicts test_qemu_img_convert test_migration test_restore_config
 
 test_snapshot: run_snapshot_tests.pl
 	./run_snapshot_tests.pl
 	./test_get_replicatable_volumes.pl
 
-test_ovf: run_ovf_tests.pl
-	./run_ovf_tests.pl
-
 test_cfg_to_cmd: run_config2command_tests.pl cfg2cmd/*.conf
 	perl -I../ ./run_config2command_tests.pl
 
diff --git a/test/ovf_manifests/Win10-Liz-disk1.vmdk b/test/ovf_manifests/Win10-Liz-disk1.vmdk
deleted file mode 100644
index 662354a3d1333a2f6c4364005e53bfe7cd8b9044..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 65536
zcmeIvy>HV%7zbeUF`dK)46s<q+^B&Pi6H|eMIb;zP1VdMK8V$P$u?2T)IS|NNta69
zSXw<No$qu%-}$}AUq|21A0<ihr0Gwa-nQ%QGfCR@wmshsN%A;JUhL<u_T%+U7Sd<o
zW^TMU0^M{}R2S(eR@1Ur*Q@eVF^^#r%c@u{hyC#J%V?Nq?*?z)=UG^1Wn9+n(yx6B
z(=ujtJiA)QVP~;guI5EOE2iV-%_??6=%y!^b+aeU_aA6Z4X2azC>{U!a5_FoJCkDB
zKRozW{5{B<Li)YUBEQ&fJe$RRZCRbA$5|CacQiT<A<uvIHbq(g$>yIY=etVNVcI$B
zY@^?CwTN|j)tg?;i)G&AZFqPqoW(5P2K~XUq>9sqVVe!!?y@Y;)^#k~TefEvd2_XU
z^M@5mfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk4^iOdL%ftb
z5g<T-009C72;3>~`p!f^fB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
p0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK;Zui`~%h>XmtPp

diff --git a/test/ovf_manifests/Win10-Liz.ovf b/test/ovf_manifests/Win10-Liz.ovf
deleted file mode 100755
index 46642c04..00000000
--- a/test/ovf_manifests/Win10-Liz.ovf
+++ /dev/null
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--Generated by VMware ovftool 4.1.0 (build-2982904), UTC time: 2017-02-07T13:50:15.265014Z-->
-<Envelope vmw:buildId="build-2982904" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <References>
-    <File ovf:href="Win10-Liz-disk1.vmdk" ovf:id="file1" ovf:size="9155243008"/>
-  </References>
-  <DiskSection>
-    <Info>Virtual disk information</Info>
-    <Disk ovf:capacity="128" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="16798056448"/>
-  </DiskSection>
-  <NetworkSection>
-    <Info>The list of logical networks</Info>
-    <Network ovf:name="bridged">
-      <Description>The bridged network</Description>
-    </Network>
-  </NetworkSection>
-  <VirtualSystem ovf:id="vm">
-    <Info>A virtual machine</Info>
-    <Name>Win10-Liz</Name>
-    <OperatingSystemSection ovf:id="1" vmw:osType="windows9_64Guest">
-      <Info>The kind of installed guest operating system</Info>
-    </OperatingSystemSection>
-    <VirtualHardwareSection>
-      <Info>Virtual hardware requirements</Info>
-      <System>
-        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
-        <vssd:InstanceID>0</vssd:InstanceID>
-        <vssd:VirtualSystemIdentifier>Win10-Liz</vssd:VirtualSystemIdentifier>
-        <vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
-      </System>
-      <Item>
-        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
-        <rasd:Description>Number of Virtual CPUs</rasd:Description>
-        <rasd:ElementName>4 virtual CPU(s)</rasd:ElementName>
-        <rasd:InstanceID>1</rasd:InstanceID>
-        <rasd:ResourceType>3</rasd:ResourceType>
-        <rasd:VirtualQuantity>4</rasd:VirtualQuantity>
-      </Item>
-      <Item>
-        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
-        <rasd:Description>Memory Size</rasd:Description>
-        <rasd:ElementName>6144MB of memory</rasd:ElementName>
-        <rasd:InstanceID>2</rasd:InstanceID>
-        <rasd:ResourceType>4</rasd:ResourceType>
-        <rasd:VirtualQuantity>6144</rasd:VirtualQuantity>
-      </Item>
-      <Item>
-        <rasd:Address>0</rasd:Address>
-        <rasd:Description>SATA Controller</rasd:Description>
-        <rasd:ElementName>sataController0</rasd:ElementName>
-        <rasd:InstanceID>3</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.sata.ahci</rasd:ResourceSubType>
-        <rasd:ResourceType>20</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:Address>0</rasd:Address>
-        <rasd:Description>USB Controller (XHCI)</rasd:Description>
-        <rasd:ElementName>usb3</rasd:ElementName>
-        <rasd:InstanceID>4</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.usb.xhci</rasd:ResourceSubType>
-        <rasd:ResourceType>23</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:Address>0</rasd:Address>
-        <rasd:Description>USB Controller (EHCI)</rasd:Description>
-        <rasd:ElementName>usb</rasd:ElementName>
-        <rasd:InstanceID>5</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.usb.ehci</rasd:ResourceSubType>
-        <rasd:ResourceType>23</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="ehciEnabled" vmw:value="true"/>
-      </Item>
-      <Item>
-        <rasd:Address>0</rasd:Address>
-        <rasd:Description>SCSI Controller</rasd:Description>
-        <rasd:ElementName>scsiController0</rasd:ElementName>
-        <rasd:InstanceID>6</rasd:InstanceID>
-        <rasd:ResourceSubType>lsilogicsas</rasd:ResourceSubType>
-        <rasd:ResourceType>6</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
-        <rasd:ElementName>serial0</rasd:ElementName>
-        <rasd:InstanceID>7</rasd:InstanceID>
-        <rasd:ResourceType>21</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="yieldOnPoll" vmw:value="false"/>
-      </Item>
-      <Item>
-        <rasd:AddressOnParent>0</rasd:AddressOnParent>
-        <rasd:ElementName>disk0</rasd:ElementName>
-        <rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
-        <rasd:InstanceID>8</rasd:InstanceID>
-        <rasd:Parent>6</rasd:Parent>
-        <rasd:ResourceType>17</rasd:ResourceType>
-      </Item>
-      <Item>
-        <rasd:AddressOnParent>2</rasd:AddressOnParent>
-        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
-        <rasd:Connection>bridged</rasd:Connection>
-        <rasd:Description>E1000e ethernet adapter on &quot;bridged&quot;</rasd:Description>
-        <rasd:ElementName>ethernet0</rasd:ElementName>
-        <rasd:InstanceID>9</rasd:InstanceID>
-        <rasd:ResourceSubType>E1000e</rasd:ResourceSubType>
-        <rasd:ResourceType>10</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="false"/>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
-        <rasd:ElementName>sound</rasd:ElementName>
-        <rasd:InstanceID>10</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.soundcard.hdaudio</rasd:ResourceSubType>
-        <rasd:ResourceType>1</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
-        <rasd:ElementName>video</rasd:ElementName>
-        <rasd:InstanceID>11</rasd:InstanceID>
-        <rasd:ResourceType>24</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="true"/>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
-        <rasd:ElementName>vmci</rasd:ElementName>
-        <rasd:InstanceID>12</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
-        <rasd:ResourceType>1</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AddressOnParent>1</rasd:AddressOnParent>
-        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
-        <rasd:ElementName>cdrom0</rasd:ElementName>
-        <rasd:InstanceID>13</rasd:InstanceID>
-        <rasd:Parent>3</rasd:Parent>
-        <rasd:ResourceType>15</rasd:ResourceType>
-      </Item>
-      <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="true"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
-    </VirtualHardwareSection>
-  </VirtualSystem>
-</Envelope>
\ No newline at end of file
diff --git a/test/ovf_manifests/Win10-Liz_no_default_ns.ovf b/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
deleted file mode 100755
index b93540f4..00000000
--- a/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
+++ /dev/null
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--Generated by VMware ovftool 4.1.0 (build-2982904), UTC time: 2017-02-07T13:50:15.265014Z-->
-<Envelope vmw:buildId="build-2982904" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <References>
-    <File ovf:href="Win10-Liz-disk1.vmdk" ovf:id="file1" ovf:size="9155243008"/>
-  </References>
-  <DiskSection>
-    <Info>Virtual disk information</Info>
-    <Disk ovf:capacity="128" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="16798056448"/>
-  </DiskSection>
-  <NetworkSection>
-    <Info>The list of logical networks</Info>
-    <Network ovf:name="bridged">
-      <Description>The bridged network</Description>
-    </Network>
-  </NetworkSection>
-  <VirtualSystem ovf:id="vm">
-    <Info>A virtual machine</Info>
-    <Name>Win10-Liz</Name>
-    <OperatingSystemSection ovf:id="1" vmw:osType="windows9_64Guest">
-      <Info>The kind of installed guest operating system</Info>
-    </OperatingSystemSection>
-    <VirtualHardwareSection>
-      <Info>Virtual hardware requirements</Info>
-      <System>
-        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
-        <vssd:InstanceID>0</vssd:InstanceID>
-        <vssd:VirtualSystemIdentifier>Win10-Liz</vssd:VirtualSystemIdentifier>
-        <vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
-      </System>
-      <Item>
-        <rasd:AllocationUnits xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">hertz * 10^6</rasd:AllocationUnits>
-        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">Number of Virtual CPUs</rasd:Description>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">4 virtual CPU(s)</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">1</rasd:InstanceID>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">3</rasd:ResourceType>
-        <rasd:VirtualQuantity xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">4</rasd:VirtualQuantity>
-      </Item>
-      <Item>
-        <rasd:AllocationUnits xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">byte * 2^20</rasd:AllocationUnits>
-        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">Memory Size</rasd:Description>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6144MB of memory</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">2</rasd:InstanceID>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">4</rasd:ResourceType>
-        <rasd:VirtualQuantity xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6144</rasd:VirtualQuantity>
-      </Item>
-      <Item>
-        <rasd:Address xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">0</rasd:Address>
-        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">SATA Controller</rasd:Description>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">sataController0</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">3</rasd:InstanceID>
-        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.sata.ahci</rasd:ResourceSubType>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">20</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:Address xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">0</rasd:Address>
-        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">USB Controller (XHCI)</rasd:Description>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">usb3</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">4</rasd:InstanceID>
-        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.usb.xhci</rasd:ResourceSubType>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">23</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:Address xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">0</rasd:Address>
-        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">USB Controller (EHCI)</rasd:Description>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">usb</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">5</rasd:InstanceID>
-        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.usb.ehci</rasd:ResourceSubType>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">23</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="ehciEnabled" vmw:value="true"/>
-      </Item>
-      <Item>
-        <rasd:Address xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">0</rasd:Address>
-        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">SCSI Controller</rasd:Description>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">scsiController0</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6</rasd:InstanceID>
-        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">lsilogicsas</rasd:ResourceSubType>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">true</rasd:AutomaticAllocation>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">serial0</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">7</rasd:InstanceID>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">21</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="yieldOnPoll" vmw:value="false"/>
-      </Item>
-      <Item>
-        <rasd:AddressOnParent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" >0</rasd:AddressOnParent>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" >disk0</rasd:ElementName>
-        <rasd:HostResource xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" >ovf:/disk/vmdisk1</rasd:HostResource>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">8</rasd:InstanceID>
-        <rasd:Parent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">6</rasd:Parent>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">17</rasd:ResourceType>
-      </Item>
-      <Item>
-        <rasd:AddressOnParent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">2</rasd:AddressOnParent>
-        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">true</rasd:AutomaticAllocation>
-        <rasd:Connection xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">bridged</rasd:Connection>
-        <rasd:Description xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">E1000e ethernet adapter on &quot;bridged&quot;</rasd:Description>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">ethernet0</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">9</rasd:InstanceID>
-        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">E1000e</rasd:ResourceSubType>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">10</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="false"/>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">false</rasd:AutomaticAllocation>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">sound</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">10</rasd:InstanceID>
-        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.soundcard.hdaudio</rasd:ResourceSubType>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">1</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">false</rasd:AutomaticAllocation>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">video</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">11</rasd:InstanceID>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">24</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="true"/>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">false</rasd:AutomaticAllocation>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmci</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">12</rasd:InstanceID>
-        <rasd:ResourceSubType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">vmware.vmci</rasd:ResourceSubType>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">1</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AddressOnParent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">1</rasd:AddressOnParent>
-        <rasd:AutomaticAllocation xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">false</rasd:AutomaticAllocation>
-        <rasd:ElementName xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">cdrom0</rasd:ElementName>
-        <rasd:InstanceID xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">13</rasd:InstanceID>
-        <rasd:Parent xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">3</rasd:Parent>
-        <rasd:ResourceType xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">15</rasd:ResourceType>
-      </Item>
-      <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="true"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
-    </VirtualHardwareSection>
-  </VirtualSystem>
-</Envelope>
diff --git a/test/ovf_manifests/Win_2008_R2_two-disks.ovf b/test/ovf_manifests/Win_2008_R2_two-disks.ovf
deleted file mode 100755
index a563aabb..00000000
--- a/test/ovf_manifests/Win_2008_R2_two-disks.ovf
+++ /dev/null
@@ -1,145 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--Generated by VMware ovftool 4.1.0 (build-2982904), UTC time: 2017-02-27T15:09:29.768974Z-->
-<Envelope vmw:buildId="build-2982904" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <References>
-    <File ovf:href="disk1.vmdk" ovf:id="file1" ovf:size="3481968640"/>
-    <File ovf:href="disk2.vmdk" ovf:id="file2" ovf:size="68096"/>
-  </References>
-  <DiskSection>
-    <Info>Virtual disk information</Info>
-    <Disk ovf:capacity="40" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="7684882432"/>
-    <Disk ovf:capacity="1" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk2" ovf:fileRef="file2" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="0"/>
-  </DiskSection>
-  <NetworkSection>
-    <Info>The list of logical networks</Info>
-    <Network ovf:name="bridged">
-      <Description>The bridged network</Description>
-    </Network>
-  </NetworkSection>
-  <VirtualSystem ovf:id="vm">
-    <Info>A virtual machine</Info>
-    <Name>Win_2008-R2x64</Name>
-    <OperatingSystemSection ovf:id="103" vmw:osType="windows7Server64Guest">
-      <Info>The kind of installed guest operating system</Info>
-    </OperatingSystemSection>
-    <VirtualHardwareSection>
-      <Info>Virtual hardware requirements</Info>
-      <System>
-        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
-        <vssd:InstanceID>0</vssd:InstanceID>
-        <vssd:VirtualSystemIdentifier>Win_2008-R2x64</vssd:VirtualSystemIdentifier>
-        <vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
-      </System>
-      <Item>
-        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
-        <rasd:Description>Number of Virtual CPUs</rasd:Description>
-        <rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
-        <rasd:InstanceID>1</rasd:InstanceID>
-        <rasd:ResourceType>3</rasd:ResourceType>
-        <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
-      </Item>
-      <Item>
-        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
-        <rasd:Description>Memory Size</rasd:Description>
-        <rasd:ElementName>2048MB of memory</rasd:ElementName>
-        <rasd:InstanceID>2</rasd:InstanceID>
-        <rasd:ResourceType>4</rasd:ResourceType>
-        <rasd:VirtualQuantity>2048</rasd:VirtualQuantity>
-      </Item>
-      <Item>
-        <rasd:Address>0</rasd:Address>
-        <rasd:Description>SATA Controller</rasd:Description>
-        <rasd:ElementName>sataController0</rasd:ElementName>
-        <rasd:InstanceID>3</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.sata.ahci</rasd:ResourceSubType>
-        <rasd:ResourceType>20</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:Address>0</rasd:Address>
-        <rasd:Description>USB Controller (EHCI)</rasd:Description>
-        <rasd:ElementName>usb</rasd:ElementName>
-        <rasd:InstanceID>4</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.usb.ehci</rasd:ResourceSubType>
-        <rasd:ResourceType>23</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="ehciEnabled" vmw:value="true"/>
-      </Item>
-      <Item>
-        <rasd:Address>0</rasd:Address>
-        <rasd:Description>SCSI Controller</rasd:Description>
-        <rasd:ElementName>scsiController0</rasd:ElementName>
-        <rasd:InstanceID>5</rasd:InstanceID>
-        <rasd:ResourceSubType>lsilogicsas</rasd:ResourceSubType>
-        <rasd:ResourceType>6</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
-        <rasd:ElementName>serial0</rasd:ElementName>
-        <rasd:InstanceID>6</rasd:InstanceID>
-        <rasd:ResourceType>21</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="yieldOnPoll" vmw:value="false"/>
-      </Item>
-      <Item>
-        <rasd:AddressOnParent>0</rasd:AddressOnParent>
-        <rasd:ElementName>disk0</rasd:ElementName>
-        <rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
-        <rasd:InstanceID>7</rasd:InstanceID>
-        <rasd:Parent>5</rasd:Parent>
-        <rasd:ResourceType>17</rasd:ResourceType>
-      </Item>
-      <Item>
-        <rasd:AddressOnParent>1</rasd:AddressOnParent>
-        <rasd:ElementName>disk1</rasd:ElementName>
-        <rasd:HostResource>ovf:/disk/vmdisk2</rasd:HostResource>
-        <rasd:InstanceID>8</rasd:InstanceID>
-        <rasd:Parent>5</rasd:Parent>
-        <rasd:ResourceType>17</rasd:ResourceType>
-      </Item>
-      <Item>
-        <rasd:AddressOnParent>2</rasd:AddressOnParent>
-        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
-        <rasd:Connection>bridged</rasd:Connection>
-        <rasd:Description>E1000 ethernet adapter on &quot;bridged&quot;</rasd:Description>
-        <rasd:ElementName>ethernet0</rasd:ElementName>
-        <rasd:InstanceID>9</rasd:InstanceID>
-        <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
-        <rasd:ResourceType>10</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="false"/>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
-        <rasd:ElementName>sound</rasd:ElementName>
-        <rasd:InstanceID>10</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.soundcard.hdaudio</rasd:ResourceSubType>
-        <rasd:ResourceType>1</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
-        <rasd:ElementName>video</rasd:ElementName>
-        <rasd:InstanceID>11</rasd:InstanceID>
-        <rasd:ResourceType>24</rasd:ResourceType>
-        <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="true"/>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
-        <rasd:ElementName>vmci</rasd:ElementName>
-        <rasd:InstanceID>12</rasd:InstanceID>
-        <rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
-        <rasd:ResourceType>1</rasd:ResourceType>
-      </Item>
-      <Item ovf:required="false">
-        <rasd:AddressOnParent>1</rasd:AddressOnParent>
-        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
-        <rasd:ElementName>cdrom0</rasd:ElementName>
-        <rasd:InstanceID>13</rasd:InstanceID>
-        <rasd:Parent>3</rasd:Parent>
-        <rasd:ResourceType>15</rasd:ResourceType>
-      </Item>
-      <vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="true"/>
-      <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="true"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="soft"/>
-      <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
-    </VirtualHardwareSection>
-  </VirtualSystem>
-</Envelope>
diff --git a/test/ovf_manifests/disk1.vmdk b/test/ovf_manifests/disk1.vmdk
deleted file mode 100644
index 8660602343a1a955f9bcf2e6beaed99316dd8167..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 65536
zcmeIvy>HV%7zbeUF`dK)46s<q9ua7|WuUkSgpg2EwX+*viPe0`HWAtSr(-ASlAWQ|
zbJF?F{+-YFKK_yYyn2=-$&0qXY<t)4ch@B8o_Fo_en^t%N%H0}e|H$~AF`0X3J-JR
zqY>z*Sy|tuS*)j3xo%d~*K!`iCRTO1T8@X|%lB-2b2}Q2@{gmi&a1d=x<|K%7N%9q
zn|Qfh$8m45TCV10Gb^W)c4ZxVA@tMpzfJp2S{y#m?iwzx)01@a>+{9rJna?j=ZAyM
zqPW{FznsOxiSi~-&+<BkewLkuP!u<VO<6U6^7*&xtNr=XaoRiS?V{gtwTMl%9Za|L
za#^%_7k)SjXE85!!SM7bspGUQewUqo+Glx@ubWtPwRL-yMO)CL`L7O2fB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pkPg~&a(=JbS1PBlyK!5-N0!ISx
zkM7+PAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
d1PBlyK!5-N0t5&UAV7cs0RjXF5cr=0{{Ra(Wheju

diff --git a/test/ovf_manifests/disk2.vmdk b/test/ovf_manifests/disk2.vmdk
deleted file mode 100644
index c4634513348b392202898374f1c8d2d51d565b27..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 65536
zcmeIvy>HV%7zbeUF`dK)46s<q9#IIDI%J@v2!xPOQ?;`jUy0Rx$u<$$`lr`+(j_}X
ztLLQio&7tX?|uAp{Oj^rk|Zyh{<7(9yX&q=(mrq7>)ntf&y(cMe*SJh-aTX?eH9+&
z#z!O2Psc@dn~q~OEsJ%%D!&!;7&fu2iq&#-6u$l#k8ZBB&%=0f64qH6mv#5(X4k^B
zj9DEow(B_REmq6byr^fzbkeM>VlRY#diJkw-bwTQ2bx{O`BgehC%?a(PtMX_-hBS!
zV6(_?yX6<NxIa-=XX$BH#n2y*PeaJ_>%pcd>%ZCj`_<*{eCa6d4SQYmC$1K;F1Lf}
zc3v#=CU3(J2jMJcc^4cVA0$<rHpO?@@uyvu<=MK9Wm{XjSCKabJ(~aOpacjIAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAn>#W-ahT}R7ZdS0RjXF5Fl_M
z@c!W5Edc@q2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
eAV7cs0RjXF5FkK+009C72oNAZfB=F2DR2*l=VfOA

diff --git a/test/run_ovf_tests.pl b/test/run_ovf_tests.pl
deleted file mode 100755
index ff6c7863..00000000
--- a/test/run_ovf_tests.pl
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-use lib qw(..); # prepend .. to @INC so we use the local version of PVE packages
-
-use FindBin '$Bin';
-use PVE::QemuServer::OVF;
-use Test::More;
-
-use Data::Dumper;
-
-my $test_manifests = join ('/', $Bin, 'ovf_manifests');
-
-print "parsing ovfs\n";
-
-my $win2008 = eval { PVE::QemuServer::OVF::parse_ovf("$test_manifests/Win_2008_R2_two-disks.ovf") };
-if (my $err = $@) {
-    fail('parse win2008');
-    warn("error: $err\n");
-} else {
-    ok('parse win2008');
-}
-my $win10 = eval { PVE::QemuServer::OVF::parse_ovf("$test_manifests/Win10-Liz.ovf") };
-if (my $err = $@) {
-    fail('parse win10');
-    warn("error: $err\n");
-} else {
-    ok('parse win10');
-}
-my $win10noNs = eval { PVE::QemuServer::OVF::parse_ovf("$test_manifests/Win10-Liz_no_default_ns.ovf") };
-if (my $err = $@) {
-    fail("parse win10 no default rasd NS");
-    warn("error: $err\n");
-} else {
-    ok('parse win10 no default rasd NS');
-}
-
-print "testing disks\n";
-
-is($win2008->{disks}->[0]->{disk_address}, 'scsi0', 'multidisk vm has the correct first disk controller');
-is($win2008->{disks}->[0]->{backing_file}, "$test_manifests/disk1.vmdk", 'multidisk vm has the correct first disk backing device');
-is($win2008->{disks}->[0]->{virtual_size}, 2048, 'multidisk vm has the correct first disk size');
-
-is($win2008->{disks}->[1]->{disk_address}, 'scsi1', 'multidisk vm has the correct second disk controller');
-is($win2008->{disks}->[1]->{backing_file}, "$test_manifests/disk2.vmdk", 'multidisk vm has the correct second disk backing device');
-is($win2008->{disks}->[1]->{virtual_size}, 2048, 'multidisk vm has the correct second disk size');
-
-is($win10->{disks}->[0]->{disk_address}, 'scsi0', 'single disk vm has the correct disk controller');
-is($win10->{disks}->[0]->{backing_file}, "$test_manifests/Win10-Liz-disk1.vmdk", 'single disk vm has the correct disk backing device');
-is($win10->{disks}->[0]->{virtual_size}, 2048, 'single disk vm has the correct size');
-
-is($win10noNs->{disks}->[0]->{disk_address}, 'scsi0', 'single disk vm (no default rasd NS) has the correct disk controller');
-is($win10noNs->{disks}->[0]->{backing_file}, "$test_manifests/Win10-Liz-disk1.vmdk", 'single disk vm (no default rasd NS) has the correct disk backing device');
-is($win10noNs->{disks}->[0]->{virtual_size}, 2048, 'single disk vm (no default rasd NS) has the correct size');
-
-print "\ntesting vm.conf extraction\n";
-
-is($win2008->{qm}->{name}, 'Win2008-R2x64', 'win2008 VM name is correct');
-is($win2008->{qm}->{memory}, '2048', 'win2008 VM memory is correct');
-is($win2008->{qm}->{cores}, '1', 'win2008 VM cores are correct');
-
-is($win10->{qm}->{name}, 'Win10-Liz', 'win10 VM name is correct');
-is($win10->{qm}->{memory}, '6144', 'win10 VM memory is correct');
-is($win10->{qm}->{cores}, '4', 'win10 VM cores are correct');
-
-is($win10noNs->{qm}->{name}, 'Win10-Liz', 'win10 VM (no default rasd NS) name is correct');
-is($win10noNs->{qm}->{memory}, '6144', 'win10 VM (no default rasd NS) memory is correct');
-is($win10noNs->{qm}->{cores}, '4', 'win10 VM (no default rasd NS) cores are correct');
-
-done_testing();
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH qemu-server v4 3/4] api: create: implement extracting disks when needed for import-from
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (13 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 2/4] use OVF from Storage Dominik Csapak
@ 2024-05-24 13:21 ` Dominik Csapak
  2024-06-12 16:01   ` Max Carrara
  2024-05-24 13:22 ` [pve-devel] [PATCH qemu-server v4 4/4] api: create: add 'import-extraction-storage' parameter Dominik Csapak
                   ` (10 subsequent siblings)
  25 siblings, 1 reply; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:21 UTC (permalink / raw)
  To: pve-devel

when 'import-from' contains a disk image that needs extraction
(currently only from an 'ova' archive), do that in 'create_disks'
and overwrite the '$source' volid.

Collect the names into a 'delete_sources' list, that we use later
to clean it up again (either when we're finished with importing or in an
error case).

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 PVE/API2/Qemu.pm          | 55 +++++++++++++++++++++++++++++++--------
 PVE/QemuServer.pm         | 12 +++++++++
 PVE/QemuServer/Helpers.pm |  5 ++++
 3 files changed, 61 insertions(+), 11 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 2a1d4d79..8c335ac4 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -24,6 +24,7 @@ use PVE::JSONSchema qw(get_standard_option);
 use PVE::RESTHandler;
 use PVE::ReplicationConfig;
 use PVE::GuestHelpers qw(assert_tag_permissions);
+use PVE::GuestImport;
 use PVE::QemuConfig;
 use PVE::QemuServer;
 use PVE::QemuServer::Cloudinit;
@@ -159,10 +160,19 @@ my $check_storage_access = sub {
 
 	if (my $src_image = $drive->{'import-from'}) {
 	    my $src_vmid;
-	    if (PVE::Storage::parse_volume_id($src_image, 1)) { # PVE-managed volume
-		(my $vtype, undef, $src_vmid) = PVE::Storage::parse_volname($storecfg, $src_image);
-		raise_param_exc({ $ds => "$src_image has wrong type '$vtype' - not an image" })
-		    if $vtype ne 'images';
+	    if (my ($storeid, $volname) = PVE::Storage::parse_volume_id($src_image, 1)) { # PVE-managed volume
+		my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+		my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+		(my $vtype, undef, $src_vmid, undef, undef, undef, my $fmt) = $plugin->parse_volname($volname);
+
+		raise_param_exc({ $ds => "$src_image has wrong type '$vtype' - needs to be 'images' or 'import'" })
+		    if $vtype ne 'images' && $vtype ne 'import';
+
+		if (PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt)) {
+		    raise_param_exc({ $ds => "$src_image is not on an storage with 'images' content type."})
+			if !$scfg->{content}->{images};
+		    $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
+		}
 	    }
 
 	    if ($src_vmid) { # might be actively used by VM and will be copied via clone_disk()
@@ -392,16 +402,34 @@ my sub create_disks : prototype($$$$$$$$$$) {
 		$needs_creation = $live_import;
 
 		if (PVE::Storage::parse_volume_id($source, 1)) { # PVE-managed volume
+		    my ($vtype, undef, undef, undef, undef, undef, $fmt)
+			= PVE::Storage::parse_volname($storecfg, $source);
+		    my $needs_extraction = PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt);
+		    if ($needs_extraction) {
+			print "extracting $source\n";
+			my $extracted_volid
+			     = PVE::GuestImport::extract_disk_from_import_file($source, $vmid);
+			print "finished extracting to $extracted_volid\n";
+			push @$vollist, $extracted_volid;
+			$source = $extracted_volid;
+
+			my (undef, undef, undef, $parent)
+			    = PVE::Storage::volume_size_info($storecfg, $source);
+			die "importing from extracted images with backing file ($parent) not allowed\n"
+			    if $parent;
+		    }
+
 		    if ($live_import && $ds ne 'efidisk0') {
 			my $path = PVE::Storage::path($storecfg, $source)
 			    or die "failed to get a path for '$source'\n";
-			$source = $path;
-			($size, my $source_format) = PVE::Storage::file_size_info($source);
-			die "could not get file size of $source\n" if !$size;
+			($size, my $source_format) = PVE::Storage::file_size_info($path);
+			die "could not get file size of $path\n" if !$size;
 			$live_import_mapping->{$ds} = {
-			    path => $source,
+			    path => $path,
 			    format => $source_format,
 			};
+			$live_import_mapping->{$ds}->{'delete-after-finish'} = $source
+			    if $needs_extraction;
 		    } else {
 			my $dest_info = {
 			    vmid => $vmid,
@@ -413,8 +441,14 @@ my sub create_disks : prototype($$$$$$$$$$) {
 			$dest_info->{efisize} = PVE::QemuServer::get_efivars_size($conf, $disk)
 			    if $ds eq 'efidisk0';
 
-			($dst_volid, $size) = eval {
-			    $import_from_volid->($storecfg, $source, $dest_info, $vollist);
+			eval {
+			    ($dst_volid, $size)
+				= $import_from_volid->($storecfg, $source, $dest_info, $vollist);
+
+			    # remove extracted volumes after importing
+			    PVE::Storage::vdisk_free($storecfg, $source) if $needs_extraction;
+			    print "cleaned up extracted image $source\n";
+			    @$vollist = grep { $_ ne $source } @$vollist;
 			};
 			die "cannot import from '$source' - $@" if $@;
 		    }
@@ -1939,7 +1973,6 @@ my $update_vm_api  = sub {
 
 		    assert_scsi_feature_compatibility($opt, $conf, $storecfg, $param->{$opt})
 			if $opt =~ m/^scsi\d+$/;
-
 		    my (undef, $created_opts) = create_disks(
 			$rpcenv,
 			$authuser,
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 5df0c96d..76136570 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -7310,6 +7310,7 @@ sub live_import_from_files {
     my ($mapping, $vmid, $conf, $restore_options) = @_;
 
     my $live_restore_backing = {};
+    my $sources_to_remove = [];
     for my $dev (keys %$mapping) {
 	die "disk not support for live-restoring: '$dev'\n"
 	    if !is_valid_drivename($dev) || $dev =~ /^(?:efidisk|tpmstate)/;
@@ -7330,6 +7331,9 @@ sub live_import_from_files {
 	    . ",read-only=on"
 	    . ",file.driver=file,file.filename=$path"
 	};
+
+	my $source_volid = $info->{'delete-after-finish'};
+	push $sources_to_remove->@*, $source_volid if defined($source_volid);
     };
 
     my $storecfg = PVE::Storage::config();
@@ -7373,6 +7377,14 @@ sub live_import_from_files {
 
     my $err = $@;
 
+    for my $volid ($sources_to_remove->@*) {
+	eval {
+	    PVE::Storage::vdisk_free($storecfg, $volid);
+	    print "cleaned up extracted image $volid\n";
+	};
+	warn "An error occurred while cleaning up source images: $@\n" if $@;
+    }
+
     if ($err) {
 	warn "An error occurred during live-restore: $err\n";
 	_do_vm_stop($storecfg, $vmid, 1, 1, 10, 0, 1);
diff --git a/PVE/QemuServer/Helpers.pm b/PVE/QemuServer/Helpers.pm
index 0afb6317..15e2496c 100644
--- a/PVE/QemuServer/Helpers.pm
+++ b/PVE/QemuServer/Helpers.pm
@@ -225,4 +225,9 @@ sub windows_version {
     return $winversion;
 }
 
+sub needs_extraction {
+    my ($vtype, $fmt) = @_;
+    return $vtype eq 'import' && $fmt =~ m/^ova\+(.*)$/;
+}
+
 1;
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH qemu-server v4 4/4] api: create: add 'import-extraction-storage' parameter
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (14 preceding siblings ...)
  2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 3/4] api: create: implement extracting disks when needed for import-from Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-06-12 16:01   ` Max Carrara
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 1/9] ui: fix special 'import' icon for non-esxi storages Dominik Csapak
                   ` (9 subsequent siblings)
  25 siblings, 1 reply; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

this is to override the target extraction storage for the option disk
extraction for 'import-from'. This way if the storage does not
supports the content type 'images', one can give an alternative  one.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 PVE/API2/Qemu.pm | 46 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 37 insertions(+), 9 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 8c335ac4..80ea52c5 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -128,7 +128,7 @@ my $check_drive_param = sub {
 };
 
 my $check_storage_access = sub {
-   my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_;
+   my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage, $extraction_storage) = @_;
 
    $foreach_volume_with_alloc->($settings, sub {
 	my ($ds, $drive) = @_;
@@ -169,9 +169,18 @@ my $check_storage_access = sub {
 		    if $vtype ne 'images' && $vtype ne 'import';
 
 		if (PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt)) {
-		    raise_param_exc({ $ds => "$src_image is not on an storage with 'images' content type."})
-			if !$scfg->{content}->{images};
-		    $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
+		    if (defined($extraction_storage)) {
+			my $extraction_scfg = PVE::Storage::storage_config($storecfg, $extraction_storage);
+			raise_param_exc({ 'import-extraction-storage' => "$extraction_storage does not support"
+				." 'images' content type or is not file based."})
+			if !$extraction_scfg->{content}->{images} || !$extraction_scfg->{path};
+			$rpcenv->check($authuser, "/storage/$extraction_storage", ['Datastore.AllocateSpace']);
+		    } else {
+			raise_param_exc({ $ds => "$src_image is not on an storage with 'images'"
+			    ." content type and no 'import-extraction-storage' was given."})
+			    if !$scfg->{content}->{images};
+			$rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
+		    }
 		}
 	    }
 
@@ -326,7 +335,7 @@ my $import_from_volid = sub {
 
 # Note: $pool is only needed when creating a VM, because pool permissions
 # are automatically inherited if VM already exists inside a pool.
-my sub create_disks : prototype($$$$$$$$$$) {
+my sub create_disks : prototype($$$$$$$$$$$) {
     my (
 	$rpcenv,
 	$authuser,
@@ -338,6 +347,7 @@ my sub create_disks : prototype($$$$$$$$$$) {
 	$settings,
 	$default_storage,
 	$is_live_import,
+	$extraction_storage,
     ) = @_;
 
     my $vollist = [];
@@ -407,8 +417,8 @@ my sub create_disks : prototype($$$$$$$$$$) {
 		    my $needs_extraction = PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt);
 		    if ($needs_extraction) {
 			print "extracting $source\n";
-			my $extracted_volid
-			     = PVE::GuestImport::extract_disk_from_import_file($source, $vmid);
+			my $extracted_volid = PVE::GuestImport::extract_disk_from_import_file(
+			    $source, $vmid, $extraction_storage);
 			print "finished extracting to $extracted_volid\n";
 			push @$vollist, $extracted_volid;
 			$source = $extracted_volid;
@@ -941,6 +951,12 @@ __PACKAGE__->register_method({
 		    default => 0,
 		    description => "Start VM after it was created successfully.",
 		},
+		'import-extraction-storage' => get_standard_option('pve-storage-id', {
+		    description => "Storage for temporarily extracted images 'import-from' image"
+			." files (default: import source storage)",
+		    optional => 1,
+		    completion => \&PVE::QemuServer::complete_storage,
+		}),
 	    },
 	    1, # with_disk_alloc
 	),
@@ -967,6 +983,7 @@ __PACKAGE__->register_method({
 	my $storage = extract_param($param, 'storage');
 	my $unique = extract_param($param, 'unique');
 	my $live_restore = extract_param($param, 'live-restore');
+	my $extraction_storage = extract_param($param, 'import-extraction-storage');
 
 	if (defined(my $ssh_keys = $param->{sshkeys})) {
 		$ssh_keys = URI::Escape::uri_unescape($ssh_keys);
@@ -1026,7 +1043,8 @@ __PACKAGE__->register_method({
 	if (scalar(keys $param->%*) > 0) {
 	    &$resolve_cdrom_alias($param);
 
-	    &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
+	    &$check_storage_access(
+		$rpcenv, $authuser, $storecfg, $vmid, $param, $storage, $extraction_storage);
 
 	    &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
 
@@ -1141,6 +1159,7 @@ __PACKAGE__->register_method({
 			$param,
 			$storage,
 			$live_restore,
+			$extraction_storage
 		    );
 		    $conf->{$_} = $created_opts->{$_} for keys $created_opts->%*;
 
@@ -1683,6 +1702,8 @@ my $update_vm_api  = sub {
 
     my $skip_cloud_init = extract_param($param, 'skip_cloud_init');
 
+    my $extraction_storage = extract_param($param, 'import-extraction-storage');
+
     if (defined(my $cipassword = $param->{cipassword})) {
 	# Same logic as in cloud-init (but with the regex fixed...)
 	$param->{cipassword} = PVE::Tools::encrypt_pw($cipassword)
@@ -1802,7 +1823,7 @@ my $update_vm_api  = sub {
 
     &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
 
-    &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param);
+    &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, undef, $extraction_storage);
 
     PVE::QemuServer::check_bridge_access($rpcenv, $authuser, $param);
 
@@ -1984,6 +2005,7 @@ my $update_vm_api  = sub {
 			{$opt => $param->{$opt}},
 			undef,
 			undef,
+			$extraction_storage,
 		    );
 		    $conf->{pending}->{$_} = $created_opts->{$_} for keys $created_opts->%*;
 
@@ -2179,6 +2201,12 @@ __PACKAGE__->register_method({
 		    maximum => 30,
 		    optional => 1,
 		},
+		'import-extraction-storage' => get_standard_option('pve-storage-id', {
+		    description => "Storage for temporarily extracted images 'import-from' image"
+			." files (default: import source storage)",
+		    optional => 1,
+		    completion => \&PVE::QemuServer::complete_storage,
+		}),
 	    },
 	    1, # with_disk_alloc
 	),
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 1/9] ui: fix special 'import' icon for non-esxi storages
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (15 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH qemu-server v4 4/4] api: create: add 'import-extraction-storage' parameter Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text Dominik Csapak
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

we only want to show that icon in the tree when the storage is solely
used for importing, not when it's just one of several content types.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/Utils.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index f5608944..1310b04d 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -1244,7 +1244,7 @@ Ext.define('PVE.Utils', {
 	    // templates
 	    objType = 'template';
 	    status = type;
-	} else if (type === 'storage' && record.content.indexOf('import') !== -1) {
+	} else if (type === 'storage' && record.content === 'import') {
 	    return 'fa fa-cloud-download';
 	} else {
 	    // everything else
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (16 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 1/9] ui: fix special 'import' icon for non-esxi storages Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-06-12 16:02   ` Max Carrara
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 3/9] ui: enable import content type for relevant storages Dominik Csapak
                   ` (7 subsequent siblings)
  25 siblings, 1 reply; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/window/GuestImport.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/www/manager6/window/GuestImport.js b/www/manager6/window/GuestImport.js
index 4bedc211..76ba6dc8 100644
--- a/www/manager6/window/GuestImport.js
+++ b/www/manager6/window/GuestImport.js
@@ -937,6 +937,7 @@ Ext.define('PVE.window.GuestImport', {
 		    gettext('EFI state cannot be imported, you may need to reconfigure the boot order (see {0})'),
 		    '<a href="https://pve.proxmox.com/wiki/OVMF/UEFI_Boot_Entries">OVMF/UEFI Boot Entries</a>',
 		),
+		'ova-needs-extracting': gettext('Importing from an OVA requires extra space while extracting the contained disks into the import or selected storage.'),
 	    };
             let message = warningsCatalogue[w.type];
 	    if (!w.type || !message) {
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 3/9] ui: enable import content type for relevant storages
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (17 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 4/9] ui: enable upload/download/remove buttons for 'import' type storages Dominik Csapak
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/Utils.js                    | 1 +
 www/manager6/form/ContentTypeSelector.js | 2 +-
 www/manager6/storage/CephFSEdit.js       | 2 +-
 www/manager6/storage/GlusterFsEdit.js    | 2 +-
 4 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index 1310b04d..ff2fae25 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -690,6 +690,7 @@ Ext.define('PVE.Utils', {
 	'iso': gettext('ISO image'),
 	'rootdir': gettext('Container'),
 	'snippets': gettext('Snippets'),
+	'import': gettext('Import'),
     },
 
     volume_is_qemu_backup: function(volid, format) {
diff --git a/www/manager6/form/ContentTypeSelector.js b/www/manager6/form/ContentTypeSelector.js
index d0fa0b08..431bd948 100644
--- a/www/manager6/form/ContentTypeSelector.js
+++ b/www/manager6/form/ContentTypeSelector.js
@@ -10,7 +10,7 @@ Ext.define('PVE.form.ContentTypeSelector', {
 	me.comboItems = [];
 
 	if (me.cts === undefined) {
-	    me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir', 'snippets'];
+	    me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir', 'snippets', 'import'];
 	}
 
 	Ext.Array.each(me.cts, function(ct) {
diff --git a/www/manager6/storage/CephFSEdit.js b/www/manager6/storage/CephFSEdit.js
index 6a95a00a..2cdcf7cd 100644
--- a/www/manager6/storage/CephFSEdit.js
+++ b/www/manager6/storage/CephFSEdit.js
@@ -92,7 +92,7 @@ Ext.define('PVE.storage.CephFSInputPanel', {
 	me.column2 = [
 	    {
 		xtype: 'pveContentTypeSelector',
-		cts: ['backup', 'iso', 'vztmpl', 'snippets'],
+		cts: ['backup', 'iso', 'vztmpl', 'snippets', 'import'],
 		fieldLabel: gettext('Content'),
 		name: 'content',
 		value: 'backup',
diff --git a/www/manager6/storage/GlusterFsEdit.js b/www/manager6/storage/GlusterFsEdit.js
index 8155d9c2..df7fe23f 100644
--- a/www/manager6/storage/GlusterFsEdit.js
+++ b/www/manager6/storage/GlusterFsEdit.js
@@ -99,7 +99,7 @@ Ext.define('PVE.storage.GlusterFsInputPanel', {
 	    },
 	    {
 		xtype: 'pveContentTypeSelector',
-		cts: ['images', 'iso', 'backup', 'vztmpl', 'snippets'],
+		cts: ['images', 'iso', 'backup', 'vztmpl', 'snippets', 'import'],
 		name: 'content',
 		value: 'images',
 		multiSelect: true,
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 4/9] ui: enable upload/download/remove buttons for 'import' type storages
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (18 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 3/9] ui: enable import content type for relevant storages Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 5/9] ui: disable 'import' button for non importable formats Dominik Csapak
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

but only for non esxi ones, since that does not allow
uploading/downloading there

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/storage/Browser.js        | 9 +++++++--
 www/manager6/window/UploadToStorage.js | 1 +
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/www/manager6/storage/Browser.js b/www/manager6/storage/Browser.js
index 2123141d..934ce706 100644
--- a/www/manager6/storage/Browser.js
+++ b/www/manager6/storage/Browser.js
@@ -28,7 +28,9 @@ Ext.define('PVE.storage.Browser', {
 	let res = storageInfo.data;
 	let plugin = res.plugintype;
 
-	me.items = plugin !== 'esxi' ? [
+	let isEsxi = plugin === 'esxi';
+
+	me.items = !isEsxi ? [
 	    {
 		title: gettext('Summary'),
 		xtype: 'pveStorageSummary',
@@ -142,8 +144,11 @@ Ext.define('PVE.storage.Browser', {
 		    iconCls: 'fa fa-desktop',
 		    itemId: 'contentImport',
 		    content: 'import',
-		    useCustomRemoveButton: true, // hide default remove button
+		    useCustomRemoveButton: isEsxi, // hide default remove button for esxi
 		    showColumns: ['name', 'format'],
+		    enableUploadButton: enableUpload && !isEsxi,
+		    enableDownloadUrlButton: enableDownloadUrl && !isEsxi,
+		    useUploadButton: !isEsxi,
 		    itemdblclick: (view, record) => createGuestImportWindow(record),
 		    tbar: [
 			{
diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
index 3c5bba88..cdf548a8 100644
--- a/www/manager6/window/UploadToStorage.js
+++ b/www/manager6/window/UploadToStorage.js
@@ -9,6 +9,7 @@ Ext.define('PVE.window.UploadToStorage', {
     title: gettext('Upload'),
 
     acceptedExtensions: {
+	'import': ['.ova'],
 	iso: ['.img', '.iso'],
 	vztmpl: ['.tar.gz', '.tar.xz', '.tar.zst'],
     },
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 5/9] ui: disable 'import' button for non importable formats
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (19 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 4/9] ui: enable upload/download/remove buttons for 'import' type storages Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 6/9] ui: import: improve rendering of volume names Dominik Csapak
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

importable formats are currently ova/ovf/vmx

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/storage/Browser.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/www/manager6/storage/Browser.js b/www/manager6/storage/Browser.js
index 934ce706..822257e7 100644
--- a/www/manager6/storage/Browser.js
+++ b/www/manager6/storage/Browser.js
@@ -124,6 +124,7 @@ Ext.define('PVE.storage.Browser', {
 		});
 	    }
 	    if (contents.includes('import')) {
+		let isImportable = format => ['ova', 'ovf', 'vmx'].indexOf(format) !== -1;
 		let createGuestImportWindow = (selection) => {
 		    if (!selection) {
 			return;
@@ -149,13 +150,18 @@ Ext.define('PVE.storage.Browser', {
 		    enableUploadButton: enableUpload && !isEsxi,
 		    enableDownloadUrlButton: enableDownloadUrl && !isEsxi,
 		    useUploadButton: !isEsxi,
-		    itemdblclick: (view, record) => createGuestImportWindow(record),
+		    itemdblclick: (view, record) => {
+			if (isImportable(record.data.format)) {
+			    createGuestImportWindow(record);
+			}
+		    },
 		    tbar: [
 			{
 			    xtype: 'proxmoxButton',
 			    disabled: true,
 			    text: gettext('Import'),
 			    iconCls: 'fa fa-cloud-download',
+			    enableFn: rec => isImportable(rec.data.format),
 			    handler: function() {
 				let grid = this.up('pveStorageContentView');
 				let selection = grid.getSelection()?.[0];
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 6/9] ui: import: improve rendering of volume names
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (20 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 5/9] ui: disable 'import' button for non importable formats Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 7/9] ui: guest import: add storage selector for ova extraction storage Dominik Csapak
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

in directory storages, we don't need the 'import/' part of the volumes,
as that is implied in dir based storages

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/Utils.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index ff2fae25..ea6e30e8 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -1024,7 +1024,13 @@ Ext.define('PVE.Utils', {
 		Ext.String.leftPad(data.channel, 2, '0') +
 		" ID " + data.id + " LUN " + data.lun;
 	} else if (data.content === 'import') {
-	    result = data.volid.replace(/^.*?:/, '');
+	    if (data.volid.match(/^.*?:import\//)) {
+		// dir-based storages
+		result = data.volid.replace(/^.*?:import\//, '');
+	    } else {
+		// esxi storage
+		result = data.volid.replace(/^.*?:/, '');
+	    }
 	} else {
 	    result = data.volid.replace(/^.*?:(.*?\/)?/, '');
 	}
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 7/9] ui: guest import: add storage selector for ova extraction storage
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (21 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 6/9] ui: import: improve rendering of volume names Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 8/9] ui: guest import: change icon/text for non-esxi import storage Dominik Csapak
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

but only when we detect the 'ova-needs-extraction' warning.
This can be used to select the storage where the disks contained in an
OVA will be extracted to temporarily.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/window/GuestImport.js | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/www/manager6/window/GuestImport.js b/www/manager6/window/GuestImport.js
index 76ba6dc8..972f715b 100644
--- a/www/manager6/window/GuestImport.js
+++ b/www/manager6/window/GuestImport.js
@@ -303,6 +303,7 @@ Ext.define('PVE.window.GuestImport', {
 	    os: 'l26',
 	    maxCdDrives: false,
 	    uniqueMACAdresses: false,
+	    isOva: false,
 	    warnings: [],
 	},
 
@@ -432,6 +433,10 @@ Ext.define('PVE.window.GuestImport', {
 			}
 		    }
 
+		    if (config['import-extraction-storage'] === '') {
+			delete config['import-extraction-storage'];
+		    }
+
 		    return config;
 		},
 
@@ -553,6 +558,22 @@ Ext.define('PVE.window.GuestImport', {
 			allowBlank: false,
 			fieldLabel: gettext('Default Bridge'),
 		    },
+		    {
+			xtype: 'pveStorageSelector',
+			reference: 'extractionStorage',
+			fieldLabel: gettext('Extraction Storage'),
+			storageContent: 'images',
+			emptyText: gettext('Import Storage'),
+			autoSelect: false,
+			name: 'import-extraction-storage',
+			disabled: true,
+			hidden: true,
+			allowBlank: true,
+			bind: {
+			    disabled: '{!isOva}',
+			    hidden: '{!isOva}',
+			},
+		    },
 		],
 
 		columnB: [
@@ -925,6 +946,7 @@ Ext.define('PVE.window.GuestImport', {
 
 	me.lookup('defaultStorage').setNodename(me.nodename);
 	me.lookup('defaultBridge').setNodename(me.nodename);
+	me.lookup('extractionStorage').setNodename(me.nodename);
 
 	let renderWarning = w => {
 	    const warningsCatalogue = {
@@ -1006,6 +1028,7 @@ Ext.define('PVE.window.GuestImport', {
 		}
 
 		me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w)));
+		me.getViewModel().set('isOva', data.warnings.map(w => w.type).indexOf('ova-needs-extracting') !== -1);
 
 		let osinfo = PVE.Utils.get_kvm_osinfo(me.vmConfig.ostype ?? '');
 		let prepareForVirtIO = (me.vmConfig.ostype ?? '').startsWith('w') && (me.vmConfig.bios ?? '').indexOf('ovmf') !== -1;
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 8/9] ui: guest import: change icon/text for non-esxi import storage
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (22 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 7/9] ui: guest import: add storage selector for ova extraction storage Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 9/9] ui: import: show size for dir-based storages Dominik Csapak
  2024-06-12 15:56 ` [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Max Carrara
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

since 'virtual guests' only make sense for a hypervisor, not e.g. a
directory for OVAs

also change the icon from 'desktop' to 'cloud-download' in the
non-esxi case

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/storage/Browser.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/www/manager6/storage/Browser.js b/www/manager6/storage/Browser.js
index 822257e7..763abc70 100644
--- a/www/manager6/storage/Browser.js
+++ b/www/manager6/storage/Browser.js
@@ -141,8 +141,10 @@ Ext.define('PVE.storage.Browser', {
 		};
 		me.items.push({
 		    xtype: 'pveStorageContentView',
-		    title: gettext('Virtual Guests'),
-		    iconCls: 'fa fa-desktop',
+		    // each gettext needs to be in a separate line
+		    title: isEsxi ? gettext('Virtual Guests')
+			: gettext('Import'),
+		    iconCls: isEsxi ? 'fa fa-desktop' : 'fa fa-cloud-download',
 		    itemId: 'contentImport',
 		    content: 'import',
 		    useCustomRemoveButton: isEsxi, // hide default remove button for esxi
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [pve-devel] [PATCH manager v4 9/9] ui: import: show size for dir-based storages
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (23 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 8/9] ui: guest import: change icon/text for non-esxi import storage Dominik Csapak
@ 2024-05-24 13:22 ` Dominik Csapak
  2024-06-12 15:56 ` [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Max Carrara
  25 siblings, 0 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-05-24 13:22 UTC (permalink / raw)
  To: pve-devel

since there we already have the size information

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/storage/Browser.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www/manager6/storage/Browser.js b/www/manager6/storage/Browser.js
index 763abc70..c0b66acc 100644
--- a/www/manager6/storage/Browser.js
+++ b/www/manager6/storage/Browser.js
@@ -148,7 +148,7 @@ Ext.define('PVE.storage.Browser', {
 		    itemId: 'contentImport',
 		    content: 'import',
 		    useCustomRemoveButton: isEsxi, // hide default remove button for esxi
-		    showColumns: ['name', 'format'],
+		    showColumns: isEsxi ? ['name', 'format'] : ['name', 'size', 'format'],
 		    enableUploadButton: enableUpload && !isEsxi,
 		    enableDownloadUrlButton: enableDownloadUrl && !isEsxi,
 		    useUploadButton: !isEsxi,
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages
  2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
                   ` (24 preceding siblings ...)
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 9/9] ui: import: show size for dir-based storages Dominik Csapak
@ 2024-06-12 15:56 ` Max Carrara
  2024-06-13 10:52   ` Dominik Csapak
  25 siblings, 1 reply; 39+ messages in thread
From: Max Carrara @ 2024-06-12 15:56 UTC (permalink / raw)
  To: Proxmox VE development discussion

On Fri May 24, 2024 at 3:21 PM CEST, Dominik Csapak wrote:
> This series enables importing ova/ovf from directory based storages,
> inclusive upload/download via the webui (ova only).
>
> It also improves the ovf importer by parsing the ostype, nics, bootorder
> (and firmware from vmware exported files).

Really great work! Built the packages and ran some tests. There are also
a couple comments, so see below.

Building
--------

* Patches applied cleanly
* Had to build the `qemu-server` package on a development VM and build
  and install `pve-storage` with the patches installed there first,
  because the new `PVE::GuestImport` module couldn't be found
  (obviously). Worked fine though.

Testing
-------

* Installed all packages on a development VM, encountered no issues

* Checked if new `import` content type shows up only for directory-esque 
  storage types
  --> sure does

* Added `import` as content type to my "local" storage
* Checked if "local" storage has new "import" tab
  --> it does

* Exported a Debian VM from ESXi and tarballed the .ovf, .mf and .vmdk
  to an .ova file
* Uploaded the file to the storage
  --> succeeded

* Tried to import the VM onto my local lvm thin-pool storage
  --> fails, because my "local" storage doesn't have the "Disk Image"
      content type

I hadn't expected this to be honest, because I'm not trying to import it
to "local" here?

* Created a new storage with type "directory" and set its content types
  to "import" and "disk image"
* Tried to import the VM again, but this time selected my LVM thin pool
  as "Extraction Storage" in the dialogue window
  --> also fails as expected, because an LVM thin pool is not file-based

A note on the above test: I'd prefer if the error message here was more
fine grained - the popup said:
> import-extraction-storage: local-lvm does not support 'images' content
> type or is not file based.

IMO it would probably be best to not display these storages at all if
they're not supported as an extraction target, but I'm not sure if that
can actually be done (conveniently) atm. Not a blocker or anything
though.

FYI, even though my development has multiple storage types, only the
following are shown in that drop down list:
  - zfs
  - lvm-thin
  - directory (the new one, but not "local")

The only storages with the new "import" content type are the local one
and the new one I made for testing above, so I'm not sure why the zfs
pool and lvm thin pool are showing up there.

* Attempted another import, this time just keeping the "Extraction
  Storage" as is
  --> Worked quite fast, the VM was online really quick because I had
      live import on and worked without any issues

* Attempted another import, but this time from the "local" storage again
  as I did earlier, but I set the "Extraction Storage" to the new
  directory storage
  --> This time importing from "local" worked, somewhat surprisingly

* On the new directory storage, set the content type to "import" only
  --> The icon changed to the little cloud with the arrow, cute

* Attempted yet another import, this time from "local" again and setting
  the new directory storage as "Extraction Storage"
  --> fails unexpectedly with:
  > scsi0: test-dir-storage:import/debian-esxi.ova/debian-esxi-0.vmdk is
  > not on an storage with 'images' content type and no
  > 'import-extraction-storage' was given."

So, it's now pretty clear to me that the `disks` content type plays a
role in terms of whether something can actually be extracted or not
here. Is there perhaps any way the extraction can be "decoupled" from
the content type or be done in a different manner? If this is currently
a limitation (e.g. because of the storage API) then I'd say it's an
*okay* restriction to have at the moment, but it still feels a little
unintuitive from a user's perspective.

Code Review
-----------

The patches are rather easy to follow and logically structured, so
that's very nice. There are more comments inline.

The only other thing I'm not sure about is whether we'd actually want
the `PVE::GuestImport` module (or namespace) -- is there anything we'd
like to import in the future? Maybe something like `PVE::Import::Guest`
would be better, even if there's nothing in `PVE::Import` for the time
being. Just so that we don't have to touch the module structure again
soon.

Conclusion
----------

Very solid work, this is pretty great! This will be a lovely feature for
our users. Apart from the couple little things I encountered there's
nothing to complain about.

To sum all of the above up, the only suggestions I have are as follows:
* I think the error handling regarding the "Extraction Storage" should
  be more fine-grained
* The `disks` content type shouldn't determine whether an .ova file is
  able to be extracted or not, I think there should be some other
  mechanism for that
* Maybe use `PVE::Import::Guest` instead of `PVE::GuestImport`

>
> I opted to move the OVF.pm to pve-storage, since there is no
> real other place where we could put it. I put it in a new module
> 'GuestImport'
>
> We now extract the images into either a given target storage or in the
> import storage in the 'images' dir so accidentally left over images
> are discoverable by the ui/cli.
>
> changes from v3:
> * fixed dependencies in control file
> * removed unnecessary use statements
> * removed unnecessary remove helper
> * moved 'needs_extract' helper to qemu-server
> * removed import storage param from PUT call
> * check down/uploaded ova filename more strictly (same as listing)
> * improved filepath checking in ovf
> * forbid importing when extracted image references a base/backing file
> * instead of trying to manually create a proper filename, use 'alloc' to
>   create a small (1M) file with the same format and overwrite it with
>   renaming. this also solves the cluster locking issue
> * prefer using PVE::Storage functions instead of plugin methods in
>   ova extraction code
> * use $vollist for cleaning up extracted images in qemu-server and
>   add manual cleanup for the success case
>
> changes from v2:
> * use better 'format' values for embedded images (e.g. ova+vmdk)
> * use this format to decide if images should be extracted
> * consistent use of the 'safe character' classes when listing
>   and parsing
> * also list vmdk/qcow2/raw images in content listing
>   (this will be useful when we have a gui for the 'import-from'
>   in the wizard/disk edit for vms)
> * a few gui adaptions
>
>
> changes from v1:
> * move ovf code to GuestImport
> * move extract/checking code to GuestImport
> * don't return 'image' types from import volumes
> * use allow 'safe' characters for filenames of ova/ovfs and inside
> * check for non-regular files (e.g. symlinks) after extraction
> * add new 'import-extraction-storage' for import
> * rename panel in gui for directory storages
> * typo fixes
> * and probably more, see the individual patches for details
>
> pve-storage:
>
> Dominik Csapak (12):
>   copy OVF.pm from qemu-server
>   plugin: dir: implement import content type
>   plugin: dir: handle ova files for import
>   ovf: improve and simplify path checking code
>   ovf: implement parsing the ostype
>   ovf: implement parsing out firmware type
>   ovf: implement rudimentary boot order
>   ovf: implement parsing nics
>   api: allow ova upload/download
>   plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs
>   add 'import' content type to 'check_volume_access'
>   plugin: file_size_info: don't ignore base path with whitespace
>
>  debian/control                                |   2 +
>  src/PVE/API2/Storage/Status.pm                |  19 +-
>  src/PVE/GuestImport.pm                        |  77 ++++
>  src/PVE/GuestImport/Makefile                  |   3 +
>  src/PVE/GuestImport/OVF.pm                    | 383 ++++++++++++++++++
>  src/PVE/Makefile                              |   2 +
>  src/PVE/Storage.pm                            |  23 +-
>  src/PVE/Storage/BTRFSPlugin.pm                |   5 +
>  src/PVE/Storage/CIFSPlugin.pm                 |   6 +-
>  src/PVE/Storage/CephFSPlugin.pm               |   6 +-
>  src/PVE/Storage/DirPlugin.pm                  |  52 ++-
>  src/PVE/Storage/GlusterfsPlugin.pm            |   6 +-
>  src/PVE/Storage/Makefile                      |   1 +
>  src/PVE/Storage/NFSPlugin.pm                  |   6 +-
>  src/PVE/Storage/Plugin.pm                     |  17 +-
>  src/test/Makefile                             |   5 +-
>  src/test/ovf_manifests/Win10-Liz-disk1.vmdk   | Bin 0 -> 65536 bytes
>  src/test/ovf_manifests/Win10-Liz.ovf          | 142 +++++++
>  .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 143 +++++++
>  .../ovf_manifests/Win_2008_R2_two-disks.ovf   | 145 +++++++
>  src/test/ovf_manifests/disk1.vmdk             | Bin 0 -> 65536 bytes
>  src/test/ovf_manifests/disk2.vmdk             | Bin 0 -> 65536 bytes
>  src/test/parse_volname_test.pm                |  33 ++
>  src/test/path_to_volume_id_test.pm            |  21 +
>  src/test/run_ovf_tests.pl                     |  85 ++++
>  25 files changed, 1169 insertions(+), 13 deletions(-)
>  create mode 100644 src/PVE/GuestImport.pm
>  create mode 100644 src/PVE/GuestImport/Makefile
>  create mode 100644 src/PVE/GuestImport/OVF.pm
>  create mode 100644 src/test/ovf_manifests/Win10-Liz-disk1.vmdk
>  create mode 100755 src/test/ovf_manifests/Win10-Liz.ovf
>  create mode 100755 src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
>  create mode 100755 src/test/ovf_manifests/Win_2008_R2_two-disks.ovf
>  create mode 100644 src/test/ovf_manifests/disk1.vmdk
>  create mode 100644 src/test/ovf_manifests/disk2.vmdk
>  create mode 100755 src/test/run_ovf_tests.pl
>
> qemu-server:
>
> Dominik Csapak (4):
>   api: delete unused OVF.pm
>   use OVF from Storage
>   api: create: implement extracting disks when needed for import-from
>   api: create: add 'import-extraction-storage' parameter
>
>  PVE/API2/Qemu.pm                              |  91 +++++--
>  PVE/API2/Qemu/Makefile                        |   2 +-
>  PVE/API2/Qemu/OVF.pm                          |  53 ----
>  PVE/CLI/qm.pm                                 |   4 +-
>  PVE/QemuServer.pm                             |  12 +
>  PVE/QemuServer/Helpers.pm                     |   5 +
>  PVE/QemuServer/Makefile                       |   1 -
>  PVE/QemuServer/OVF.pm                         | 242 ------------------
>  debian/control                                |   2 -
>  test/Makefile                                 |   5 +-
>  test/ovf_manifests/Win10-Liz-disk1.vmdk       | Bin 65536 -> 0 bytes
>  test/ovf_manifests/Win10-Liz.ovf              | 142 ----------
>  .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 142 ----------
>  test/ovf_manifests/Win_2008_R2_two-disks.ovf  | 145 -----------
>  test/ovf_manifests/disk1.vmdk                 | Bin 65536 -> 0 bytes
>  test/ovf_manifests/disk2.vmdk                 | Bin 65536 -> 0 bytes
>  test/run_ovf_tests.pl                         |  71 -----
>  17 files changed, 97 insertions(+), 820 deletions(-)
>  delete mode 100644 PVE/API2/Qemu/OVF.pm
>  delete mode 100644 PVE/QemuServer/OVF.pm
>  delete mode 100644 test/ovf_manifests/Win10-Liz-disk1.vmdk
>  delete mode 100755 test/ovf_manifests/Win10-Liz.ovf
>  delete mode 100755 test/ovf_manifests/Win10-Liz_no_default_ns.ovf
>  delete mode 100755 test/ovf_manifests/Win_2008_R2_two-disks.ovf
>  delete mode 100644 test/ovf_manifests/disk1.vmdk
>  delete mode 100644 test/ovf_manifests/disk2.vmdk
>  delete mode 100755 test/run_ovf_tests.pl
>
> pve-manager:
>
> Dominik Csapak (9):
>   ui: fix special 'import' icon for non-esxi storages
>   ui: guest import: add ova-needs-extracting warning text
>   ui: enable import content type for relevant storages
>   ui: enable upload/download/remove buttons for 'import' type storages
>   ui: disable 'import' button for non importable formats
>   ui: import: improve rendering of volume names
>   ui: guest import: add storage selector for ova extraction storage
>   ui: guest import: change icon/text for non-esxi import storage
>   ui: import: show size for dir-based storages
>
>  www/manager6/Utils.js                    | 11 +++++++++--
>  www/manager6/form/ContentTypeSelector.js |  2 +-
>  www/manager6/storage/Browser.js          | 25 ++++++++++++++++++------
>  www/manager6/storage/CephFSEdit.js       |  2 +-
>  www/manager6/storage/GlusterFsEdit.js    |  2 +-
>  www/manager6/window/GuestImport.js       | 24 +++++++++++++++++++++++
>  www/manager6/window/UploadToStorage.js   |  1 +
>  7 files changed, 56 insertions(+), 11 deletions(-)



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH storage v4 03/12] plugin: dir: handle ova files for import
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 03/12] plugin: dir: handle ova files for import Dominik Csapak
@ 2024-06-12 15:56   ` Max Carrara
  0 siblings, 0 replies; 39+ messages in thread
From: Max Carrara @ 2024-06-12 15:56 UTC (permalink / raw)
  To: Proxmox VE development discussion

On Fri May 24, 2024 at 3:21 PM CEST, Dominik Csapak wrote:
> since we want to handle ova files (which are only ovf+images bundled in
> a tar file) for import, add code that handles that.
>
> we introduce a valid volname for files contained in ovas like this:
>
>  storage:import/archive.ova/disk-1.vmdk
>
> by basically treating the last part of the path as the name for the
> contained disk we want.
>
> in that case we return 'import' as type with 'vmdk/qcow2/raw' as format
> (we cannot use something like 'ova+vmdk' without extending the 'format'
> parsing to that for all storages/formats. This is because it runs
> though a verify format check at least once)
>
> we then provide 3 functions to use for that:
>
> * copy_needs_extraction: determines from the given volid (like above) if
>   that needs extraction to copy it, currently only 'import' vtype + a
>   volid with the above format returns true
>
> * extract_disk_from_import_file: this actually extracts the file from
>   the archive. Currently only ova is supported, so the extraction with
>   'tar' is hardcoded, but again we can easily extend/modify that should
>   we need to.
>
>   we currently extract into the either the import storage or a given
>   target storage in the images directory so if the cleanup does not
>   happen, the user can still see and interact with the image via
>   api/cli/gui
>
> * cleanup_extracted_image: intended to cleanup the extracted images from
>   above
>
> we have to modify the `parse_ovf` a bit to handle the missing disk
> images, and we parse the size out of the ovf part (since this is
> informal only, it should be no problem if we cannot parse it sometimes)
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  src/PVE/API2/Storage/Status.pm     |  1 +
>  src/PVE/GuestImport.pm             | 77 ++++++++++++++++++++++++++++++
>  src/PVE/GuestImport/OVF.pm         | 52 +++++++++++++++++---
>  src/PVE/Makefile                   |  1 +
>  src/PVE/Storage.pm                 |  4 +-
>  src/PVE/Storage/DirPlugin.pm       | 15 +++++-
>  src/PVE/Storage/Plugin.pm          |  4 ++
>  src/test/parse_volname_test.pm     | 20 ++++++++
>  src/test/path_to_volume_id_test.pm |  8 ++++
>  9 files changed, 173 insertions(+), 9 deletions(-)
>  create mode 100644 src/PVE/GuestImport.pm
>
> diff --git a/src/PVE/API2/Storage/Status.pm b/src/PVE/API2/Storage/Status.pm
> index dc6cc69..acde730 100644
> --- a/src/PVE/API2/Storage/Status.pm
> +++ b/src/PVE/API2/Storage/Status.pm
> @@ -749,6 +749,7 @@ __PACKAGE__->register_method({
>  				'efi-state-lost',
>  				'guest-is-running',
>  				'nvme-unsupported',
> +				'ova-needs-extracting',
>  				'ovmf-with-lsi-unsupported',
>  				'serial-port-socket-only',
>  			    ],
> diff --git a/src/PVE/GuestImport.pm b/src/PVE/GuestImport.pm
> new file mode 100644
> index 0000000..988d1f6
> --- /dev/null
> +++ b/src/PVE/GuestImport.pm
> @@ -0,0 +1,77 @@
> +package PVE::GuestImport;
> +
> +use strict;
> +use warnings;
> +
> +use File::Path;
> +
> +use PVE::Storage;
> +use PVE::Tools qw(run_command);
> +
> +sub extract_disk_from_import_file {
> +    my ($volid, $vmid, $target_storeid) = @_;
> +
> +    my ($source_storeid, $volname) = PVE::Storage::parse_volume_id($volid);
> +    $target_storeid //= $source_storeid;
> +    my $cfg = PVE::Storage::config();
> +
> +    my ($vtype, $name, undef, undef, undef, undef, $fmt) =
> +	PVE::Storage::parse_volname($cfg, $volid);
> +
> +    die "only files with content type 'import' can be extracted\n"
> +	if $vtype ne 'import' || $fmt !~ m/^ova\+/;
> +
> +    # extract the inner file from the name
> +    my $archive_volid;
> +    my $inner_file;
> +    my $inner_fmt;
> +    if ($name =~ m!^(.*\.ova)/(${PVE::Storage::SAFE_CHAR_CLASS_RE}+)$!) {
> +	$archive_volid = "$source_storeid:import/$1";
> +	$inner_file = $2;
> +	($inner_fmt) = $fmt =~ /^ova\+(.*)$/;
> +    } else {
> +	die "cannot extract $volid - invalid volname $volname\n";
> +    }
> +
> +    my $ova_path = PVE::Storage::path($cfg, $archive_volid);
> +
> +    my $tmpdir = PVE::Storage::get_image_dir($cfg, $target_storeid, $vmid);
> +    my $pid = $$;
> +    $tmpdir .= "/tmp_${pid}_${vmid}";
> +    mkpath $tmpdir;
> +
> +    ($ova_path) = $ova_path =~ m|^(.*)$|; # untaint
> +
> +    my $source_path = "$tmpdir/$inner_file";
> +    my $target_path;
> +    my $target_volid;
> +    eval {
> +	run_command(['tar', '-x', '--force-local', '-C', $tmpdir, '-f', $ova_path, $inner_file]);
> +
> +	# check for symlinks and other non regular files
> +	if (-l $source_path || ! -f $source_path) {
> +	    die "only regular files are allowed\n";
> +	}
> +
> +	# TODO check for base images in file
> +
> +	# create temporary 1M image that will get overwritten by the rename
> +	# to reserve the filename and take care of locking
> +	$target_volid = PVE::Storage::vdisk_alloc($cfg, $target_storeid, $vmid, $inner_fmt, undef, 1024);
> +	$target_path = PVE::Storage::path($cfg, $target_volid);
> +
> +	print "renaming $source_path to $target_path\n";
> +
> +	rename($source_path, $target_path) or die "unable to move - $!\n";
> +    };
> +    if (my $err = $@) {
> +	File::Path::remove_tree($tmpdir);
> +	die "error during extraction: $err\n";
> +    }
> +
> +    File::Path::remove_tree($tmpdir);
> +
> +    return $target_volid;
> +}
> +
> +1;
> diff --git a/src/PVE/GuestImport/OVF.pm b/src/PVE/GuestImport/OVF.pm
> index 899cfdc..0728684 100644
> --- a/src/PVE/GuestImport/OVF.pm
> +++ b/src/PVE/GuestImport/OVF.pm
> @@ -84,11 +84,37 @@ sub id_to_pve {
>      }
>  }
>  
> +# technically defined in DSP0004 (https://www.dmtf.org/dsp/DSP0004) as an ABNF
> +# but realistically this always takes the form of 'byte * base^exponent'
> +sub try_parse_capacity_unit {
> +    my ($unit_text) = @_;
> +
> +    if ($unit_text =~ m/^\s*byte\s*\*\s*([0-9]+)\s*\^\s*([0-9]+)\s*$/) {
> +	my $base = $1;
> +	my $exp = $2;
> +	return $base ** $exp;
> +    }
> +
> +    return undef;
> +}
> +
>  # returns two references, $qm which holds qm.conf style key/values, and \@disks
>  sub parse_ovf {
> -    my ($ovf, $debug) = @_;
> +    my ($ovf, $isOva, $debug) = @_;
> +
> +    # we have to ignore missing disk images for ova
> +    my $dom;
> +    if ($isOva) {
> +	my $raw = "";
> +	PVE::Tools::run_command(['tar', '-xO', '--wildcards', '--occurrence=1', '-f', $ovf, '*.ovf'], outfunc => sub {
> +	    my $line = shift;
> +	    $raw .= $line;
> +	})

Style: Perhaps this could be something like this:

	PVE::Tools::run_command(
	    ['tar', '-xO', '--wildcards', '--occurrence=1', '-f', $ovf, '*.ovf'],
	    outfunc => sub {
		my $line = shift;
		$raw .= $line;
	    },
	)

> +	$dom = XML::LibXML->load_xml(string => $raw, no_blanks => 1);
> +    } else {
> +	$dom = XML::LibXML->load_xml(location => $ovf, no_blanks => 1);
> +    }
>  
> -    my $dom = XML::LibXML->load_xml(location => $ovf, no_blanks => 1);
>  
>      # register the xml namespaces in a xpath context object
>      # 'ovf' is the default namespace so it will prepended to each xml element
> @@ -176,7 +202,17 @@ sub parse_ovf {
>  	# @ needs to be escaped to prevent Perl double quote interpolation
>  	my $xpath_find_fileref = sprintf("/ovf:Envelope/ovf:DiskSection/\
>  ovf:Disk[\@ovf:diskId='%s']/\@ovf:fileRef", $disk_id);
> +	my $xpath_find_capacity = sprintf("/ovf:Envelope/ovf:DiskSection/\
> +ovf:Disk[\@ovf:diskId='%s']/\@ovf:capacity", $disk_id);
> +	my $xpath_find_capacity_unit = sprintf("/ovf:Envelope/ovf:DiskSection/\
> +ovf:Disk[\@ovf:diskId='%s']/\@ovf:capacityAllocationUnits", $disk_id);
>  	my $fileref = $xpc->findvalue($xpath_find_fileref);
> +	my $capacity = $xpc->findvalue($xpath_find_capacity);
> +	my $capacity_unit = $xpc->findvalue($xpath_find_capacity_unit);
> +	my $virtual_size;
> +	if (my $factor = try_parse_capacity_unit($capacity_unit)) {
> +	    $virtual_size = $capacity * $factor;
> +	}
>  
>  	my $valid_url_chars = qr@${valid_uripath_chars}|/@;
>  	if (!$fileref || $fileref !~ m/^${valid_url_chars}+$/) {
> @@ -216,7 +252,7 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
>  	    die "error parsing $filepath, are you using a symlink ?\n";
>  	}
>  
> -	if (!-e $backing_file_path) {
> +	if (!-e $backing_file_path && !$isOva) {
>  	    die "error parsing $filepath, file seems not to exist at $backing_file_path\n";
>  	}
>  
> @@ -224,16 +260,20 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
>  	($filepath) = $filepath =~ m|^(${PVE::Storage::SAFE_CHAR_CLASS_RE}+)$|; # untaint & check no sub/parent dirs
>  	die "invalid path\n" if !$filepath;
>  
> -	my $virtual_size = PVE::Storage::file_size_info($backing_file_path);
> -	die "error parsing $backing_file_path, cannot determine file size\n"
> -	    if !$virtual_size;
> +	if (!$isOva) {
> +	    my $size = PVE::Storage::file_size_info($backing_file_path);
> +	    die "error parsing $backing_file_path, cannot determine file size\n"
> +		if !$size;
>  
> +	    $virtual_size = $size;
> +	}
>  	$pve_disk = {
>  	    disk_address => $pve_disk_address,
>  	    backing_file => $backing_file_path,
>  	    virtual_size => $virtual_size,
>  	    relative_path => $filepath,
>  	};
> +	$pve_disk->{virtual_size} = $virtual_size if defined($virtual_size);
>  	push @disks, $pve_disk;
>  
>      }
> diff --git a/src/PVE/Makefile b/src/PVE/Makefile
> index e15a275..0af3081 100644
> --- a/src/PVE/Makefile
> +++ b/src/PVE/Makefile
> @@ -5,6 +5,7 @@ install:
>  	install -D -m 0644 Storage.pm ${DESTDIR}${PERLDIR}/PVE/Storage.pm
>  	install -D -m 0644 Diskmanage.pm ${DESTDIR}${PERLDIR}/PVE/Diskmanage.pm
>  	install -D -m 0644 CephConfig.pm ${DESTDIR}${PERLDIR}/PVE/CephConfig.pm
> +	install -D -m 0644 GuestImport.pm ${DESTDIR}${PERLDIR}/PVE/GuestImport.pm
>  	make -C Storage install
>  	make -C GuestImport install
>  	make -C API2 install
> diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm
> index 1ed91c2..bd6c4df 100755
> --- a/src/PVE/Storage.pm
> +++ b/src/PVE/Storage.pm
> @@ -114,10 +114,12 @@ our $VZTMPL_EXT_RE_1 = qr/\.tar\.(gz|xz|zst)/i;
>  
>  our $BACKUP_EXT_RE_2 = qr/\.(tgz|(?:tar|vma)(?:\.(${\PVE::Storage::Plugin::COMPRESSOR_RE}))?)/;
>  
> -our $IMPORT_EXT_RE_1 = qr/\.(ovf|qcow2|raw|vmdk)/;
> +our $IMPORT_EXT_RE_1 = qr/\.(ova|ovf|qcow2|raw|vmdk)/;
>  
>  our $SAFE_CHAR_CLASS_RE = qr/[a-zA-Z0-9\-\.\+\=\_]/;
>  
> +our $OVA_CONTENT_RE_1 = qr/${SAFE_CHAR_CLASS_RE}+\.(qcow2|raw|vmdk)/;
> +
>  # FIXME remove with PVE 8.0, add versioned breaks for pve-manager
>  our $vztmpl_extension_re = $VZTMPL_EXT_RE_1;
>  
> diff --git a/src/PVE/Storage/DirPlugin.pm b/src/PVE/Storage/DirPlugin.pm
> index 3e3b1e7..ea89464 100644
> --- a/src/PVE/Storage/DirPlugin.pm
> +++ b/src/PVE/Storage/DirPlugin.pm
> @@ -258,15 +258,26 @@ sub get_import_metadata {
>      # NOTE: all types of warnings must be added to the return schema of the import-metadata API endpoint
>      my $warnings = [];
>  
> +    my $isOva = 0;
> +    if ($name =~ m/\.ova$/) {
> +	$isOva = 1;
> +	push @$warnings, { type => 'ova-needs-extracting' };
> +    }
>      my $path = $class->path($scfg, $volname, $storeid, undef);
> -    my $res = PVE::GuestImport::OVF::parse_ovf($path);
> +    my $res = PVE::GuestImport::OVF::parse_ovf($path, $isOva);
>      my $disks = {};
>      for my $disk ($res->{disks}->@*) {
>  	my $id = $disk->{disk_address};
>  	my $size = $disk->{virtual_size};
>  	my $path = $disk->{relative_path};
> +	my $volid;
> +	if ($isOva) {
> +	    $volid = "$storeid:$volname/$path";
> +	} else {
> +	    $volid = "$storeid:import/$path",
> +	}
>  	$disks->{$id} = {
> -	    volid => "$storeid:import/$path",
> +	    volid => $volid,
>  	    defined($size) ? (size => $size) : (),
>  	};
>      }
> diff --git a/src/PVE/Storage/Plugin.pm b/src/PVE/Storage/Plugin.pm
> index fc30894..848e053 100644
> --- a/src/PVE/Storage/Plugin.pm
> +++ b/src/PVE/Storage/Plugin.pm
> @@ -654,6 +654,10 @@ sub parse_volname {
>  	return ('backup', $fn);
>      } elsif ($volname =~ m!^snippets/([^/]+)$!) {
>  	return ('snippets', $1);
> +    } elsif ($volname =~ m!^import/(${PVE::Storage::SAFE_CHAR_CLASS_RE}+\.ova\/${PVE::Storage::OVA_CONTENT_RE_1})$!) {
> +	my $archive = $1;
> +	my $format = $2;
> +	return ('import', $archive, undef, undef, undef, undef, "ova+$format");
>      } elsif ($volname =~ m!^import/(${PVE::Storage::SAFE_CHAR_CLASS_RE}+$PVE::Storage::IMPORT_EXT_RE_1)$!) {
>  	return ('import', $1, undef, undef, undef, undef, $2);
>      }
> diff --git a/src/test/parse_volname_test.pm b/src/test/parse_volname_test.pm
> index 0367b83..bc7b4e8 100644
> --- a/src/test/parse_volname_test.pm
> +++ b/src/test/parse_volname_test.pm
> @@ -83,11 +83,31 @@ my $tests = [
>      #
>      # Import
>      #
> +    {
> +	description => "Import, ova",
> +	volname     => 'import/import.ova',
> +	expected    => ['import', 'import.ova', undef, undef, undef ,undef, 'ova'],
> +    },
>      {
>  	description => "Import, ovf",
>  	volname     => 'import/import.ovf',
>  	expected    => ['import', 'import.ovf', undef, undef, undef ,undef, 'ovf'],
>      },
> +    {
> +	description => "Import, innner file of ova",
> +	volname     => 'import/import.ova/disk.qcow2',
> +	expected    => ['import', 'import.ova/disk.qcow2', undef, undef, undef, undef, 'ova+qcow2'],
> +    },
> +    {
> +	description => "Import, innner file of ova",
> +	volname     => 'import/import.ova/disk.vmdk',
> +	expected    => ['import', 'import.ova/disk.vmdk', undef, undef, undef, undef, 'ova+vmdk'],
> +    },
> +    {
> +	description => "Import, innner file of ova",
> +	volname     => 'import/import.ova/disk.raw',
> +	expected    => ['import', 'import.ova/disk.raw', undef, undef, undef, undef, 'ova+raw'],
> +    },
>      #
>      # failed matches
>      #
> diff --git a/src/test/path_to_volume_id_test.pm b/src/test/path_to_volume_id_test.pm
> index 0e0c15f..0d238f9 100644
> --- a/src/test/path_to_volume_id_test.pm
> +++ b/src/test/path_to_volume_id_test.pm
> @@ -174,6 +174,14 @@ my @tests = (
>  	    'local:vztmpl/debian-10.0-standard_10.0-1_amd64.tar.xz',
>  	],
>      },
> +    {
> +	description => 'Import, ova',
> +	volname     => "$storage_dir/import/import.ova",
> +	expected    => [
> +	    'import',
> +	    'local:import/import.ova',
> +	],
> +    },
>      {
>  	description => 'Import, ovf',
>  	volname     => "$storage_dir/import/import.ovf",



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH storage v4 10/12] plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs
  2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 10/12] plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs Dominik Csapak
@ 2024-06-12 15:57   ` Max Carrara
  0 siblings, 0 replies; 39+ messages in thread
From: Max Carrara @ 2024-06-12 15:57 UTC (permalink / raw)
  To: Proxmox VE development discussion

On Fri May 24, 2024 at 3:21 PM CEST, Dominik Csapak wrote:
> and reuse the DirPlugin implementation
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  src/PVE/Storage/BTRFSPlugin.pm     | 5 +++++
>  src/PVE/Storage/CIFSPlugin.pm      | 6 +++++-
>  src/PVE/Storage/CephFSPlugin.pm    | 6 +++++-
>  src/PVE/Storage/GlusterfsPlugin.pm | 6 +++++-
>  src/PVE/Storage/NFSPlugin.pm       | 6 +++++-
>  5 files changed, 25 insertions(+), 4 deletions(-)
>
> diff --git a/src/PVE/Storage/BTRFSPlugin.pm b/src/PVE/Storage/BTRFSPlugin.pm
> index 42815cb..b7e3f82 100644
> --- a/src/PVE/Storage/BTRFSPlugin.pm
> +++ b/src/PVE/Storage/BTRFSPlugin.pm
> @@ -40,6 +40,7 @@ sub plugindata {
>  		backup => 1,
>  		snippets => 1,
>  		none => 1,
> +		import => 1,
>  	    },
>  	    { images => 1, rootdir => 1 },
>  	],
> @@ -930,4 +931,8 @@ sub volume_import {
>      return "$storeid:$volname";
>  }
>  
> +sub get_import_metadata {
> +    return PVE::Storage::DirPlugin::get_import_metadata(@_);

Not _reaaaaally_ a fan of one plugin having to rely on another one, as
that introduces (another) interdependency (-> higher coupling). But I
would leave this as it is, because I'm working on a series that aims to
resolve stuff like that (and it's IMO better to just tackle this all in
one series at once anyway). So this is IMO fine and gets an OK from me.
Just wanted to note.

> +}
> +
>  1
> diff --git a/src/PVE/Storage/CIFSPlugin.pm b/src/PVE/Storage/CIFSPlugin.pm
> index 2184471..475065a 100644
> --- a/src/PVE/Storage/CIFSPlugin.pm
> +++ b/src/PVE/Storage/CIFSPlugin.pm
> @@ -99,7 +99,7 @@ sub type {
>  sub plugindata {
>      return {
>  	content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1,
> -		   backup => 1, snippets => 1}, { images => 1 }],
> +		   backup => 1, snippets => 1, import => 1}, { images => 1 }],
>  	format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
>      };
>  }
> @@ -314,4 +314,8 @@ sub update_volume_attribute {
>      return PVE::Storage::DirPlugin::update_volume_attribute(@_);
>  }
>  
> +sub get_import_metadata {
> +    return PVE::Storage::DirPlugin::get_import_metadata(@_);
> +}
> +
>  1;
> diff --git a/src/PVE/Storage/CephFSPlugin.pm b/src/PVE/Storage/CephFSPlugin.pm
> index 8aad518..36c64ea 100644
> --- a/src/PVE/Storage/CephFSPlugin.pm
> +++ b/src/PVE/Storage/CephFSPlugin.pm
> @@ -116,7 +116,7 @@ sub type {
>  
>  sub plugindata {
>      return {
> -	content => [ { vztmpl => 1, iso => 1, backup => 1, snippets => 1},
> +	content => [ { vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1 },
>  		     { backup => 1 }],
>      };
>  }
> @@ -261,4 +261,8 @@ sub update_volume_attribute {
>      return PVE::Storage::DirPlugin::update_volume_attribute(@_);
>  }
>  
> +sub get_import_metadata {
> +    return PVE::Storage::DirPlugin::get_import_metadata(@_);
> +}
> +
>  1;
> diff --git a/src/PVE/Storage/GlusterfsPlugin.pm b/src/PVE/Storage/GlusterfsPlugin.pm
> index 2b7f9e1..9d17180 100644
> --- a/src/PVE/Storage/GlusterfsPlugin.pm
> +++ b/src/PVE/Storage/GlusterfsPlugin.pm
> @@ -97,7 +97,7 @@ sub type {
>  
>  sub plugindata {
>      return {
> -	content => [ { images => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1},
> +	content => [ { images => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1},
>  		     { images => 1 }],
>  	format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
>      };
> @@ -352,4 +352,8 @@ sub check_connection {
>      return defined($server) ? 1 : 0;
>  }
>  
> +sub get_import_metadata {
> +    return PVE::Storage::DirPlugin::get_import_metadata(@_);
> +}
> +
>  1;
> diff --git a/src/PVE/Storage/NFSPlugin.pm b/src/PVE/Storage/NFSPlugin.pm
> index f2e4c0d..72e9c6d 100644
> --- a/src/PVE/Storage/NFSPlugin.pm
> +++ b/src/PVE/Storage/NFSPlugin.pm
> @@ -53,7 +53,7 @@ sub type {
>  
>  sub plugindata {
>      return {
> -	content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1 },
> +	content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1 },

This hash exceeds our line length limit, but I assume that's because it's
easier to read in diffs ... Can IMO be also left like that and changed
later or be adapted in a follow-up patch if so desired.

>  		     { images => 1 }],
>  	format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
>      };
> @@ -223,4 +223,8 @@ sub update_volume_attribute {
>      return PVE::Storage::DirPlugin::update_volume_attribute(@_);
>  }
>  
> +sub get_import_metadata {
> +    return PVE::Storage::DirPlugin::get_import_metadata(@_);
> +}
> +
>  1;



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH qemu-server v4 3/4] api: create: implement extracting disks when needed for import-from
  2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 3/4] api: create: implement extracting disks when needed for import-from Dominik Csapak
@ 2024-06-12 16:01   ` Max Carrara
  2024-06-13 10:29     ` Dominik Csapak
  0 siblings, 1 reply; 39+ messages in thread
From: Max Carrara @ 2024-06-12 16:01 UTC (permalink / raw)
  To: Proxmox VE development discussion

On Fri May 24, 2024 at 3:21 PM CEST, Dominik Csapak wrote:
> when 'import-from' contains a disk image that needs extraction
> (currently only from an 'ova' archive), do that in 'create_disks'
> and overwrite the '$source' volid.
>
> Collect the names into a 'delete_sources' list, that we use later
> to clean it up again (either when we're finished with importing or in an
> error case).
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  PVE/API2/Qemu.pm          | 55 +++++++++++++++++++++++++++++++--------
>  PVE/QemuServer.pm         | 12 +++++++++
>  PVE/QemuServer/Helpers.pm |  5 ++++
>  3 files changed, 61 insertions(+), 11 deletions(-)
>
> diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
> index 2a1d4d79..8c335ac4 100644
> --- a/PVE/API2/Qemu.pm
> +++ b/PVE/API2/Qemu.pm
> @@ -24,6 +24,7 @@ use PVE::JSONSchema qw(get_standard_option);
>  use PVE::RESTHandler;
>  use PVE::ReplicationConfig;
>  use PVE::GuestHelpers qw(assert_tag_permissions);
> +use PVE::GuestImport;
>  use PVE::QemuConfig;
>  use PVE::QemuServer;
>  use PVE::QemuServer::Cloudinit;
> @@ -159,10 +160,19 @@ my $check_storage_access = sub {
>  
>  	if (my $src_image = $drive->{'import-from'}) {
>  	    my $src_vmid;
> -	    if (PVE::Storage::parse_volume_id($src_image, 1)) { # PVE-managed volume
> -		(my $vtype, undef, $src_vmid) = PVE::Storage::parse_volname($storecfg, $src_image);
> -		raise_param_exc({ $ds => "$src_image has wrong type '$vtype' - not an image" })
> -		    if $vtype ne 'images';
> +	    if (my ($storeid, $volname) = PVE::Storage::parse_volume_id($src_image, 1)) { # PVE-managed volume
> +		my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
> +		my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
> +		(my $vtype, undef, $src_vmid, undef, undef, undef, my $fmt) = $plugin->parse_volname($volname);
> +
> +		raise_param_exc({ $ds => "$src_image has wrong type '$vtype' - needs to be 'images' or 'import'" })
> +		    if $vtype ne 'images' && $vtype ne 'import';

... Shouldn't this here be `||` instead of `&&`? According to the error
message at least.

> +
> +		if (PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt)) {
> +		    raise_param_exc({ $ds => "$src_image is not on an storage with 'images' content type."})

s/an storage/a storage

Also, as I mentioned in another comment, is it maybe possible to display
the same name that's used in the UI instead of 'images' or 'import'?

> +			if !$scfg->{content}->{images};
> +		    $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
> +		}
>  	    }
>  
>  	    if ($src_vmid) { # might be actively used by VM and will be copied via clone_disk()
> @@ -392,16 +402,34 @@ my sub create_disks : prototype($$$$$$$$$$) {
>  		$needs_creation = $live_import;
>  
>  		if (PVE::Storage::parse_volume_id($source, 1)) { # PVE-managed volume
> +		    my ($vtype, undef, undef, undef, undef, undef, $fmt)
> +			= PVE::Storage::parse_volname($storecfg, $source);
> +		    my $needs_extraction = PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt);
> +		    if ($needs_extraction) {
> +			print "extracting $source\n";
> +			my $extracted_volid
> +			     = PVE::GuestImport::extract_disk_from_import_file($source, $vmid);
> +			print "finished extracting to $extracted_volid\n";
> +			push @$vollist, $extracted_volid;
> +			$source = $extracted_volid;
> +
> +			my (undef, undef, undef, $parent)
> +			    = PVE::Storage::volume_size_info($storecfg, $source);
> +			die "importing from extracted images with backing file ($parent) not allowed\n"
> +			    if $parent;
> +		    }
> +
>  		    if ($live_import && $ds ne 'efidisk0') {
>  			my $path = PVE::Storage::path($storecfg, $source)
>  			    or die "failed to get a path for '$source'\n";
> -			$source = $path;
> -			($size, my $source_format) = PVE::Storage::file_size_info($source);
> -			die "could not get file size of $source\n" if !$size;
> +			($size, my $source_format) = PVE::Storage::file_size_info($path);
> +			die "could not get file size of $path\n" if !$size;
>  			$live_import_mapping->{$ds} = {
> -			    path => $source,
> +			    path => $path,
>  			    format => $source_format,
>  			};
> +			$live_import_mapping->{$ds}->{'delete-after-finish'} = $source
> +			    if $needs_extraction;
>  		    } else {
>  			my $dest_info = {
>  			    vmid => $vmid,
> @@ -413,8 +441,14 @@ my sub create_disks : prototype($$$$$$$$$$) {
>  			$dest_info->{efisize} = PVE::QemuServer::get_efivars_size($conf, $disk)
>  			    if $ds eq 'efidisk0';
>  
> -			($dst_volid, $size) = eval {
> -			    $import_from_volid->($storecfg, $source, $dest_info, $vollist);
> +			eval {
> +			    ($dst_volid, $size)
> +				= $import_from_volid->($storecfg, $source, $dest_info, $vollist);
> +
> +			    # remove extracted volumes after importing
> +			    PVE::Storage::vdisk_free($storecfg, $source) if $needs_extraction;
> +			    print "cleaned up extracted image $source\n";
> +			    @$vollist = grep { $_ ne $source } @$vollist;
>  			};
>  			die "cannot import from '$source' - $@" if $@;
>  		    }
> @@ -1939,7 +1973,6 @@ my $update_vm_api  = sub {
>  
>  		    assert_scsi_feature_compatibility($opt, $conf, $storecfg, $param->{$opt})
>  			if $opt =~ m/^scsi\d+$/;
> -
>  		    my (undef, $created_opts) = create_disks(
>  			$rpcenv,
>  			$authuser,
> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
> index 5df0c96d..76136570 100644
> --- a/PVE/QemuServer.pm
> +++ b/PVE/QemuServer.pm
> @@ -7310,6 +7310,7 @@ sub live_import_from_files {
>      my ($mapping, $vmid, $conf, $restore_options) = @_;
>  
>      my $live_restore_backing = {};
> +    my $sources_to_remove = [];
>      for my $dev (keys %$mapping) {
>  	die "disk not support for live-restoring: '$dev'\n"
>  	    if !is_valid_drivename($dev) || $dev =~ /^(?:efidisk|tpmstate)/;
> @@ -7330,6 +7331,9 @@ sub live_import_from_files {
>  	    . ",read-only=on"
>  	    . ",file.driver=file,file.filename=$path"
>  	};
> +
> +	my $source_volid = $info->{'delete-after-finish'};
> +	push $sources_to_remove->@*, $source_volid if defined($source_volid);
>      };
>  
>      my $storecfg = PVE::Storage::config();
> @@ -7373,6 +7377,14 @@ sub live_import_from_files {
>  
>      my $err = $@;
>  
> +    for my $volid ($sources_to_remove->@*) {
> +	eval {
> +	    PVE::Storage::vdisk_free($storecfg, $volid);
> +	    print "cleaned up extracted image $volid\n";
> +	};
> +	warn "An error occurred while cleaning up source images: $@\n" if $@;
> +    }
> +
>      if ($err) {
>  	warn "An error occurred during live-restore: $err\n";
>  	_do_vm_stop($storecfg, $vmid, 1, 1, 10, 0, 1);
> diff --git a/PVE/QemuServer/Helpers.pm b/PVE/QemuServer/Helpers.pm
> index 0afb6317..15e2496c 100644
> --- a/PVE/QemuServer/Helpers.pm
> +++ b/PVE/QemuServer/Helpers.pm
> @@ -225,4 +225,9 @@ sub windows_version {
>      return $winversion;
>  }
>  
> +sub needs_extraction {
> +    my ($vtype, $fmt) = @_;
> +    return $vtype eq 'import' && $fmt =~ m/^ova\+(.*)$/;
> +}
> +
>  1;



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH qemu-server v4 4/4] api: create: add 'import-extraction-storage' parameter
  2024-05-24 13:22 ` [pve-devel] [PATCH qemu-server v4 4/4] api: create: add 'import-extraction-storage' parameter Dominik Csapak
@ 2024-06-12 16:01   ` Max Carrara
  0 siblings, 0 replies; 39+ messages in thread
From: Max Carrara @ 2024-06-12 16:01 UTC (permalink / raw)
  To: Proxmox VE development discussion

On Fri May 24, 2024 at 3:22 PM CEST, Dominik Csapak wrote:
> this is to override the target extraction storage for the option disk
> extraction for 'import-from'. This way if the storage does not
> supports the content type 'images', one can give an alternative  one.
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  PVE/API2/Qemu.pm | 46 +++++++++++++++++++++++++++++++++++++---------
>  1 file changed, 37 insertions(+), 9 deletions(-)
>
> diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
> index 8c335ac4..80ea52c5 100644
> --- a/PVE/API2/Qemu.pm
> +++ b/PVE/API2/Qemu.pm
> @@ -128,7 +128,7 @@ my $check_drive_param = sub {
>  };
>  
>  my $check_storage_access = sub {
> -   my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_;
> +   my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage, $extraction_storage) = @_;
>  
>     $foreach_volume_with_alloc->($settings, sub {
>  	my ($ds, $drive) = @_;
> @@ -169,9 +169,18 @@ my $check_storage_access = sub {
>  		    if $vtype ne 'images' && $vtype ne 'import';
>  
>  		if (PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt)) {
> -		    raise_param_exc({ $ds => "$src_image is not on an storage with 'images' content type."})
> -			if !$scfg->{content}->{images};
> -		    $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
> +		    if (defined($extraction_storage)) {
> +			my $extraction_scfg = PVE::Storage::storage_config($storecfg, $extraction_storage);
> +			raise_param_exc({ 'import-extraction-storage' => "$extraction_storage does not support"
> +				." 'images' content type or is not file based."})

Is there perhaps a way to display "Disk images" like in the storage
config drop down menu? Also, this is where the error handling could be
more fine-grained so the user immediately knows what's wrong, as
mentioned in my response to your cover letter.

> +			if !$extraction_scfg->{content}->{images} || !$extraction_scfg->{path};

Style: The `raise_param_exc` plus `if` here should IMO be indented like
you did ...

> +			$rpcenv->check($authuser, "/storage/$extraction_storage", ['Datastore.AllocateSpace']);
> +		    } else {
> +			raise_param_exc({ $ds => "$src_image is not on an storage with 'images'"

s/an storage/a storage

> +			    ." content type and no 'import-extraction-storage' was given."})
> +			    if !$scfg->{content}->{images};

... here.

> +			$rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
> +		    }
>  		}
>  	    }
>  
> @@ -326,7 +335,7 @@ my $import_from_volid = sub {
>  
>  # Note: $pool is only needed when creating a VM, because pool permissions
>  # are automatically inherited if VM already exists inside a pool.
> -my sub create_disks : prototype($$$$$$$$$$) {
> +my sub create_disks : prototype($$$$$$$$$$$) {
>      my (
>  	$rpcenv,
>  	$authuser,
> @@ -338,6 +347,7 @@ my sub create_disks : prototype($$$$$$$$$$) {
>  	$settings,
>  	$default_storage,
>  	$is_live_import,
> +	$extraction_storage,
>      ) = @_;
>  
>      my $vollist = [];
> @@ -407,8 +417,8 @@ my sub create_disks : prototype($$$$$$$$$$) {
>  		    my $needs_extraction = PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt);
>  		    if ($needs_extraction) {
>  			print "extracting $source\n";
> -			my $extracted_volid
> -			     = PVE::GuestImport::extract_disk_from_import_file($source, $vmid);
> +			my $extracted_volid = PVE::GuestImport::extract_disk_from_import_file(
> +			    $source, $vmid, $extraction_storage);
>  			print "finished extracting to $extracted_volid\n";
>  			push @$vollist, $extracted_volid;
>  			$source = $extracted_volid;
> @@ -941,6 +951,12 @@ __PACKAGE__->register_method({
>  		    default => 0,
>  		    description => "Start VM after it was created successfully.",
>  		},
> +		'import-extraction-storage' => get_standard_option('pve-storage-id', {
> +		    description => "Storage for temporarily extracted images 'import-from' image"
> +			." files (default: import source storage)",
> +		    optional => 1,
> +		    completion => \&PVE::QemuServer::complete_storage,
> +		}),
>  	    },
>  	    1, # with_disk_alloc
>  	),
> @@ -967,6 +983,7 @@ __PACKAGE__->register_method({
>  	my $storage = extract_param($param, 'storage');
>  	my $unique = extract_param($param, 'unique');
>  	my $live_restore = extract_param($param, 'live-restore');
> +	my $extraction_storage = extract_param($param, 'import-extraction-storage');
>  
>  	if (defined(my $ssh_keys = $param->{sshkeys})) {
>  		$ssh_keys = URI::Escape::uri_unescape($ssh_keys);
> @@ -1026,7 +1043,8 @@ __PACKAGE__->register_method({
>  	if (scalar(keys $param->%*) > 0) {
>  	    &$resolve_cdrom_alias($param);
>  
> -	    &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
> +	    &$check_storage_access(
> +		$rpcenv, $authuser, $storecfg, $vmid, $param, $storage, $extraction_storage);
>  
>  	    &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
>  
> @@ -1141,6 +1159,7 @@ __PACKAGE__->register_method({
>  			$param,
>  			$storage,
>  			$live_restore,
> +			$extraction_storage
>  		    );
>  		    $conf->{$_} = $created_opts->{$_} for keys $created_opts->%*;
>  
> @@ -1683,6 +1702,8 @@ my $update_vm_api  = sub {
>  
>      my $skip_cloud_init = extract_param($param, 'skip_cloud_init');
>  
> +    my $extraction_storage = extract_param($param, 'import-extraction-storage');
> +
>      if (defined(my $cipassword = $param->{cipassword})) {
>  	# Same logic as in cloud-init (but with the regex fixed...)
>  	$param->{cipassword} = PVE::Tools::encrypt_pw($cipassword)
> @@ -1802,7 +1823,7 @@ my $update_vm_api  = sub {
>  
>      &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
>  
> -    &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param);
> +    &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, undef, $extraction_storage);

Style: Should perhaps be indented like above:

	    &$check_storage_access(
		$rpcenv, $authuser, $storecfg, $vmid, $param, $storage, $extraction_storage);

>  
>      PVE::QemuServer::check_bridge_access($rpcenv, $authuser, $param);
>  
> @@ -1984,6 +2005,7 @@ my $update_vm_api  = sub {
>  			{$opt => $param->{$opt}},
>  			undef,
>  			undef,
> +			$extraction_storage,
>  		    );
>  		    $conf->{pending}->{$_} = $created_opts->{$_} for keys $created_opts->%*;
>  
> @@ -2179,6 +2201,12 @@ __PACKAGE__->register_method({
>  		    maximum => 30,
>  		    optional => 1,
>  		},
> +		'import-extraction-storage' => get_standard_option('pve-storage-id', {
> +		    description => "Storage for temporarily extracted images 'import-from' image"
> +			." files (default: import source storage)",
> +		    optional => 1,
> +		    completion => \&PVE::QemuServer::complete_storage,
> +		}),
>  	    },
>  	    1, # with_disk_alloc
>  	),



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text
  2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text Dominik Csapak
@ 2024-06-12 16:02   ` Max Carrara
  2024-06-13 10:39     ` Dominik Csapak
  0 siblings, 1 reply; 39+ messages in thread
From: Max Carrara @ 2024-06-12 16:02 UTC (permalink / raw)
  To: Proxmox VE development discussion

On Fri May 24, 2024 at 3:22 PM CEST, Dominik Csapak wrote:
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  www/manager6/window/GuestImport.js | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/www/manager6/window/GuestImport.js b/www/manager6/window/GuestImport.js
> index 4bedc211..76ba6dc8 100644
> --- a/www/manager6/window/GuestImport.js
> +++ b/www/manager6/window/GuestImport.js
> @@ -937,6 +937,7 @@ Ext.define('PVE.window.GuestImport', {
>  		    gettext('EFI state cannot be imported, you may need to reconfigure the boot order (see {0})'),
>  		    '<a href="https://pve.proxmox.com/wiki/OVMF/UEFI_Boot_Entries">OVMF/UEFI Boot Entries</a>',
>  		),
> +		'ova-needs-extracting': gettext('Importing from an OVA requires extra space while extracting the contained disks into the import or selected storage.'),

I'm assuming the string here needs to be in one line because of
`gettext`, right? If not, I'd prefer to break it up ;)

>  	    };
>              let message = warningsCatalogue[w.type];
>  	    if (!w.type || !message) {



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH qemu-server v4 3/4] api: create: implement extracting disks when needed for import-from
  2024-06-12 16:01   ` Max Carrara
@ 2024-06-13 10:29     ` Dominik Csapak
  2024-06-14  8:36       ` Max Carrara
  0 siblings, 1 reply; 39+ messages in thread
From: Dominik Csapak @ 2024-06-13 10:29 UTC (permalink / raw)
  To: Proxmox VE development discussion, Max Carrara

On 6/12/24 18:01, Max Carrara wrote:
> On Fri May 24, 2024 at 3:21 PM CEST, Dominik Csapak wrote:
>> when 'import-from' contains a disk image that needs extraction
>> (currently only from an 'ova' archive), do that in 'create_disks'
>> and overwrite the '$source' volid.
>>
>> Collect the names into a 'delete_sources' list, that we use later
>> to clean it up again (either when we're finished with importing or in an
>> error case).
>>
>> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
>> ---
>>   PVE/API2/Qemu.pm          | 55 +++++++++++++++++++++++++++++++--------
>>   PVE/QemuServer.pm         | 12 +++++++++
>>   PVE/QemuServer/Helpers.pm |  5 ++++
>>   3 files changed, 61 insertions(+), 11 deletions(-)
>>
>> diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
>> index 2a1d4d79..8c335ac4 100644
>> --- a/PVE/API2/Qemu.pm
>> +++ b/PVE/API2/Qemu.pm
>> @@ -24,6 +24,7 @@ use PVE::JSONSchema qw(get_standard_option);
>>   use PVE::RESTHandler;
>>   use PVE::ReplicationConfig;
>>   use PVE::GuestHelpers qw(assert_tag_permissions);
>> +use PVE::GuestImport;
>>   use PVE::QemuConfig;
>>   use PVE::QemuServer;
>>   use PVE::QemuServer::Cloudinit;
>> @@ -159,10 +160,19 @@ my $check_storage_access = sub {
>>   
>>   	if (my $src_image = $drive->{'import-from'}) {
>>   	    my $src_vmid;
>> -	    if (PVE::Storage::parse_volume_id($src_image, 1)) { # PVE-managed volume
>> -		(my $vtype, undef, $src_vmid) = PVE::Storage::parse_volname($storecfg, $src_image);
>> -		raise_param_exc({ $ds => "$src_image has wrong type '$vtype' - not an image" })
>> -		    if $vtype ne 'images';
>> +	    if (my ($storeid, $volname) = PVE::Storage::parse_volume_id($src_image, 1)) { # PVE-managed volume
>> +		my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
>> +		my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
>> +		(my $vtype, undef, $src_vmid, undef, undef, undef, my $fmt) = $plugin->parse_volname($volname);
>> +
>> +		raise_param_exc({ $ds => "$src_image has wrong type '$vtype' - needs to be 'images' or 'import'" })
>> +		    if $vtype ne 'images' && $vtype ne 'import';
> 
> ... Shouldn't this here be `||` instead of `&&`? According to the error
> message at least.

no?

we raise the  error if it is not 'images' and not 'import', so it's neither
making it || would always fail then because images != import and vice versa ;)

(note the operator is 'ne' not 'eq' )

we could make it more like the error message but then it would have to be:

if !($vtype eq 'images' || $vtype eq 'import')

which should be the same what i posted just with more syntax ;)

> 
>> +
>> +		if (PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt)) {
>> +		    raise_param_exc({ $ds => "$src_image is not on an storage with 'images' content type."})
> 
> s/an storage/a storage
> 
> Also, as I mentioned in another comment, is it maybe possible to display
> the same name that's used in the UI instead of 'images' or 'import'?

we generally stick to the backend terminology in error messages from the backend AFAIK
but i'd be willing to change that if we have precendence somewhere for that

these error message also are for the cli though, and there we use also the backend terminology
for the configs

also we generally try to catch such cases beforehand in the ui (if possible) so the
user does not see the backend error message at all most of the time

> 
>> +			if !$scfg->{content}->{images};
>> +		    $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
>> +		}
>>   	    }
>>   
>>   	    if ($src_vmid) { # might be actively used by VM and will be copied via clone_disk()
>> @@ -392,16 +402,34 @@ my sub create_disks : prototype($$$$$$$$$$) {
>>   		$needs_creation = $live_import;
>>   
>>   		if (PVE::Storage::parse_volume_id($source, 1)) { # PVE-managed volume
>> +		    my ($vtype, undef, undef, undef, undef, undef, $fmt)
>> +			= PVE::Storage::parse_volname($storecfg, $source);
>> +		    my $needs_extraction = PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt);
>> +		    if ($needs_extraction) {
>> +			print "extracting $source\n";
>> +			my $extracted_volid
>> +			     = PVE::GuestImport::extract_disk_from_import_file($source, $vmid);
>> +			print "finished extracting to $extracted_volid\n";
>> +			push @$vollist, $extracted_volid;
>> +			$source = $extracted_volid;
>> +
>> +			my (undef, undef, undef, $parent)
>> +			    = PVE::Storage::volume_size_info($storecfg, $source);
>> +			die "importing from extracted images with backing file ($parent) not allowed\n"
>> +			    if $parent;
>> +		    }
>> +
>>   		    if ($live_import && $ds ne 'efidisk0') {
>>   			my $path = PVE::Storage::path($storecfg, $source)
>>   			    or die "failed to get a path for '$source'\n";
>> -			$source = $path;
>> -			($size, my $source_format) = PVE::Storage::file_size_info($source);
>> -			die "could not get file size of $source\n" if !$size;
>> +			($size, my $source_format) = PVE::Storage::file_size_info($path);
>> +			die "could not get file size of $path\n" if !$size;
>>   			$live_import_mapping->{$ds} = {
>> -			    path => $source,
>> +			    path => $path,
>>   			    format => $source_format,
>>   			};
>> +			$live_import_mapping->{$ds}->{'delete-after-finish'} = $source
>> +			    if $needs_extraction;
>>   		    } else {
>>   			my $dest_info = {
>>   			    vmid => $vmid,
>> @@ -413,8 +441,14 @@ my sub create_disks : prototype($$$$$$$$$$) {
>>   			$dest_info->{efisize} = PVE::QemuServer::get_efivars_size($conf, $disk)
>>   			    if $ds eq 'efidisk0';
>>   
>> -			($dst_volid, $size) = eval {
>> -			    $import_from_volid->($storecfg, $source, $dest_info, $vollist);
>> +			eval {
>> +			    ($dst_volid, $size)
>> +				= $import_from_volid->($storecfg, $source, $dest_info, $vollist);
>> +
>> +			    # remove extracted volumes after importing
>> +			    PVE::Storage::vdisk_free($storecfg, $source) if $needs_extraction;
>> +			    print "cleaned up extracted image $source\n";
>> +			    @$vollist = grep { $_ ne $source } @$vollist;
>>   			};
>>   			die "cannot import from '$source' - $@" if $@;
>>   		    }
>> @@ -1939,7 +1973,6 @@ my $update_vm_api  = sub {
>>   
>>   		    assert_scsi_feature_compatibility($opt, $conf, $storecfg, $param->{$opt})
>>   			if $opt =~ m/^scsi\d+$/;
>> -
>>   		    my (undef, $created_opts) = create_disks(
>>   			$rpcenv,
>>   			$authuser,
>> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
>> index 5df0c96d..76136570 100644
>> --- a/PVE/QemuServer.pm
>> +++ b/PVE/QemuServer.pm
>> @@ -7310,6 +7310,7 @@ sub live_import_from_files {
>>       my ($mapping, $vmid, $conf, $restore_options) = @_;
>>   
>>       my $live_restore_backing = {};
>> +    my $sources_to_remove = [];
>>       for my $dev (keys %$mapping) {
>>   	die "disk not support for live-restoring: '$dev'\n"
>>   	    if !is_valid_drivename($dev) || $dev =~ /^(?:efidisk|tpmstate)/;
>> @@ -7330,6 +7331,9 @@ sub live_import_from_files {
>>   	    . ",read-only=on"
>>   	    . ",file.driver=file,file.filename=$path"
>>   	};
>> +
>> +	my $source_volid = $info->{'delete-after-finish'};
>> +	push $sources_to_remove->@*, $source_volid if defined($source_volid);
>>       };
>>   
>>       my $storecfg = PVE::Storage::config();
>> @@ -7373,6 +7377,14 @@ sub live_import_from_files {
>>   
>>       my $err = $@;
>>   
>> +    for my $volid ($sources_to_remove->@*) {
>> +	eval {
>> +	    PVE::Storage::vdisk_free($storecfg, $volid);
>> +	    print "cleaned up extracted image $volid\n";
>> +	};
>> +	warn "An error occurred while cleaning up source images: $@\n" if $@;
>> +    }
>> +
>>       if ($err) {
>>   	warn "An error occurred during live-restore: $err\n";
>>   	_do_vm_stop($storecfg, $vmid, 1, 1, 10, 0, 1);
>> diff --git a/PVE/QemuServer/Helpers.pm b/PVE/QemuServer/Helpers.pm
>> index 0afb6317..15e2496c 100644
>> --- a/PVE/QemuServer/Helpers.pm
>> +++ b/PVE/QemuServer/Helpers.pm
>> @@ -225,4 +225,9 @@ sub windows_version {
>>       return $winversion;
>>   }
>>   
>> +sub needs_extraction {
>> +    my ($vtype, $fmt) = @_;
>> +    return $vtype eq 'import' && $fmt =~ m/^ova\+(.*)$/;
>> +}
>> +
>>   1;
> 
> 
> 
> _______________________________________________
> pve-devel mailing list
> pve-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> 
> 



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text
  2024-06-12 16:02   ` Max Carrara
@ 2024-06-13 10:39     ` Dominik Csapak
  2024-06-13 10:52       ` Fiona Ebner
  2024-06-14  8:37       ` Max Carrara
  0 siblings, 2 replies; 39+ messages in thread
From: Dominik Csapak @ 2024-06-13 10:39 UTC (permalink / raw)
  To: Proxmox VE development discussion, Max Carrara

On 6/12/24 18:02, Max Carrara wrote:
> On Fri May 24, 2024 at 3:22 PM CEST, Dominik Csapak wrote:
>> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
>> ---
>>   www/manager6/window/GuestImport.js | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/www/manager6/window/GuestImport.js b/www/manager6/window/GuestImport.js
>> index 4bedc211..76ba6dc8 100644
>> --- a/www/manager6/window/GuestImport.js
>> +++ b/www/manager6/window/GuestImport.js
>> @@ -937,6 +937,7 @@ Ext.define('PVE.window.GuestImport', {
>>   		    gettext('EFI state cannot be imported, you may need to reconfigure the boot order (see {0})'),
>>   		    '<a href="https://pve.proxmox.com/wiki/OVMF/UEFI_Boot_Entries">OVMF/UEFI Boot Entries</a>',
>>   		),
>> +		'ova-needs-extracting': gettext('Importing from an OVA requires extra space while extracting the contained disks into the import or selected storage.'),
> 
> I'm assuming the string here needs to be in one line because of
> `gettext`, right? If not, I'd prefer to break it up ;)

yes, AFAIK we the gettext parsing script does not handle multiline calls at the moment
we could probably check if there is a javascript syntax parsing library available
in perl, or rewrite the script in some other language where such a thing exists,
then it wouldn't be so brittle probably

but it's not generally such a big issue that we would need to worry about it

> 
>>   	    };
>>               let message = warningsCatalogue[w.type];
>>   	    if (!w.type || !message) {
> 
> 
> 
> _______________________________________________
> pve-devel mailing list
> pve-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> 
> 



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages
  2024-06-12 15:56 ` [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Max Carrara
@ 2024-06-13 10:52   ` Dominik Csapak
  2024-06-14  8:57     ` Max Carrara
  0 siblings, 1 reply; 39+ messages in thread
From: Dominik Csapak @ 2024-06-13 10:52 UTC (permalink / raw)
  To: Proxmox VE development discussion, Max Carrara

On 6/12/24 17:56, Max Carrara wrote:
> On Fri May 24, 2024 at 3:21 PM CEST, Dominik Csapak wrote:
>> This series enables importing ova/ovf from directory based storages,
>> inclusive upload/download via the webui (ova only).
>>
>> It also improves the ovf importer by parsing the ostype, nics, bootorder
>> (and firmware from vmware exported files).
> 
> Really great work! Built the packages and ran some tests. There are also
> a couple comments, so see below.
> 
> Building
> --------
> 
> * Patches applied cleanly
> * Had to build the `qemu-server` package on a development VM and build
>    and install `pve-storage` with the patches installed there first,
>    because the new `PVE::GuestImport` module couldn't be found
>    (obviously). Worked fine though.
> 
> Testing
> -------
> 
> * Installed all packages on a development VM, encountered no issues
> 
> * Checked if new `import` content type shows up only for directory-esque
>    storage types
>    --> sure does
> 
> * Added `import` as content type to my "local" storage
> * Checked if "local" storage has new "import" tab
>    --> it does
> 
> * Exported a Debian VM from ESXi and tarballed the .ovf, .mf and .vmdk
>    to an .ova file
> * Uploaded the file to the storage
>    --> succeeded
> 
> * Tried to import the VM onto my local lvm thin-pool storage
>    --> fails, because my "local" storage doesn't have the "Disk Image"
>        content type
> 
> I hadn't expected this to be honest, because I'm not trying to import it
> to "local" here?

by default the images are extracted onto the import storage, so it
needs the disk images too in case they're left over, so users
have an easy way to clean them up.

you can give an explicit extraction storage though (that too
needs  'images' as content type)

> 
> * Created a new storage with type "directory" and set its content types
>    to "import" and "disk image"
> * Tried to import the VM again, but this time selected my LVM thin pool
>    as "Extraction Storage" in the dialogue window
>    --> also fails as expected, because an LVM thin pool is not file-based
> 
> A note on the above test: I'd prefer if the error message here was more
> fine grained - the popup said:
>> import-extraction-storage: local-lvm does not support 'images' content
>> type or is not file based.
> 
> IMO it would probably be best to not display these storages at all if
> they're not supported as an extraction target, but I'm not sure if that
> can actually be done (conveniently) atm. Not a blocker or anything
> though.

sure, that should be possible to filter out somehow

> 
> FYI, even though my development has multiple storage types, only the
> following are shown in that drop down list:
>    - zfs
>    - lvm-thin
>    - directory (the new one, but not "local")
> 
> The only storages with the new "import" content type are the local one
> and the new one I made for testing above, so I'm not sure why the zfs
> pool and lvm thin pool are showing up there.
> 

because they have 'images' enabled, as written above i can see that
i filter those out better

> * Attempted another import, this time just keeping the "Extraction
>    Storage" as is
>    --> Worked quite fast, the VM was online really quick because I had
>        live import on and worked without any issues
> 
> * Attempted another import, but this time from the "local" storage again
>    as I did earlier, but I set the "Extraction Storage" to the new
>    directory storage
>    --> This time importing from "local" worked, somewhat surprisingly

because the extracting storage supported 'images'

> 
> * On the new directory storage, set the content type to "import" only
>    --> The icon changed to the little cloud with the arrow, cute
> 
> * Attempted yet another import, this time from "local" again and setting
>    the new directory storage as "Extraction Storage"
>    --> fails unexpectedly with:
>    > scsi0: test-dir-storage:import/debian-esxi.ova/debian-esxi-0.vmdk is
>    > not on an storage with 'images' content type and no
>    > 'import-extraction-storage' was given."
> 
> So, it's now pretty clear to me that the `disks` content type plays a
> role in terms of whether something can actually be extracted or not
> here. Is there perhaps any way the extraction can be "decoupled" from
> the content type or be done in a different manner? If this is currently
> a limitation (e.g. because of the storage API) then I'd say it's an
> *okay* restriction to have at the moment, but it still feels a little
> unintuitive from a user's perspective.

as said above it's done so in case an image is left over, the user has an 'out' and
is able to see the images in the ui

we could allow it simply for any file based storages, but without the
'images' content type the user wouldn't see left over images and not notice
the used space (i can practically see the forum threads with:
'why is my storage full?')

(it shouldn't happen, but better safe than sorry)

> 
> Code Review
> -----------
> 
> The patches are rather easy to follow and logically structured, so
> that's very nice. There are more comments inline.
> 
> The only other thing I'm not sure about is whether we'd actually want
> the `PVE::GuestImport` module (or namespace) -- is there anything we'd
> like to import in the future? Maybe something like `PVE::Import::Guest`
> would be better, even if there's nothing in `PVE::Import` for the time
> being. Just so that we don't have to touch the module structure again
> soon.

i don't think we'll import anything other than guests on PVE soon,
but i have no hard feelings about the name (i think it was a suggestion
from thomas previously)

> 
> Conclusion
> ----------
> 
> Very solid work, this is pretty great! This will be a lovely feature for
> our users. Apart from the couple little things I encountered there's
> nothing to complain about.
> 
> To sum all of the above up, the only suggestions I have are as follows:
> * I think the error handling regarding the "Extraction Storage" should
>    be more fine-grained

sure that should be possible

> * The `disks` content type shouldn't determine whether an .ova file is
>    able to be extracted or not, I think there should be some other
>    mechanism for that

not quite sure about that (see my notes above)

> * Maybe use `PVE::Import::Guest` instead of `PVE::GuestImport`

idc really, maybe someone else wants to chime in here?

> 
>>
>> I opted to move the OVF.pm to pve-storage, since there is no
>> real other place where we could put it. I put it in a new module
>> 'GuestImport'
>>
>> We now extract the images into either a given target storage or in the
>> import storage in the 'images' dir so accidentally left over images
>> are discoverable by the ui/cli.
>>
>> changes from v3:
>> * fixed dependencies in control file
>> * removed unnecessary use statements
>> * removed unnecessary remove helper
>> * moved 'needs_extract' helper to qemu-server
>> * removed import storage param from PUT call
>> * check down/uploaded ova filename more strictly (same as listing)
>> * improved filepath checking in ovf
>> * forbid importing when extracted image references a base/backing file
>> * instead of trying to manually create a proper filename, use 'alloc' to
>>    create a small (1M) file with the same format and overwrite it with
>>    renaming. this also solves the cluster locking issue
>> * prefer using PVE::Storage functions instead of plugin methods in
>>    ova extraction code
>> * use $vollist for cleaning up extracted images in qemu-server and
>>    add manual cleanup for the success case
>>
>> changes from v2:
>> * use better 'format' values for embedded images (e.g. ova+vmdk)
>> * use this format to decide if images should be extracted
>> * consistent use of the 'safe character' classes when listing
>>    and parsing
>> * also list vmdk/qcow2/raw images in content listing
>>    (this will be useful when we have a gui for the 'import-from'
>>    in the wizard/disk edit for vms)
>> * a few gui adaptions
>>
>>
>> changes from v1:
>> * move ovf code to GuestImport
>> * move extract/checking code to GuestImport
>> * don't return 'image' types from import volumes
>> * use allow 'safe' characters for filenames of ova/ovfs and inside
>> * check for non-regular files (e.g. symlinks) after extraction
>> * add new 'import-extraction-storage' for import
>> * rename panel in gui for directory storages
>> * typo fixes
>> * and probably more, see the individual patches for details
>>
>> pve-storage:
>>
>> Dominik Csapak (12):
>>    copy OVF.pm from qemu-server
>>    plugin: dir: implement import content type
>>    plugin: dir: handle ova files for import
>>    ovf: improve and simplify path checking code
>>    ovf: implement parsing the ostype
>>    ovf: implement parsing out firmware type
>>    ovf: implement rudimentary boot order
>>    ovf: implement parsing nics
>>    api: allow ova upload/download
>>    plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs
>>    add 'import' content type to 'check_volume_access'
>>    plugin: file_size_info: don't ignore base path with whitespace
>>
>>   debian/control                                |   2 +
>>   src/PVE/API2/Storage/Status.pm                |  19 +-
>>   src/PVE/GuestImport.pm                        |  77 ++++
>>   src/PVE/GuestImport/Makefile                  |   3 +
>>   src/PVE/GuestImport/OVF.pm                    | 383 ++++++++++++++++++
>>   src/PVE/Makefile                              |   2 +
>>   src/PVE/Storage.pm                            |  23 +-
>>   src/PVE/Storage/BTRFSPlugin.pm                |   5 +
>>   src/PVE/Storage/CIFSPlugin.pm                 |   6 +-
>>   src/PVE/Storage/CephFSPlugin.pm               |   6 +-
>>   src/PVE/Storage/DirPlugin.pm                  |  52 ++-
>>   src/PVE/Storage/GlusterfsPlugin.pm            |   6 +-
>>   src/PVE/Storage/Makefile                      |   1 +
>>   src/PVE/Storage/NFSPlugin.pm                  |   6 +-
>>   src/PVE/Storage/Plugin.pm                     |  17 +-
>>   src/test/Makefile                             |   5 +-
>>   src/test/ovf_manifests/Win10-Liz-disk1.vmdk   | Bin 0 -> 65536 bytes
>>   src/test/ovf_manifests/Win10-Liz.ovf          | 142 +++++++
>>   .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 143 +++++++
>>   .../ovf_manifests/Win_2008_R2_two-disks.ovf   | 145 +++++++
>>   src/test/ovf_manifests/disk1.vmdk             | Bin 0 -> 65536 bytes
>>   src/test/ovf_manifests/disk2.vmdk             | Bin 0 -> 65536 bytes
>>   src/test/parse_volname_test.pm                |  33 ++
>>   src/test/path_to_volume_id_test.pm            |  21 +
>>   src/test/run_ovf_tests.pl                     |  85 ++++
>>   25 files changed, 1169 insertions(+), 13 deletions(-)
>>   create mode 100644 src/PVE/GuestImport.pm
>>   create mode 100644 src/PVE/GuestImport/Makefile
>>   create mode 100644 src/PVE/GuestImport/OVF.pm
>>   create mode 100644 src/test/ovf_manifests/Win10-Liz-disk1.vmdk
>>   create mode 100755 src/test/ovf_manifests/Win10-Liz.ovf
>>   create mode 100755 src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
>>   create mode 100755 src/test/ovf_manifests/Win_2008_R2_two-disks.ovf
>>   create mode 100644 src/test/ovf_manifests/disk1.vmdk
>>   create mode 100644 src/test/ovf_manifests/disk2.vmdk
>>   create mode 100755 src/test/run_ovf_tests.pl
>>
>> qemu-server:
>>
>> Dominik Csapak (4):
>>    api: delete unused OVF.pm
>>    use OVF from Storage
>>    api: create: implement extracting disks when needed for import-from
>>    api: create: add 'import-extraction-storage' parameter
>>
>>   PVE/API2/Qemu.pm                              |  91 +++++--
>>   PVE/API2/Qemu/Makefile                        |   2 +-
>>   PVE/API2/Qemu/OVF.pm                          |  53 ----
>>   PVE/CLI/qm.pm                                 |   4 +-
>>   PVE/QemuServer.pm                             |  12 +
>>   PVE/QemuServer/Helpers.pm                     |   5 +
>>   PVE/QemuServer/Makefile                       |   1 -
>>   PVE/QemuServer/OVF.pm                         | 242 ------------------
>>   debian/control                                |   2 -
>>   test/Makefile                                 |   5 +-
>>   test/ovf_manifests/Win10-Liz-disk1.vmdk       | Bin 65536 -> 0 bytes
>>   test/ovf_manifests/Win10-Liz.ovf              | 142 ----------
>>   .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 142 ----------
>>   test/ovf_manifests/Win_2008_R2_two-disks.ovf  | 145 -----------
>>   test/ovf_manifests/disk1.vmdk                 | Bin 65536 -> 0 bytes
>>   test/ovf_manifests/disk2.vmdk                 | Bin 65536 -> 0 bytes
>>   test/run_ovf_tests.pl                         |  71 -----
>>   17 files changed, 97 insertions(+), 820 deletions(-)
>>   delete mode 100644 PVE/API2/Qemu/OVF.pm
>>   delete mode 100644 PVE/QemuServer/OVF.pm
>>   delete mode 100644 test/ovf_manifests/Win10-Liz-disk1.vmdk
>>   delete mode 100755 test/ovf_manifests/Win10-Liz.ovf
>>   delete mode 100755 test/ovf_manifests/Win10-Liz_no_default_ns.ovf
>>   delete mode 100755 test/ovf_manifests/Win_2008_R2_two-disks.ovf
>>   delete mode 100644 test/ovf_manifests/disk1.vmdk
>>   delete mode 100644 test/ovf_manifests/disk2.vmdk
>>   delete mode 100755 test/run_ovf_tests.pl
>>
>> pve-manager:
>>
>> Dominik Csapak (9):
>>    ui: fix special 'import' icon for non-esxi storages
>>    ui: guest import: add ova-needs-extracting warning text
>>    ui: enable import content type for relevant storages
>>    ui: enable upload/download/remove buttons for 'import' type storages
>>    ui: disable 'import' button for non importable formats
>>    ui: import: improve rendering of volume names
>>    ui: guest import: add storage selector for ova extraction storage
>>    ui: guest import: change icon/text for non-esxi import storage
>>    ui: import: show size for dir-based storages
>>
>>   www/manager6/Utils.js                    | 11 +++++++++--
>>   www/manager6/form/ContentTypeSelector.js |  2 +-
>>   www/manager6/storage/Browser.js          | 25 ++++++++++++++++++------
>>   www/manager6/storage/CephFSEdit.js       |  2 +-
>>   www/manager6/storage/GlusterFsEdit.js    |  2 +-
>>   www/manager6/window/GuestImport.js       | 24 +++++++++++++++++++++++
>>   www/manager6/window/UploadToStorage.js   |  1 +
>>   7 files changed, 56 insertions(+), 11 deletions(-)
> 
> 
> 
> _______________________________________________
> pve-devel mailing list
> pve-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> 
> 



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text
  2024-06-13 10:39     ` Dominik Csapak
@ 2024-06-13 10:52       ` Fiona Ebner
  2024-06-14  8:37       ` Max Carrara
  1 sibling, 0 replies; 39+ messages in thread
From: Fiona Ebner @ 2024-06-13 10:52 UTC (permalink / raw)
  To: Proxmox VE development discussion, Dominik Csapak, Max Carrara

Am 13.06.24 um 12:39 schrieb Dominik Csapak:
> On 6/12/24 18:02, Max Carrara wrote:
>> On Fri May 24, 2024 at 3:22 PM CEST, Dominik Csapak wrote:
>>> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
>>> ---
>>>   www/manager6/window/GuestImport.js | 1 +
>>>   1 file changed, 1 insertion(+)
>>>
>>> diff --git a/www/manager6/window/GuestImport.js
>>> b/www/manager6/window/GuestImport.js
>>> index 4bedc211..76ba6dc8 100644
>>> --- a/www/manager6/window/GuestImport.js
>>> +++ b/www/manager6/window/GuestImport.js
>>> @@ -937,6 +937,7 @@ Ext.define('PVE.window.GuestImport', {
>>>               gettext('EFI state cannot be imported, you may need to
>>> reconfigure the boot order (see {0})'),
>>>               '<a
>>> href="https://pve.proxmox.com/wiki/OVMF/UEFI_Boot_Entries">OVMF/UEFI
>>> Boot Entries</a>',
>>>           ),
>>> +        'ova-needs-extracting': gettext('Importing from an OVA
>>> requires extra space while extracting the contained disks into the
>>> import or selected storage.'),
>>
>> I'm assuming the string here needs to be in one line because of
>> `gettext`, right? If not, I'd prefer to break it up ;)
> 
> yes, AFAIK we the gettext parsing script does not handle multiline calls
> at the moment
> we could probably check if there is a javascript syntax parsing library
> available
> in perl, or rewrite the script in some other language where such a thing
> exists,
> then it wouldn't be so brittle probably
> 
> but it's not generally such a big issue that we would need to worry
> about it

FYI, there's also the proposal from Maximiliano to use xgettext:
https://lists.proxmox.com/pipermail/pve-devel/2023-December/060970.html


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH qemu-server v4 3/4] api: create: implement extracting disks when needed for import-from
  2024-06-13 10:29     ` Dominik Csapak
@ 2024-06-14  8:36       ` Max Carrara
  0 siblings, 0 replies; 39+ messages in thread
From: Max Carrara @ 2024-06-14  8:36 UTC (permalink / raw)
  To: Dominik Csapak, Proxmox VE development discussion

On Thu Jun 13, 2024 at 12:29 PM CEST, Dominik Csapak wrote:
> On 6/12/24 18:01, Max Carrara wrote:
> > On Fri May 24, 2024 at 3:21 PM CEST, Dominik Csapak wrote:
> >> when 'import-from' contains a disk image that needs extraction
> >> (currently only from an 'ova' archive), do that in 'create_disks'
> >> and overwrite the '$source' volid.
> >>
> >> Collect the names into a 'delete_sources' list, that we use later
> >> to clean it up again (either when we're finished with importing or in an
> >> error case).
> >>
> >> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> >> ---
> >>   PVE/API2/Qemu.pm          | 55 +++++++++++++++++++++++++++++++--------
> >>   PVE/QemuServer.pm         | 12 +++++++++
> >>   PVE/QemuServer/Helpers.pm |  5 ++++
> >>   3 files changed, 61 insertions(+), 11 deletions(-)
> >>
> >> diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
> >> index 2a1d4d79..8c335ac4 100644
> >> --- a/PVE/API2/Qemu.pm
> >> +++ b/PVE/API2/Qemu.pm
> >> @@ -24,6 +24,7 @@ use PVE::JSONSchema qw(get_standard_option);
> >>   use PVE::RESTHandler;
> >>   use PVE::ReplicationConfig;
> >>   use PVE::GuestHelpers qw(assert_tag_permissions);
> >> +use PVE::GuestImport;
> >>   use PVE::QemuConfig;
> >>   use PVE::QemuServer;
> >>   use PVE::QemuServer::Cloudinit;
> >> @@ -159,10 +160,19 @@ my $check_storage_access = sub {
> >>   
> >>   	if (my $src_image = $drive->{'import-from'}) {
> >>   	    my $src_vmid;
> >> -	    if (PVE::Storage::parse_volume_id($src_image, 1)) { # PVE-managed volume
> >> -		(my $vtype, undef, $src_vmid) = PVE::Storage::parse_volname($storecfg, $src_image);
> >> -		raise_param_exc({ $ds => "$src_image has wrong type '$vtype' - not an image" })
> >> -		    if $vtype ne 'images';
> >> +	    if (my ($storeid, $volname) = PVE::Storage::parse_volume_id($src_image, 1)) { # PVE-managed volume
> >> +		my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
> >> +		my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
> >> +		(my $vtype, undef, $src_vmid, undef, undef, undef, my $fmt) = $plugin->parse_volname($volname);
> >> +
> >> +		raise_param_exc({ $ds => "$src_image has wrong type '$vtype' - needs to be 'images' or 'import'" })
> >> +		    if $vtype ne 'images' && $vtype ne 'import';
> > 
> > ... Shouldn't this here be `||` instead of `&&`? According to the error
> > message at least.
>
> no?
>
> we raise the  error if it is not 'images' and not 'import', so it's neither
> making it || would always fail then because images != import and vice versa ;)

Yeah, my bad; sterzy had pointed that out off-list, so that was
brainfart on my side. Disregard my comment :D

>
> (note the operator is 'ne' not 'eq' )
>
> we could make it more like the error message but then it would have to be:
>
> if !($vtype eq 'images' || $vtype eq 'import')
>
> which should be the same what i posted just with more syntax ;)
>
> > 
> >> +
> >> +		if (PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt)) {
> >> +		    raise_param_exc({ $ds => "$src_image is not on an storage with 'images' content type."})
> > 
> > s/an storage/a storage
> > 
> > Also, as I mentioned in another comment, is it maybe possible to display
> > the same name that's used in the UI instead of 'images' or 'import'?
>
> we generally stick to the backend terminology in error messages from the backend AFAIK
> but i'd be willing to change that if we have precendence somewhere for that
>
> these error message also are for the cli though, and there we use also the backend terminology
> for the configs
>
> also we generally try to catch such cases beforehand in the ui (if possible) so the
> user does not see the backend error message at all most of the time

Alright, that's fair! Thanks for elaborating!

>
> > 
> >> +			if !$scfg->{content}->{images};
> >> +		    $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
> >> +		}
> >>   	    }
> >>   
> >>   	    if ($src_vmid) { # might be actively used by VM and will be copied via clone_disk()
> >> @@ -392,16 +402,34 @@ my sub create_disks : prototype($$$$$$$$$$) {
> >>   		$needs_creation = $live_import;
> >>   
> >>   		if (PVE::Storage::parse_volume_id($source, 1)) { # PVE-managed volume
> >> +		    my ($vtype, undef, undef, undef, undef, undef, $fmt)
> >> +			= PVE::Storage::parse_volname($storecfg, $source);
> >> +		    my $needs_extraction = PVE::QemuServer::Helpers::needs_extraction($vtype, $fmt);
> >> +		    if ($needs_extraction) {
> >> +			print "extracting $source\n";
> >> +			my $extracted_volid
> >> +			     = PVE::GuestImport::extract_disk_from_import_file($source, $vmid);
> >> +			print "finished extracting to $extracted_volid\n";
> >> +			push @$vollist, $extracted_volid;
> >> +			$source = $extracted_volid;
> >> +
> >> +			my (undef, undef, undef, $parent)
> >> +			    = PVE::Storage::volume_size_info($storecfg, $source);
> >> +			die "importing from extracted images with backing file ($parent) not allowed\n"
> >> +			    if $parent;
> >> +		    }
> >> +
> >>   		    if ($live_import && $ds ne 'efidisk0') {
> >>   			my $path = PVE::Storage::path($storecfg, $source)
> >>   			    or die "failed to get a path for '$source'\n";
> >> -			$source = $path;
> >> -			($size, my $source_format) = PVE::Storage::file_size_info($source);
> >> -			die "could not get file size of $source\n" if !$size;
> >> +			($size, my $source_format) = PVE::Storage::file_size_info($path);
> >> +			die "could not get file size of $path\n" if !$size;
> >>   			$live_import_mapping->{$ds} = {
> >> -			    path => $source,
> >> +			    path => $path,
> >>   			    format => $source_format,
> >>   			};
> >> +			$live_import_mapping->{$ds}->{'delete-after-finish'} = $source
> >> +			    if $needs_extraction;
> >>   		    } else {
> >>   			my $dest_info = {
> >>   			    vmid => $vmid,
> >> @@ -413,8 +441,14 @@ my sub create_disks : prototype($$$$$$$$$$) {
> >>   			$dest_info->{efisize} = PVE::QemuServer::get_efivars_size($conf, $disk)
> >>   			    if $ds eq 'efidisk0';
> >>   
> >> -			($dst_volid, $size) = eval {
> >> -			    $import_from_volid->($storecfg, $source, $dest_info, $vollist);
> >> +			eval {
> >> +			    ($dst_volid, $size)
> >> +				= $import_from_volid->($storecfg, $source, $dest_info, $vollist);
> >> +
> >> +			    # remove extracted volumes after importing
> >> +			    PVE::Storage::vdisk_free($storecfg, $source) if $needs_extraction;
> >> +			    print "cleaned up extracted image $source\n";
> >> +			    @$vollist = grep { $_ ne $source } @$vollist;
> >>   			};
> >>   			die "cannot import from '$source' - $@" if $@;
> >>   		    }
> >> @@ -1939,7 +1973,6 @@ my $update_vm_api  = sub {
> >>   
> >>   		    assert_scsi_feature_compatibility($opt, $conf, $storecfg, $param->{$opt})
> >>   			if $opt =~ m/^scsi\d+$/;
> >> -
> >>   		    my (undef, $created_opts) = create_disks(
> >>   			$rpcenv,
> >>   			$authuser,
> >> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
> >> index 5df0c96d..76136570 100644
> >> --- a/PVE/QemuServer.pm
> >> +++ b/PVE/QemuServer.pm
> >> @@ -7310,6 +7310,7 @@ sub live_import_from_files {
> >>       my ($mapping, $vmid, $conf, $restore_options) = @_;
> >>   
> >>       my $live_restore_backing = {};
> >> +    my $sources_to_remove = [];
> >>       for my $dev (keys %$mapping) {
> >>   	die "disk not support for live-restoring: '$dev'\n"
> >>   	    if !is_valid_drivename($dev) || $dev =~ /^(?:efidisk|tpmstate)/;
> >> @@ -7330,6 +7331,9 @@ sub live_import_from_files {
> >>   	    . ",read-only=on"
> >>   	    . ",file.driver=file,file.filename=$path"
> >>   	};
> >> +
> >> +	my $source_volid = $info->{'delete-after-finish'};
> >> +	push $sources_to_remove->@*, $source_volid if defined($source_volid);
> >>       };
> >>   
> >>       my $storecfg = PVE::Storage::config();
> >> @@ -7373,6 +7377,14 @@ sub live_import_from_files {
> >>   
> >>       my $err = $@;
> >>   
> >> +    for my $volid ($sources_to_remove->@*) {
> >> +	eval {
> >> +	    PVE::Storage::vdisk_free($storecfg, $volid);
> >> +	    print "cleaned up extracted image $volid\n";
> >> +	};
> >> +	warn "An error occurred while cleaning up source images: $@\n" if $@;
> >> +    }
> >> +
> >>       if ($err) {
> >>   	warn "An error occurred during live-restore: $err\n";
> >>   	_do_vm_stop($storecfg, $vmid, 1, 1, 10, 0, 1);
> >> diff --git a/PVE/QemuServer/Helpers.pm b/PVE/QemuServer/Helpers.pm
> >> index 0afb6317..15e2496c 100644
> >> --- a/PVE/QemuServer/Helpers.pm
> >> +++ b/PVE/QemuServer/Helpers.pm
> >> @@ -225,4 +225,9 @@ sub windows_version {
> >>       return $winversion;
> >>   }
> >>   
> >> +sub needs_extraction {
> >> +    my ($vtype, $fmt) = @_;
> >> +    return $vtype eq 'import' && $fmt =~ m/^ova\+(.*)$/;
> >> +}
> >> +
> >>   1;
> > 
> > 
> > 
> > _______________________________________________
> > pve-devel mailing list
> > pve-devel@lists.proxmox.com
> > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> > 
> > 



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text
  2024-06-13 10:39     ` Dominik Csapak
  2024-06-13 10:52       ` Fiona Ebner
@ 2024-06-14  8:37       ` Max Carrara
  1 sibling, 0 replies; 39+ messages in thread
From: Max Carrara @ 2024-06-14  8:37 UTC (permalink / raw)
  To: Dominik Csapak, Proxmox VE development discussion

On Thu Jun 13, 2024 at 12:39 PM CEST, Dominik Csapak wrote:
> On 6/12/24 18:02, Max Carrara wrote:
> > On Fri May 24, 2024 at 3:22 PM CEST, Dominik Csapak wrote:
> >> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> >> ---
> >>   www/manager6/window/GuestImport.js | 1 +
> >>   1 file changed, 1 insertion(+)
> >>
> >> diff --git a/www/manager6/window/GuestImport.js b/www/manager6/window/GuestImport.js
> >> index 4bedc211..76ba6dc8 100644
> >> --- a/www/manager6/window/GuestImport.js
> >> +++ b/www/manager6/window/GuestImport.js
> >> @@ -937,6 +937,7 @@ Ext.define('PVE.window.GuestImport', {
> >>   		    gettext('EFI state cannot be imported, you may need to reconfigure the boot order (see {0})'),
> >>   		    '<a href="https://pve.proxmox.com/wiki/OVMF/UEFI_Boot_Entries">OVMF/UEFI Boot Entries</a>',
> >>   		),
> >> +		'ova-needs-extracting': gettext('Importing from an OVA requires extra space while extracting the contained disks into the import or selected storage.'),
> > 
> > I'm assuming the string here needs to be in one line because of
> > `gettext`, right? If not, I'd prefer to break it up ;)
>
> yes, AFAIK we the gettext parsing script does not handle multiline calls at the moment
> we could probably check if there is a javascript syntax parsing library available
> in perl, or rewrite the script in some other language where such a thing exists,
> then it wouldn't be so brittle probably
>
> but it's not generally such a big issue that we would need to worry about it

Alright, thanks for the clarification! I agree that it can stay then.

>
> > 
> >>   	    };
> >>               let message = warningsCatalogue[w.type];
> >>   	    if (!w.type || !message) {
> > 
> > 
> > 
> > _______________________________________________
> > pve-devel mailing list
> > pve-devel@lists.proxmox.com
> > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> > 
> > 



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages
  2024-06-13 10:52   ` Dominik Csapak
@ 2024-06-14  8:57     ` Max Carrara
  0 siblings, 0 replies; 39+ messages in thread
From: Max Carrara @ 2024-06-14  8:57 UTC (permalink / raw)
  To: Dominik Csapak, Proxmox VE development discussion

On Thu Jun 13, 2024 at 12:52 PM CEST, Dominik Csapak wrote:
> On 6/12/24 17:56, Max Carrara wrote:
> > On Fri May 24, 2024 at 3:21 PM CEST, Dominik Csapak wrote:
> >> This series enables importing ova/ovf from directory based storages,
> >> inclusive upload/download via the webui (ova only).
> >>
> >> It also improves the ovf importer by parsing the ostype, nics, bootorder
> >> (and firmware from vmware exported files).
> > 
> > Really great work! Built the packages and ran some tests. There are also
> > a couple comments, so see below.
> > 
> > Building
> > --------
> > 
> > * Patches applied cleanly
> > * Had to build the `qemu-server` package on a development VM and build
> >    and install `pve-storage` with the patches installed there first,
> >    because the new `PVE::GuestImport` module couldn't be found
> >    (obviously). Worked fine though.
> > 
> > Testing
> > -------
> > 
> > * Installed all packages on a development VM, encountered no issues
> > 
> > * Checked if new `import` content type shows up only for directory-esque
> >    storage types
> >    --> sure does
> > 
> > * Added `import` as content type to my "local" storage
> > * Checked if "local" storage has new "import" tab
> >    --> it does
> > 
> > * Exported a Debian VM from ESXi and tarballed the .ovf, .mf and .vmdk
> >    to an .ova file
> > * Uploaded the file to the storage
> >    --> succeeded
> > 
> > * Tried to import the VM onto my local lvm thin-pool storage
> >    --> fails, because my "local" storage doesn't have the "Disk Image"
> >        content type
> > 
> > I hadn't expected this to be honest, because I'm not trying to import it
> > to "local" here?
>
> by default the images are extracted onto the import storage, so it
> needs the disk images too in case they're left over, so users
> have an easy way to clean them up.

Ah, I hadn't considered that. That makes more sense, then. IIRC in my
tests there were no disk images left behind, so that's why I was
wondering why the extra content type was necessary.

>
> you can give an explicit extraction storage though (that too
> needs  'images' as content type)
>
> > 
> > * Created a new storage with type "directory" and set its content types
> >    to "import" and "disk image"
> > * Tried to import the VM again, but this time selected my LVM thin pool
> >    as "Extraction Storage" in the dialogue window
> >    --> also fails as expected, because an LVM thin pool is not file-based
> > 
> > A note on the above test: I'd prefer if the error message here was more
> > fine grained - the popup said:
> >> import-extraction-storage: local-lvm does not support 'images' content
> >> type or is not file based.
> > 
> > IMO it would probably be best to not display these storages at all if
> > they're not supported as an extraction target, but I'm not sure if that
> > can actually be done (conveniently) atm. Not a blocker or anything
> > though.
>
> sure, that should be possible to filter out somehow

Sweet! Think that might avoid some noise in the forum as well. ;)

>
> > 
> > FYI, even though my development has multiple storage types, only the
> > following are shown in that drop down list:
> >    - zfs
> >    - lvm-thin
> >    - directory (the new one, but not "local")
> > 
> > The only storages with the new "import" content type are the local one
> > and the new one I made for testing above, so I'm not sure why the zfs
> > pool and lvm thin pool are showing up there.
> > 
>
> because they have 'images' enabled, as written above i can see that
> i filter those out better
>
> > * Attempted another import, this time just keeping the "Extraction
> >    Storage" as is
> >    --> Worked quite fast, the VM was online really quick because I had
> >        live import on and worked without any issues
> > 
> > * Attempted another import, but this time from the "local" storage again
> >    as I did earlier, but I set the "Extraction Storage" to the new
> >    directory storage
> >    --> This time importing from "local" worked, somewhat surprisingly
>
> because the extracting storage supported 'images'
>
> > 
> > * On the new directory storage, set the content type to "import" only
> >    --> The icon changed to the little cloud with the arrow, cute
> > 
> > * Attempted yet another import, this time from "local" again and setting
> >    the new directory storage as "Extraction Storage"
> >    --> fails unexpectedly with:
> >    > scsi0: test-dir-storage:import/debian-esxi.ova/debian-esxi-0.vmdk is
> >    > not on an storage with 'images' content type and no
> >    > 'import-extraction-storage' was given."
> > 
> > So, it's now pretty clear to me that the `disks` content type plays a
> > role in terms of whether something can actually be extracted or not
> > here. Is there perhaps any way the extraction can be "decoupled" from
> > the content type or be done in a different manner? If this is currently
> > a limitation (e.g. because of the storage API) then I'd say it's an
> > *okay* restriction to have at the moment, but it still feels a little
> > unintuitive from a user's perspective.
>
> as said above it's done so in case an image is left over, the user has an 'out' and
> is able to see the images in the ui
>
> we could allow it simply for any file based storages, but without the
> 'images' content type the user wouldn't see left over images and not notice
> the used space (i can practically see the forum threads with:
> 'why is my storage full?')
>
> (it shouldn't happen, but better safe than sorry)

Yeah, I completely get that rationale. In that case I'm fine with
keeping it that way, but I think we should document that at some point,
so users aren't confused about the import / disks content type
"interplay", if that makes sense.

Would it also make sense to extract the disks to /tmp? Since that's
periodically cleaned up by default. Though, that could clog up the root
FS nevertheless ...

>
> > 
> > Code Review
> > -----------
> > 
> > The patches are rather easy to follow and logically structured, so
> > that's very nice. There are more comments inline.
> > 
> > The only other thing I'm not sure about is whether we'd actually want
> > the `PVE::GuestImport` module (or namespace) -- is there anything we'd
> > like to import in the future? Maybe something like `PVE::Import::Guest`
> > would be better, even if there's nothing in `PVE::Import` for the time
> > being. Just so that we don't have to touch the module structure again
> > soon.
>
> i don't think we'll import anything other than guests on PVE soon,
> but i have no hard feelings about the name (i think it was a suggestion
> from thomas previously)
>
> > 
> > Conclusion
> > ----------
> > 
> > Very solid work, this is pretty great! This will be a lovely feature for
> > our users. Apart from the couple little things I encountered there's
> > nothing to complain about.
> > 
> > To sum all of the above up, the only suggestions I have are as follows:
> > * I think the error handling regarding the "Extraction Storage" should
> >    be more fine-grained
>
> sure that should be possible
>
> > * The `disks` content type shouldn't determine whether an .ova file is
> >    able to be extracted or not, I think there should be some other
> >    mechanism for that
>
> not quite sure about that (see my notes above)
>
> > * Maybe use `PVE::Import::Guest` instead of `PVE::GuestImport`
>
> idc really, maybe someone else wants to chime in here?
>
> > 
> >>
> >> I opted to move the OVF.pm to pve-storage, since there is no
> >> real other place where we could put it. I put it in a new module
> >> 'GuestImport'
> >>
> >> We now extract the images into either a given target storage or in the
> >> import storage in the 'images' dir so accidentally left over images
> >> are discoverable by the ui/cli.
> >>
> >> changes from v3:
> >> * fixed dependencies in control file
> >> * removed unnecessary use statements
> >> * removed unnecessary remove helper
> >> * moved 'needs_extract' helper to qemu-server
> >> * removed import storage param from PUT call
> >> * check down/uploaded ova filename more strictly (same as listing)
> >> * improved filepath checking in ovf
> >> * forbid importing when extracted image references a base/backing file
> >> * instead of trying to manually create a proper filename, use 'alloc' to
> >>    create a small (1M) file with the same format and overwrite it with
> >>    renaming. this also solves the cluster locking issue
> >> * prefer using PVE::Storage functions instead of plugin methods in
> >>    ova extraction code
> >> * use $vollist for cleaning up extracted images in qemu-server and
> >>    add manual cleanup for the success case
> >>
> >> changes from v2:
> >> * use better 'format' values for embedded images (e.g. ova+vmdk)
> >> * use this format to decide if images should be extracted
> >> * consistent use of the 'safe character' classes when listing
> >>    and parsing
> >> * also list vmdk/qcow2/raw images in content listing
> >>    (this will be useful when we have a gui for the 'import-from'
> >>    in the wizard/disk edit for vms)
> >> * a few gui adaptions
> >>
> >>
> >> changes from v1:
> >> * move ovf code to GuestImport
> >> * move extract/checking code to GuestImport
> >> * don't return 'image' types from import volumes
> >> * use allow 'safe' characters for filenames of ova/ovfs and inside
> >> * check for non-regular files (e.g. symlinks) after extraction
> >> * add new 'import-extraction-storage' for import
> >> * rename panel in gui for directory storages
> >> * typo fixes
> >> * and probably more, see the individual patches for details
> >>
> >> pve-storage:
> >>
> >> Dominik Csapak (12):
> >>    copy OVF.pm from qemu-server
> >>    plugin: dir: implement import content type
> >>    plugin: dir: handle ova files for import
> >>    ovf: improve and simplify path checking code
> >>    ovf: implement parsing the ostype
> >>    ovf: implement parsing out firmware type
> >>    ovf: implement rudimentary boot order
> >>    ovf: implement parsing nics
> >>    api: allow ova upload/download
> >>    plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs
> >>    add 'import' content type to 'check_volume_access'
> >>    plugin: file_size_info: don't ignore base path with whitespace
> >>
> >>   debian/control                                |   2 +
> >>   src/PVE/API2/Storage/Status.pm                |  19 +-
> >>   src/PVE/GuestImport.pm                        |  77 ++++
> >>   src/PVE/GuestImport/Makefile                  |   3 +
> >>   src/PVE/GuestImport/OVF.pm                    | 383 ++++++++++++++++++
> >>   src/PVE/Makefile                              |   2 +
> >>   src/PVE/Storage.pm                            |  23 +-
> >>   src/PVE/Storage/BTRFSPlugin.pm                |   5 +
> >>   src/PVE/Storage/CIFSPlugin.pm                 |   6 +-
> >>   src/PVE/Storage/CephFSPlugin.pm               |   6 +-
> >>   src/PVE/Storage/DirPlugin.pm                  |  52 ++-
> >>   src/PVE/Storage/GlusterfsPlugin.pm            |   6 +-
> >>   src/PVE/Storage/Makefile                      |   1 +
> >>   src/PVE/Storage/NFSPlugin.pm                  |   6 +-
> >>   src/PVE/Storage/Plugin.pm                     |  17 +-
> >>   src/test/Makefile                             |   5 +-
> >>   src/test/ovf_manifests/Win10-Liz-disk1.vmdk   | Bin 0 -> 65536 bytes
> >>   src/test/ovf_manifests/Win10-Liz.ovf          | 142 +++++++
> >>   .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 143 +++++++
> >>   .../ovf_manifests/Win_2008_R2_two-disks.ovf   | 145 +++++++
> >>   src/test/ovf_manifests/disk1.vmdk             | Bin 0 -> 65536 bytes
> >>   src/test/ovf_manifests/disk2.vmdk             | Bin 0 -> 65536 bytes
> >>   src/test/parse_volname_test.pm                |  33 ++
> >>   src/test/path_to_volume_id_test.pm            |  21 +
> >>   src/test/run_ovf_tests.pl                     |  85 ++++
> >>   25 files changed, 1169 insertions(+), 13 deletions(-)
> >>   create mode 100644 src/PVE/GuestImport.pm
> >>   create mode 100644 src/PVE/GuestImport/Makefile
> >>   create mode 100644 src/PVE/GuestImport/OVF.pm
> >>   create mode 100644 src/test/ovf_manifests/Win10-Liz-disk1.vmdk
> >>   create mode 100755 src/test/ovf_manifests/Win10-Liz.ovf
> >>   create mode 100755 src/test/ovf_manifests/Win10-Liz_no_default_ns.ovf
> >>   create mode 100755 src/test/ovf_manifests/Win_2008_R2_two-disks.ovf
> >>   create mode 100644 src/test/ovf_manifests/disk1.vmdk
> >>   create mode 100644 src/test/ovf_manifests/disk2.vmdk
> >>   create mode 100755 src/test/run_ovf_tests.pl
> >>
> >> qemu-server:
> >>
> >> Dominik Csapak (4):
> >>    api: delete unused OVF.pm
> >>    use OVF from Storage
> >>    api: create: implement extracting disks when needed for import-from
> >>    api: create: add 'import-extraction-storage' parameter
> >>
> >>   PVE/API2/Qemu.pm                              |  91 +++++--
> >>   PVE/API2/Qemu/Makefile                        |   2 +-
> >>   PVE/API2/Qemu/OVF.pm                          |  53 ----
> >>   PVE/CLI/qm.pm                                 |   4 +-
> >>   PVE/QemuServer.pm                             |  12 +
> >>   PVE/QemuServer/Helpers.pm                     |   5 +
> >>   PVE/QemuServer/Makefile                       |   1 -
> >>   PVE/QemuServer/OVF.pm                         | 242 ------------------
> >>   debian/control                                |   2 -
> >>   test/Makefile                                 |   5 +-
> >>   test/ovf_manifests/Win10-Liz-disk1.vmdk       | Bin 65536 -> 0 bytes
> >>   test/ovf_manifests/Win10-Liz.ovf              | 142 ----------
> >>   .../ovf_manifests/Win10-Liz_no_default_ns.ovf | 142 ----------
> >>   test/ovf_manifests/Win_2008_R2_two-disks.ovf  | 145 -----------
> >>   test/ovf_manifests/disk1.vmdk                 | Bin 65536 -> 0 bytes
> >>   test/ovf_manifests/disk2.vmdk                 | Bin 65536 -> 0 bytes
> >>   test/run_ovf_tests.pl                         |  71 -----
> >>   17 files changed, 97 insertions(+), 820 deletions(-)
> >>   delete mode 100644 PVE/API2/Qemu/OVF.pm
> >>   delete mode 100644 PVE/QemuServer/OVF.pm
> >>   delete mode 100644 test/ovf_manifests/Win10-Liz-disk1.vmdk
> >>   delete mode 100755 test/ovf_manifests/Win10-Liz.ovf
> >>   delete mode 100755 test/ovf_manifests/Win10-Liz_no_default_ns.ovf
> >>   delete mode 100755 test/ovf_manifests/Win_2008_R2_two-disks.ovf
> >>   delete mode 100644 test/ovf_manifests/disk1.vmdk
> >>   delete mode 100644 test/ovf_manifests/disk2.vmdk
> >>   delete mode 100755 test/run_ovf_tests.pl
> >>
> >> pve-manager:
> >>
> >> Dominik Csapak (9):
> >>    ui: fix special 'import' icon for non-esxi storages
> >>    ui: guest import: add ova-needs-extracting warning text
> >>    ui: enable import content type for relevant storages
> >>    ui: enable upload/download/remove buttons for 'import' type storages
> >>    ui: disable 'import' button for non importable formats
> >>    ui: import: improve rendering of volume names
> >>    ui: guest import: add storage selector for ova extraction storage
> >>    ui: guest import: change icon/text for non-esxi import storage
> >>    ui: import: show size for dir-based storages
> >>
> >>   www/manager6/Utils.js                    | 11 +++++++++--
> >>   www/manager6/form/ContentTypeSelector.js |  2 +-
> >>   www/manager6/storage/Browser.js          | 25 ++++++++++++++++++------
> >>   www/manager6/storage/CephFSEdit.js       |  2 +-
> >>   www/manager6/storage/GlusterFsEdit.js    |  2 +-
> >>   www/manager6/window/GuestImport.js       | 24 +++++++++++++++++++++++
> >>   www/manager6/window/UploadToStorage.js   |  1 +
> >>   7 files changed, 56 insertions(+), 11 deletions(-)
> > 
> > 
> > 
> > _______________________________________________
> > pve-devel mailing list
> > pve-devel@lists.proxmox.com
> > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> > 
> > 



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2024-06-14  8:57 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-05-24 13:21 [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 01/12] copy OVF.pm from qemu-server Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 02/12] plugin: dir: implement import content type Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 03/12] plugin: dir: handle ova files for import Dominik Csapak
2024-06-12 15:56   ` Max Carrara
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 04/12] ovf: improve and simplify path checking code Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 05/12] ovf: implement parsing the ostype Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 06/12] ovf: implement parsing out firmware type Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 07/12] ovf: implement rudimentary boot order Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 08/12] ovf: implement parsing nics Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 09/12] api: allow ova upload/download Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 10/12] plugin: enable import for nfs/btrfs/cifs/cephfs/glusterfs Dominik Csapak
2024-06-12 15:57   ` Max Carrara
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 11/12] add 'import' content type to 'check_volume_access' Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH storage v4 12/12] plugin: file_size_info: don't ignore base path with whitespace Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 1/4] api: delete unused OVF.pm Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 2/4] use OVF from Storage Dominik Csapak
2024-05-24 13:21 ` [pve-devel] [PATCH qemu-server v4 3/4] api: create: implement extracting disks when needed for import-from Dominik Csapak
2024-06-12 16:01   ` Max Carrara
2024-06-13 10:29     ` Dominik Csapak
2024-06-14  8:36       ` Max Carrara
2024-05-24 13:22 ` [pve-devel] [PATCH qemu-server v4 4/4] api: create: add 'import-extraction-storage' parameter Dominik Csapak
2024-06-12 16:01   ` Max Carrara
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 1/9] ui: fix special 'import' icon for non-esxi storages Dominik Csapak
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 2/9] ui: guest import: add ova-needs-extracting warning text Dominik Csapak
2024-06-12 16:02   ` Max Carrara
2024-06-13 10:39     ` Dominik Csapak
2024-06-13 10:52       ` Fiona Ebner
2024-06-14  8:37       ` Max Carrara
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 3/9] ui: enable import content type for relevant storages Dominik Csapak
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 4/9] ui: enable upload/download/remove buttons for 'import' type storages Dominik Csapak
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 5/9] ui: disable 'import' button for non importable formats Dominik Csapak
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 6/9] ui: import: improve rendering of volume names Dominik Csapak
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 7/9] ui: guest import: add storage selector for ova extraction storage Dominik Csapak
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 8/9] ui: guest import: change icon/text for non-esxi import storage Dominik Csapak
2024-05-24 13:22 ` [pve-devel] [PATCH manager v4 9/9] ui: import: show size for dir-based storages Dominik Csapak
2024-06-12 15:56 ` [pve-devel] [PATCH storage/qemu-server/manager v4] implement ova/ovf import for file based storages Max Carrara
2024-06-13 10:52   ` Dominik Csapak
2024-06-14  8:57     ` Max Carrara

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