From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 78A0B61F29 for ; Fri, 18 Dec 2020 12:50:52 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 72C278391 for ; Fri, 18 Dec 2020 12:50:22 +0100 (CET) Received: from dev.dominic.proxmox.com (212-186-127-178.static.upcbusiness.at [212.186.127.178]) by firstgate.proxmox.com (Proxmox) with ESMTP id 2AB8F8378 for ; Fri, 18 Dec 2020 12:50:19 +0100 (CET) Received: by dev.dominic.proxmox.com (Postfix, from userid 0) id ECBCC21079; Fri, 18 Dec 2020 12:50:03 +0100 (CET) From: =?UTF-8?q?Dominic=20J=C3=A4ger?= To: pve-devel@lists.proxmox.com Date: Fri, 18 Dec 2020 12:49:28 +0100 Message-Id: <20201218114929.94186-1-d.jaeger@proxmox.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 1 AWL -0.334 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods KHOP_HELO_FCRDNS 0.274 Relay HELO differs from its IP's reverse DNS NO_DNS_FOR_FROM 0.379 Envelope sender has no MX or A DNS records SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [memory.pm, qm.pm, drive.pm, usb.pm, qemu.pm, qemuserver.pm, cloudinit.pm, pci.pm, ovf.pm, agent.pm, importdisk.pm] Subject: [pve-devel] [PATCH qemu-server v3] Prepare API for import GUI X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 18 Dec 2020 11:50:52 -0000 Move existing import functionality to the API so that it is available for the GUI. Signed-off-by: Dominic Jäger --- 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@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