[pve-devel] [PATCH qemu-server v3] Prepare API for import GUI

Dominic Jäger d.jaeger at proxmox.com
Fri Dec 18 12:49:28 CET 2020


Move existing import functionality to the API so that it is available for the
GUI.

Signed-off-by: Dominic Jäger <d.jaeger at proxmox.com>
---
I haven't checked everything (e.g. permissions, code style) yet, but
1. a large part of intermediate bloat is away (some may be left)
2. it's rebased
3. so clicking through the GUI works (again)
Therefore I wanted to send a short update before going on holiday.

 PVE/API2/Qemu.pm             | 258 ++++++++++++++++++++++++++++++++++-
 PVE/CLI/qm.pm                | 127 +++--------------
 PVE/QemuServer.pm            |  18 ++-
 PVE/QemuServer/Drive.pm      |  21 +++
 PVE/QemuServer/ImportDisk.pm |  85 ------------
 PVE/QemuServer/Makefile      |   1 -
 PVE/QemuServer/OVF.pm        |  10 +-
 7 files changed, 319 insertions(+), 201 deletions(-)
 delete mode 100755 PVE/QemuServer/ImportDisk.pm

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index e2d2d67..e6856e6 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -45,7 +45,6 @@ BEGIN {
     }
 }
 
-use Data::Dumper; # fixme: remove
 
 use base qw(PVE::RESTHandler);
 
@@ -173,6 +172,28 @@ my $create_disks = sub {
 	    push @$vollist, $volid;
 	    delete $disk->{format}; # no longer needed
 	    $res->{$ds} = PVE::QemuServer::print_drive($disk);
+	} elsif ($disk->{importsource}) {
+	    # must be before $NEW_DISK_RE because $NEW_DISK_RE is matched in imports
+	    # because the "magic" number of the volid is irrelevant and arbitrarily set to 0 so the API allows it
+	    my $volid_as_path = eval { # Nonempty iff $original_source is a volid
+		PVE::Storage::path($storecfg, $disk->{importsource});
+	    };
+	    my $source_as_path = $volid_as_path ||  $disk->{importsource} ;
+	    my $volid = $PVE::API2::Qemu::import_disk->({
+		vmid => $vmid,
+		original_source => $disk->{importsource},
+		device_options => "discard=on",
+		storage => (split(':', $disk->{file}))[0],
+		source_as_path => $source_as_path,
+		format => $disk->{format},
+		skiplock => 1,
+		}
+	    );
+
+	    delete $disk->{importsource};
+	    $disk->{file} = $volid;
+	    push @$vollist, $volid;
+	    $res->{$ds} =  PVE::QemuServer::print_drive($disk);
 	} elsif ($volid =~ $NEW_DISK_RE) {
 	    my ($storeid, $size) = ($2 || $default_storage, $3);
 	    die "no storage ID specified (and no default storage)\n" if !$storeid;
@@ -1281,6 +1302,19 @@ my $update_vm_api  = sub {
 			if defined($conf->{pending}->{$opt});
 
 		    &$create_disks($rpcenv, $authuser, $conf->{pending}, $arch, $storecfg, $vmid, undef, {$opt => $param->{$opt}});
+		} elsif ($param->{$opt} =~ m/importsource/) {
+			my $disk = $param->{$opt};
+			$disk =~ s/importsource=([^,]+),?//;
+			my $path = $1;
+			$disk =~ m/^(.+):0/;
+			my $storage = $1;
+			my $volid = $PVE::API2::Qemu::import_disk->({
+			    vmid => $vmid,
+			    source_as_path => $path,
+			    storage => $storage,
+			});
+
+			$conf->{pending}->{$opt} = $volid;
 		} elsif ($opt =~ m/^serial\d+/) {
 		    if ((!defined($conf->{$opt}) || $conf->{$opt} eq 'socket') && $param->{$opt} eq 'socket') {
 			$rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
@@ -4320,4 +4354,226 @@ __PACKAGE__->register_method({
 	return PVE::QemuServer::Cloudinit::dump_cloudinit_config($conf, $param->{vmid}, $param->{type});
     }});
 
+# TODO Make locally scoped when importovf is moved from qm to API / this package
+#
+# 2-step process:
+#   1. convert is qemu-img convert
+#   2. attach is update_vm_api
+#
+# vmid ... target VM ID
+# source_as_path ... absolute path of the source image (volid must be converted before)
+# device ... device/drive where the image will be attached (ide0, sata2, ...)
+# device_options ... options for attaching the device (discard=on,cache=unsafe, ...)
+# storage ... target storage for the disk image
+# format ... target format for the disk image
+# skiplock ... if skiploc during attach/upate_vm_api,
+our $import_disk = sub {
+    my ($param) = @_;
+    my $vm_conf = PVE::QemuConfig->load_config($param->{vmid});
+    my $store_conf = PVE::Storage::config();
+    PVE::QemuConfig->check_lock($vm_conf) if !$param->{skiplock};
+    if (!$param->{storage}) {
+	die "It is necessary to pass the storage parameter";
+    }
+    if ($param->{device} && $vm_conf->{$param->{device}}) {
+	die "Could not import because device $param->{device} is already in ".
+	"use in VM $param->{vmid}. Choose a different device!\n";
+    }
+    if ($param->{digest} && $param->{digest} ne $vm_conf->{digest}) {
+	die "VM $param->{vmid} config checksum missmatch (file change by other user?)\n";
+    }
+
+    my $msg = $param->{device} ? "to $param->{device} on" : 'as unused disk to';
+    print "Importing disk '$param->{source_as_path}' $msg VM $param->{vmid}...\n";
+
+    my $src_size = PVE::Storage::file_size_info($param->{source_as_path});
+    if (!defined($src_size)) {
+	die "Could not get file size of $param->{source_as_path}";
+    } elsif (!$src_size) {
+	die "Size of file $param->{source_as_path} is 0";
+    } elsif ($src_size==1) {
+	die "Cannot import directory";
+    }
+
+    my $dst_format = PVE::QemuServer::resolve_dst_disk_format(
+	$store_conf, $param->{storage}, undef, $param->{format});
+    my $dst_volid = PVE::Storage::vdisk_alloc($store_conf, $param->{storage},
+	$param->{vmid}, $dst_format, undef, $src_size / 1024);
+
+    eval {
+	local $SIG{INT} =
+	local $SIG{TERM} =
+	local $SIG{QUIT} =
+	local $SIG{HUP} =
+	local $SIG{PIPE} = sub { die "Interrupted by signal $!\n"; };
+
+	my $zeroinit = PVE::Storage::volume_has_feature($store_conf,
+	    'sparseinit', $dst_volid);
+
+	PVE::Storage::activate_volumes($store_conf, [$dst_volid]);
+	PVE::QemuServer::qemu_img_convert($param->{source_as_path}, $dst_volid,
+	$src_size, undef, $zeroinit);
+	PVE::Storage::deactivate_volumes($store_conf, [$dst_volid]);
+
+    };
+    if (my $err = $@) {
+	eval { PVE::Storage::vdisk_free($store_conf, $dst_volid) };
+	warn "Cleanup of $dst_volid failed: $@ \n" if $@;
+
+	die "Importing disk '$param->{source_as_path}' failed: $err\n" if $err;
+    }
+
+    return $dst_volid;
+};
+
+__PACKAGE__->register_method ({
+    name => 'importdisk',
+    path => '{vmid}/importdisk',
+    method => 'POST',
+    protected => 1, # for worker upid file
+    proxyto => 'node',
+    description => "Import an external disk image into a VM. The image format ".
+	"has to be supported by qemu-img.",
+    permissions => {
+	check => [ 'and',
+	    [ 'perm', '/storage/{storage}', ['Datastore.Audit']],
+	    [ 'perm', '/storage/{storage}', ['Datastore.Allocate']],
+	    [ 'perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
+	    [ 'perm', '/storage/{storage}', ['Datastore.AllocateSpace']],
+	    [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
+	    [ 'perm', '/vms/{vmid}', ['VM.Config.Disk']],
+	],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	    vmid => get_standard_option('pve-vmid',
+		{completion => \&PVE::QemuServer::complete_vmid}),
+	    source => {
+		description => "Disk image to import. Can be a volid ".
+		    "(local-lvm:vm-104-disk-0), an image on a PVE storage ".
+		    "(local:104/toImport.raw) or (for root only) an absolute ".
+		    "path on the server.",
+		type => 'string',
+	    },
+	    device => {
+		type => 'string',
+		description => "Bus/Device type of the new disk (e.g. 'ide0', ".
+		    "'scsi2'). Will add the image as unused disk if omitted.",
+		enum => [PVE::QemuServer::Drive::valid_drive_names()],
+		optional => 1,
+	    },
+	    device_options => {
+		type => 'string',
+		format => 'drive_options',
+		description => "Options to set for the new disk ".
+		    "(e.g. 'discard=on,backup=0')",
+		optional => 1,
+	    },
+	    storage => get_standard_option('pve-storage-id', {
+		description => "The storage to which the image will be imported to.",
+		completion => \&PVE::QemuServer::complete_storage,
+	    }),
+	    format => {
+		type => 'string',
+		description => 'Target format.',
+		enum => [ 'raw', 'qcow2', 'vmdk' ],
+		optional => 1,
+	    },
+	    digest => get_standard_option('pve-config-digest'),
+	    skiplock => get_standard_option('skiplock'),
+	},
+    },
+    returns => { type => 'null'},
+    code => sub {
+	my ($param) = @_;
+	my $rpcenv = PVE::RPCEnvironment::get();
+	my $authuser = $rpcenv->get_user();
+
+	my $vmid = extract_param($param, 'vmid');
+	my $original_source = extract_param($param, 'source');
+	my $digest = extract_param($param, 'digest');
+	my $device_options = extract_param($param, 'device_options');
+	my $device = extract_param($param, 'device');
+	# importovf holds a lock itself which would make automatically updating
+	# VM configs fail
+	my $skiplock = extract_param($param, 'skiplock');
+	my $storecfg = PVE::Storage::config();
+
+	if ($skiplock && $authuser ne 'root at pam') {
+	    raise_perm_exc("Only root may use skiplock.");
+	}
+	if ($original_source eq "") {
+	    die "Could not import because source parameter is an empty string!\n";
+	}
+	if ($device && !PVE::QemuServer::is_valid_drivename($device)) {
+	    die "Invalid device name: $device!";
+	}
+	if ($device_options && !$device) {
+	    die "Cannot use --device_options without specifying --device!"
+	}
+	
+	if ($original_source =~ m/^http/) {
+		die "implement me";
+	#     my $tmpPath = '/tmp';
+	#     PVE::Tools::run_command(['/usr/bin/wget', $original_source, '-P', $tmpPath]);
+	#     $original_source =~ m!([^/]+)$!;
+	#     my $filename = $tmpPath . '/' . $1;
+	#     my $extractDir = $tmpPath .'/' . 'pve_importing';
+	#     if ($filename =~ m/.zip$/) {
+	# 	PVE::Tools::run_command(['/usr/bin/unzip', $filename, '-d', $extractDir], outfunc => sub {
+	# 	    my $line = shift;
+	# 	    if ($line =~ m!\s*extracting:\s*(\S+)\s*$!) {
+	# 		my $extracted_file = $1;
+	# 		$original_source = $extracted_file; # only one file for the moment
+	# 	    }
+
+	# 	});
+	#     } else {
+	# 	die "Can only import .zip files from URLs";
+	#     }
+	} else {
+	    eval {
+	    PVE::Storage::check_volume_access($rpcenv, $authuser, $storecfg,
+		$vmid, $original_source)
+	    };
+	    raise_perm_exc($@) if $@;
+	}
+
+	# A path is required for $import_disk
+	my $volid_as_path = eval { # Nonempty iff $original_source is a volid
+	    PVE::Storage::path($storecfg, $original_source);
+	};
+	my $source_as_path = $volid_as_path || $original_source ;
+	if (!-e $source_as_path) {
+	    die "Could not import because source '$original_source' does not exist!\n";
+	}
+
+	my $storeid = extract_param($param, 'storage');
+	my $format = extract_param($param, 'format');
+	my $conf = PVE::QemuConfig->load_config($vmid);
+
+	my $volid = "${storeid}:0";
+		if ($device_options) {
+			$volid .= ",${device_options}";
+		}
+		$volid .= ",importsource=${source_as_path}";
+	if ($device) {
+		$update_vm_api->({
+			node => "dev",
+			vmid => $vmid,
+			$device => $volid,
+		});
+	} else {
+		$device = PVE::QemuConfig->add_unused_volume($conf, $volid);
+		$update_vm_api->({
+			node => "dev",
+			vmid => $vmid,
+			$device => $volid,
+		});
+	}
+	return;
+    }});
+
 1;
diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm
index b9b6051..56e57ea 100755
--- a/PVE/CLI/qm.pm
+++ b/PVE/CLI/qm.pm
@@ -27,11 +27,12 @@ use PVE::Tools qw(extract_param);
 
 use PVE::API2::Qemu::Agent;
 use PVE::API2::Qemu;
+use PVE::API2::Nodes;
+use PVE::Storage::Plugin;
 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;
@@ -440,61 +441,6 @@ __PACKAGE__->register_method ({
 	return;
     }});
 
-__PACKAGE__->register_method ({
-    name => 'importdisk',
-    path => 'importdisk',
-    method => 'POST',
-    description => "Import an external disk image as an unused disk in a VM. The
- image format has to be supported by qemu-img(1).",
-    parameters => {
-	additionalProperties => 0,
-	properties => {
-	    vmid => get_standard_option('pve-vmid', {completion => \&PVE::QemuServer::complete_vmid}),
-	    source => {
-		description => 'Path to the disk image to import',
-		type => 'string',
-		optional => 0,
-	    },
-            storage => get_standard_option('pve-storage-id', {
-		description => 'Target storage ID',
-		completion => \&PVE::QemuServer::complete_storage,
-		optional => 0,
-            }),
-	    format => {
-		type => 'string',
-		description => 'Target format',
-		enum => [ 'raw', 'qcow2', 'vmdk' ],
-		optional => 1,
-	    },
-	},
-    },
-    returns => { type => 'null'},
-    code => sub {
-	my ($param) = @_;
-
-	my $vmid = extract_param($param, 'vmid');
-	my $source = extract_param($param, 'source');
-	my $storeid = extract_param($param, 'storage');
-	my $format = extract_param($param, 'format');
-
-	my $vm_conf = PVE::QemuConfig->load_config($vmid);
-	PVE::QemuConfig->check_lock($vm_conf);
-	die "$source: non-existent or non-regular file\n" if (! -f $source);
-
-	my $storecfg = PVE::Storage::config();
-	PVE::Storage::storage_check_enabled($storecfg, $storeid);
-
-	my $target_storage_config =  PVE::Storage::storage_config($storecfg, $storeid);
-	die "storage $storeid does not support vm images\n"
-	    if !$target_storage_config->{content}->{images};
-
-	print "importing disk '$source' to VM $vmid ...\n";
-	my ($drive_id, $volid) = PVE::QemuServer::ImportDisk::do_import($source, $vmid, $storeid, { format => $format });
-	print "Successfully imported disk as '$drive_id:$volid'\n";
-
-	return;
-    }});
-
 __PACKAGE__->register_method ({
     name => 'terminal',
     path => 'terminal',
@@ -612,63 +558,26 @@ __PACKAGE__->register_method ({
 	my $format = PVE::Tools::extract_param($param, 'format');
 	my $dryrun = PVE::Tools::extract_param($param, 'dryrun');
 
-	die "$ovf_file: non-existent or non-regular file\n" if (! -f $ovf_file);
-	my $storecfg = PVE::Storage::config();
-	PVE::Storage::storage_check_enabled($storecfg, $storeid);
-
-	my $parsed = PVE::QemuServer::OVF::parse_ovf($ovf_file);
-
-	if ($dryrun) {
-	    print to_json($parsed, { pretty => 1, canonical => 1});
-	    return;
+	my $all_storages = PVE::Storage::config();
+	PVE::Storage::storage_check_enabled($all_storages, $storeid);
+	if ($format) {
+		my $target_storage_config = PVE::Storage::storage_config($all_storages, $storeid);
+		my (undef, $valid_formats) = PVE::Storage::Plugin::default_format($target_storage_config);
+		if (!grep( /^$format$/, @$valid_formats)) {
+			die "Format $format is not supported in storage $storeid";
+		}
 	}
 
-	eval { PVE::QemuConfig->create_and_lock_config($vmid) };
-	die "Reserving empty config for OVF import to VM $vmid failed: $@" if $@;
-
-	my $conf = PVE::QemuConfig->load_config($vmid);
-	die "Internal error: Expected 'create' lock in config of VM $vmid!"
-	    if !PVE::QemuConfig->has_lock($conf, "create");
-
-	$conf->{name} = $parsed->{qm}->{name} if defined($parsed->{qm}->{name});
-	$conf->{memory} = $parsed->{qm}->{memory} if defined($parsed->{qm}->{memory});
-	$conf->{cores} = $parsed->{qm}->{cores} if defined($parsed->{qm}->{cores});
-
-	eval {
-	    # order matters, as do_import() will load_config() internally
-	    $conf->{vmgenid} = PVE::QemuServer::generate_uuid();
-	    $conf->{smbios1} = PVE::QemuServer::generate_smbios1_uuid();
-	    PVE::QemuConfig->write_config($vmid, $conf);
-
-	    foreach my $disk (@{ $parsed->{disks} }) {
-		my ($file, $drive) = ($disk->{backing_file}, $disk->{disk_address});
-		PVE::QemuServer::ImportDisk::do_import($file, $vmid, $storeid, {
-		    drive_name => $drive,
-		    format => $format,
-		    skiplock => 1,
-		});
+	my $parsed = PVE::API2::Nodes::Nodeinfo->readovf({node=>"dev", manifest=> $ovf_file});
+	delete $parsed->{digest};
+	foreach my $key (keys %$parsed) {
+	    if (PVE::QemuServer::is_valid_drivename($key)) {
+		$parsed->{$key} = "$storeid:0,$parsed->{$key}";
 	    }
-
-	    # reload after disks entries have been created
-	    $conf = PVE::QemuConfig->load_config($vmid);
-	    my $devs = PVE::QemuServer::get_default_bootdevices($conf);
-	    $conf->{boot} = PVE::QemuServer::print_bootorder($devs);
-	    PVE::QemuConfig->write_config($vmid, $conf);
-	};
-
-	my $err = $@;
-	if ($err) {
-	    my $skiplock = 1;
-	    # eval for additional safety in error path
-	    eval { PVE::QemuServer::destroy_vm($storecfg, $vmid, $skiplock) };
-	    warn "Could not destroy VM $vmid: $@" if "$@";
-	    die "import failed - $err";
 	}
-
-	PVE::QemuConfig->remove_lock($vmid, "create");
-
+	my $config = {%$parsed, node=>"dev", vmid=>$vmid};
+	PVE::API2::Qemu->create_vm($config);
 	return;
-
     }
 });
 
@@ -979,7 +888,7 @@ our $cmddef = {
 
     terminal => [ __PACKAGE__, 'terminal', ['vmid']],
 
-    importdisk => [ __PACKAGE__, 'importdisk', ['vmid', 'source', 'storage']],
+    importdisk => [ "PVE::API2::Qemu", 'importdisk', ['vmid', 'source', 'storage'], { node => $nodename }],
 
     importovf => [ __PACKAGE__, 'importovf', ['vmid', 'manifest', 'storage']],
 
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 43b11c3..02202b4 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -998,6 +998,22 @@ sub verify_volume_id_or_qm_path {
     return $volid;
 }
 
+PVE::JSONSchema::register_format('pve-volume-id-or-absolute-path', \&verify_volume_id_or_absolute_path);
+sub verify_volume_id_or_absolute_path {
+    my ($volid, $noerr) = @_;
+
+    if ($volid =~ m|^/|) {
+	return $volid;
+    }
+
+    $volid = eval { PVE::JSONSchema::check_format('pve-volume-id', $volid, '') };
+    if ($@) {
+	return undef if $noerr;
+	die $@;
+    }
+    return $volid;
+}
+
 my $usb_fmt = {
     host => {
 	default_key => 1,
@@ -6658,7 +6674,7 @@ sub qemu_img_convert {
 	$src_path = PVE::Storage::path($storecfg, $src_volid, $snapname);
 	$src_is_iscsi = ($src_path =~ m|^iscsi://|);
 	$cachemode = 'none' if $src_scfg->{type} eq 'zfspool';
-    } elsif (-f $src_volid) {
+    } elsif (-f $src_volid || -b _) { # -b for LVM images for example
 	$src_path = $src_volid;
 	if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
 	    $src_format = $1;
diff --git a/PVE/QemuServer/Drive.pm b/PVE/QemuServer/Drive.pm
index d560937..5850e92 100644
--- a/PVE/QemuServer/Drive.pm
+++ b/PVE/QemuServer/Drive.pm
@@ -145,6 +145,13 @@ my %drivedesc_base = (
 	verbose_description => "Mark this locally-managed volume as available on all nodes.\n\nWARNING: This option does not share the volume automatically, it assumes it is shared already!",
 	optional => 1,
 	default => 0,
+    },
+    importsource => {
+	type => 'string',
+	format => 'pve-volume-id-or-absolute-path',
+	format_description => 'Absolute path or volid',
+	description => 'Source to import the disk',
+	optional => 1,
     }
 );
 
@@ -308,6 +315,19 @@ my $alldrive_fmt = {
     %wwn_fmt,
 };
 
+my %optional_file_drivedesc_base = %drivedesc_base;
+$optional_file_drivedesc_base{file}{optional} = 1;
+my $drive_options_fmt = {
+    %optional_file_drivedesc_base,
+    %iothread_fmt,
+    %model_fmt,
+    %queues_fmt,
+    %scsiblock_fmt,
+    %ssd_fmt,
+    %wwn_fmt,
+};
+PVE::JSONSchema::register_format('drive_options', $drive_options_fmt);
+
 my $efidisk_fmt = {
     volume => { alias => 'file' },
     file => {
@@ -435,6 +455,7 @@ sub parse_drive {
 	warn "invalid drive key: $key\n";
 	return;
     }
+	use Data::Dumper;
 
     my $desc = $drivedesc_hash->{$key}->{format};
     my $res = eval { PVE::JSONSchema::parse_property_string($desc, $data) };
diff --git a/PVE/QemuServer/ImportDisk.pm b/PVE/QemuServer/ImportDisk.pm
deleted file mode 100755
index 51ad52e..0000000
--- a/PVE/QemuServer/ImportDisk.pm
+++ /dev/null
@@ -1,85 +0,0 @@
-package PVE::QemuServer::ImportDisk;
-
-use strict;
-use warnings;
-
-use PVE::Storage;
-use PVE::QemuServer;
-use PVE::Tools qw(run_command extract_param);
-
-# imports an external disk image to an existing VM
-# and creates by default a drive entry unused[n] pointing to the created volume
-# $params->{drive_name} may be used to specify ide0, scsi1, etc ...
-# $params->{format} may be used to specify qcow2, raw, etc ...
-sub do_import {
-    my ($src_path, $vmid, $storage_id, $params) = @_;
-
-    my $drive_name = extract_param($params, 'drive_name');
-    my $format = extract_param($params, 'format');
-    if ($drive_name && !(PVE::QemuServer::is_valid_drivename($drive_name))) {
-	die "invalid drive name: $drive_name\n";
-    }
-
-    # get the needed size from  source disk
-    my $src_size = PVE::Storage::file_size_info($src_path);
-
-    # get target format, target image's path, and whether it's possible to sparseinit
-    my $storecfg = PVE::Storage::config();
-    my $dst_format = PVE::QemuServer::resolve_dst_disk_format($storecfg, $storage_id, undef, $format);
-
-    my $dst_volid = PVE::Storage::vdisk_alloc($storecfg, $storage_id, $vmid, $dst_format, undef, $src_size / 1024);
-
-    my $zeroinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $dst_volid);
-
-    my $create_drive = sub {
-	my $vm_conf = PVE::QemuConfig->load_config($vmid);
-	if (!$params->{skiplock}) {
-	    PVE::QemuConfig->check_lock($vm_conf);
-	}
-
-	if ($drive_name) {
-	    # should never happen as setting $drive_name is not exposed to public interface
-	    die "cowardly refusing to overwrite existing entry: $drive_name\n" if $vm_conf->{$drive_name};
-
-	    my $modified = {}; # record what $option we modify
-	    $modified->{$drive_name} = 1;
-	    $vm_conf->{pending}->{$drive_name} = $dst_volid;
-	    PVE::QemuConfig->write_config($vmid, $vm_conf);
-
-	    my $running = PVE::QemuServer::check_running($vmid);
-	    if ($running) {
-		my $errors = {};
-		PVE::QemuServer::vmconfig_hotplug_pending($vmid, $vm_conf, $storecfg, $modified, $errors);
-		warn "hotplugging imported disk '$_' failed: $errors->{$_}\n" for keys %$errors;
-	    } else {
-		PVE::QemuServer::vmconfig_apply_pending($vmid, $vm_conf, $storecfg);
-	    }
-	} else {
-	    $drive_name = PVE::QemuConfig->add_unused_volume($vm_conf, $dst_volid);
-	    PVE::QemuConfig->write_config($vmid, $vm_conf);
-	}
-    };
-
-    eval {
-	# trap interrupts so we have a chance to clean up
-	local $SIG{INT} =
-	    local $SIG{TERM} =
-	    local $SIG{QUIT} =
-	    local $SIG{HUP} =
-	    local $SIG{PIPE} = sub { die "interrupted by signal $!\n"; };
-
-	PVE::Storage::activate_volumes($storecfg, [$dst_volid]);
-	PVE::QemuServer::qemu_img_convert($src_path, $dst_volid, $src_size, undef, $zeroinit);
-	PVE::Storage::deactivate_volumes($storecfg, [$dst_volid]);
-	PVE::QemuConfig->lock_config($vmid, $create_drive);
-    };
-    if (my $err = $@) {
-	eval { PVE::Storage::vdisk_free($storecfg, $dst_volid) };
-	warn "cleanup of $dst_volid failed: $@\n" if $@;
-	die $err;
-    }
-
-    return ($drive_name, $dst_volid);
-}
-
-1;
diff --git a/PVE/QemuServer/Makefile b/PVE/QemuServer/Makefile
index e4ed184..7a8a38f 100644
--- a/PVE/QemuServer/Makefile
+++ b/PVE/QemuServer/Makefile
@@ -1,7 +1,6 @@
 SOURCES=PCI.pm		\
 	USB.pm		\
 	Memory.pm	\
-	ImportDisk.pm	\
 	OVF.pm		\
 	Cloudinit.pm	\
 	Agent.pm	\
diff --git a/PVE/QemuServer/OVF.pm b/PVE/QemuServer/OVF.pm
index c76c199..36b7fff 100644
--- a/PVE/QemuServer/OVF.pm
+++ b/PVE/QemuServer/OVF.pm
@@ -87,7 +87,7 @@ sub id_to_pve {
 
 # returns two references, $qm which holds qm.conf style key/values, and \@disks
 sub parse_ovf {
-    my ($ovf, $debug) = @_;
+    my ($ovf, $debug, $ignore_size) = @_;
 
     my $dom = XML::LibXML->load_xml(location => $ovf, no_blanks => 1);
 
@@ -220,9 +220,11 @@ ovf:Item[rasd:InstanceID='%s']/rasd:ResourceType", $controller_id);
 	    die "error parsing $filepath, file seems not to exist at $backing_file_path\n";
 	}
 
-	my $virtual_size;
-	if ( !($virtual_size = PVE::Storage::file_size_info($backing_file_path)) ) {
-	    die "error parsing $backing_file_path, size seems to be $virtual_size\n";
+	my $virtual_size = 0;
+	if (!$ignore_size) { # Not possible if manifest is uploaded in web gui
+	    if ( !($virtual_size = PVE::Storage::file_size_info($backing_file_path)) ) {
+		die "error parsing $backing_file_path: Could not get file size info: $@\n";
+	    }
 	}
 
 	$pve_disk = {
-- 
2.20.1





More information about the pve-devel mailing list