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) server-digest SHA256) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id B9F7E62E42 for ; Wed, 28 Oct 2020 14:02:37 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id A663F1F944 for ; Wed, 28 Oct 2020 14:02:07 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id DA0BA1F937 for ; Wed, 28 Oct 2020 14:02:04 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id A1F4145E42 for ; Wed, 28 Oct 2020 14:02:04 +0100 (CET) Date: Wed, 28 Oct 2020 14:01:59 +0100 From: Fabian =?iso-8859-1?q?Gr=FCnbichler?= To: Proxmox VE development discussion References: <20200915113324.313395-1-d.jaeger@proxmox.com> <20200915113324.313395-2-d.jaeger@proxmox.com> In-Reply-To: <20200915113324.313395-2-d.jaeger@proxmox.com> MIME-Version: 1.0 User-Agent: astroid/0.15.0 (https://github.com/astroidmail/astroid) Message-Id: <1603887887.l9mni1b775.astroid@nora.none> Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-SPAM-LEVEL: Spam detection results: 0 AWL 0.027 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches 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. [agent.pm, proxmox.com, qemu.pm, memory.pm, pci.pm, importdisk.pm, cloudinit.pm, ovf.pm, qemuserver.pm, qm.pm, usb.pm, drive.pm] Subject: Re: [pve-devel] [PATCH qemu-server v4 1/2] Move importdisk from qm to API 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: Wed, 28 Oct 2020 13:02:37 -0000 needs a rebase ;) On September 15, 2020 1:33 pm, Dominic J=C3=A4ger wrote: > Required to create a GUI for importdisk. >=20 > Add parameters that enable directly attaching the disk to a bus/device wi= th all > known disk options. This avoids intermediate steps as unused disk. >=20 > We allow different places as source > * Regular VM images on PVE storages (Normal users + root) > * Other disk images on PVE storages (Normal users + root) (they have alre= ady > been displayed in the FileSelector before) > * Any path (root only) >=20 > Using any path for normal users would be security risk. But if you have t= o > move a disk image to a PVE storage you only are not too many steps > * rename image according to PVE schema > * qm rescan > * double click in GUI to attach > away from making the whole importdisk obsolete. >=20 > Enabling arbitrary paths for root additionally makes it unnecessary to mo= ve the > disk image or create an appropriate storage. That means no knowledge abou= t PVE > storage content naming schemes ("why do I have to move it into a images/<= vmid> > subfolder of a directory based storage?") is required. Importing could th= en be > comprised of only two steps: > 1. mount external drive (hopefully most PVE admins can figure this out) > 2. Click through GUI window and insert /mount/externalDrive/exportedFromE= sxi.vmdk >=20 > Uploading disk images to avoid the PVE storage naming knowledge can still= be > added in the future. However, such a function might not be ideal for big = images > because > * Upload via browser might fail easily? > * Potentially copying huge images from server to local to server? >=20 > So having the absolute path as an option between renaming everything manu= ally > and just uploading it in GUI without CLI knowledge looks like a useful ad= dition > to me. >=20 > This patch combines the main part of the previous qm importdisk and do_im= port > into a helper $convert_and_attach in PVE/API2/Qemu.pm to avoid race condi= tions > and potentially duplicating code from update_vm_api into do_import. > Furthermore, the only other place where it was invoked was importovf, whi= ch now > also uses the helper. importovf will be moved to PVE/API2/Qemu.pm, too, s= o > placing the helper somewhere else does not look useful to me. >=20 > Signed-off-by: Dominic J=C3=A4ger > --- > v3->v4: > * More detailed permissions > * Solve race conditions and code reuse questions by completely moving do_= import > to PVE/API2/Qemu.pm; lock the whole procedure > * As I found both discussed approaches > - Image selector (Thomas) > - Paths (Dominik) > useful I included in both. Hope I didn't misunderstand any of you. >=20 >=20 > PVE/API2/Qemu.pm | 184 ++++++++++++++++++++++++++++++++++- > PVE/CLI/qm.pm | 70 ++----------- > PVE/QemuServer.pm | 2 +- > PVE/QemuServer/Drive.pm | 13 +++ > PVE/QemuServer/ImportDisk.pm | 85 ---------------- > PVE/QemuServer/Makefile | 1 - > 6 files changed, 205 insertions(+), 150 deletions(-) > delete mode 100755 PVE/QemuServer/ImportDisk.pm >=20 > diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm > index 8da616a..9aa85b5 100644 > --- a/PVE/API2/Qemu.pm > +++ b/PVE/API2/Qemu.pm > @@ -45,8 +45,6 @@ BEGIN { > } > } > =20 > -use Data::Dumper; # fixme: remove > - > use base qw(PVE::RESTHandler); > =20 > my $opt_force_description =3D "Force physical removal. Without this, we = simple remove the disk from the config file and create an additional config= uration entry called 'unused[n]', which contains the volume ID. Unlink of u= nused[n] always cause physical removal."; > @@ -4265,4 +4263,186 @@ __PACKAGE__->register_method({ > return PVE::QemuServer::Cloudinit::dump_cloudinit_config($conf, $param-= >{vmid}, $param->{type}); > }}); > =20 > +# TODO Make locally scoped when importovf is moved from qm to API / this= package maybe we could have a description of $param here? doing that often helps=20 to show whether this is a good choice ;) e.g., in this case=20 $param->{original_source} is never used.. I would suggest moving the config loading outside and passing the=20 configs to this. importovf will probably hold a lock and load the=20 configs once, so no need to put this inside this shared method. > +our $convert_and_attach_disk =3D sub { > + my ($param) =3D @_; > + my $vm_conf =3D PVE::QemuConfig->load_config($param->{vmid}); > + my $store_conf =3D PVE::Storage::config(); > + PVE::QemuConfig->check_lock($vm_conf) if !$param->{skiplock}; > + 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"; > + } use assert_if_modified, but also move this to the API call where the=20 config is locked+loaded.. > + > + my $msg =3D $param->{device} ? "to $param->{device} on" : 'as unused= disk to'; > + print "Importing disk '$param->{source_as_path}' $msg VM $param->{vm= id}...\n"; this sounds strange, maybe Dylan can contribute a proper English phrasing for this ;) > + > + my $src_size =3D PVE::Storage::file_size_info($param->{source_as_pat= h}); needs check for undef > + my $dst_format =3D PVE::QemuServer::resolve_dst_disk_format( > + $store_conf, $param->{storage}, undef, $param->{format}); isn't this a bit strange? if I request to import as qcow2, but my=20 storage does not support it, I probably want to get an error and not=20 silently be converted to the default format of the storage? > + my $dst_volid =3D PVE::Storage::vdisk_alloc($store_conf, $param->{st= orage}, > + $param->{vmid}, $dst_format, undef, $src_size / 1024); > + > + eval { > + local $SIG{INT} =3D > + local $SIG{TERM} =3D > + local $SIG{QUIT} =3D > + local $SIG{HUP} =3D > + local $SIG{PIPE} =3D sub { die "Interrupted by signal $!\n"; }; > + > + my $zeroinit =3D 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 ($param->{device}) { > + my $device_param =3D $dst_volid; > + $device_param .=3D ",$param->{device_options}" if $param->{device_o= ptions}; > + $update_vm_api->({ > + vmid =3D> $param->{vmid}, > + $param->{device} =3D> $device_param, > + skiplock =3D> $param->{skiplock} || 0, # Avoid uninitialized values > + }, 1); > + } else { > + $param->{device} =3D PVE::QemuConfig->add_unused_volume($vm_conf, $= dst_volid); > + PVE::QemuConfig->write_config($param->{vmid}, $vm_conf); > + } > + }; > + if (my $err =3D $@) { > + 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 =3D> 'importdisk', > + path =3D> '{vmid}/importdisk', > + method =3D> 'POST', > + protected =3D> 1, # for worker upid file > + proxyto =3D> 'node', > + description =3D> "Import an external disk image into a VM. The image= format ". > + "has to be supported by qemu-img.", > + permissions =3D> { > + check =3D> [ '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']], I doubt this is actually what you wanted here (all those permissions=20 combined don't really make sense). Let's see whether we can disentangle it a bit: - we are modifying a VM config (adding a new disk entry or a new unused=20 entry) - this is should require the same permissions as modifying that=20 part of the config, which is probably VM.Config.Disk and=20 Datastore.AllocateSpace - we are allocating a new vdisk volume on a storage (this should require=20 Datastore.Allocatespace) we also potentially read from a storage (which requires a separate=20 check) or from an arbitrary path (which requires yet another check). > + ], > + }, > + parameters =3D> { > + additionalProperties =3D> 0, > + properties =3D> { > + node =3D> get_standard_option('pve-node'), > + vmid =3D> get_standard_option('pve-vmid', > + {completion =3D> \&PVE::QemuServer::complete_vmid}), > + source =3D> { > + description =3D> "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 =3D> 'string', we have a format for something similar (volid or path) already, maybe we=20 can extend it? > + }, > + device =3D> { > + type =3D> 'string', > + description =3D> "Bus/Device type of the new disk (e.g. 'ide0', ". > + "'scsi2'). Will add the image as unused disk if omitted.", > + enum =3D> [PVE::QemuServer::Drive::valid_drive_names()], > + optional =3D> 1, > + }, > + device_options =3D> { > + type =3D> 'string', > + format =3D> 'drive_options', > + description =3D> "Options to set for the new disk ". > + "(e.g. 'discard=3Don,backup=3D0')", > + optional =3D> 1, > + }, > + storage =3D> get_standard_option('pve-storage-id', { > + description =3D> "The storage to which the image will be imported to."= , > + completion =3D> \&PVE::QemuServer::complete_storage, > + }), > + format =3D> { > + type =3D> 'string', > + description =3D> 'Target format.', > + enum =3D> [ 'raw', 'qcow2', 'vmdk' ], > + optional =3D> 1, > + }, > + digest =3D> get_standard_option('pve-config-digest'), > + skiplock =3D> get_standard_option('skiplock'), > + }, > + }, > + returns =3D> { type =3D> 'string'}, > + code =3D> sub { > + my ($param) =3D @_; > + my $rpcenv =3D PVE::RPCEnvironment::get(); > + my $authuser =3D $rpcenv->get_user(); > + > + my $vmid =3D extract_param($param, 'vmid'); > + my $original_source =3D extract_param($param, 'source'); > + my $digest =3D extract_param($param, 'digest'); > + my $device_options =3D extract_param($param, 'device_options'); > + my $device =3D extract_param($param, 'device'); > + # importovf holds a lock itself which would make automatically updating > + # VM configs fail > + my $skiplock =3D extract_param($param, 'skiplock'); > + my $storecfg =3D 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"; > + } should be handled by schema, not manually. > + if ($device && !PVE::QemuServer::is_valid_drivename($device)) { > + die "Invalid device name: $device!"; > + } this should not be possible, since it's an enum and already checked by=20 the API > + if ($device_options && !$device) { > + die "Cannot use --device_options without specifying --device!" > + } this can be encoded in the schema (with 'requires') > + eval { > + PVE::Storage::check_volume_access($rpcenv, $authuser, $storecfg, > + $vmid, $original_source) > + }; so the STORAGE:PATH example from the description is also limited to=20 root@pam > + raise_perm_exc($@) if $@; > + > + # A path is required for $convert_and_attach_disk > + my $volid_as_path =3D eval { # Nonempty iff $original_source is a volid > + PVE::Storage::path($storecfg, $original_source); > + }; > + my $source_as_path =3D $volid_as_path || $original_source ; > + if (!-e $source_as_path) { > + die "Could not import because source '$original_source' does not ex= ist!\n"; > + } > + > + my $worker =3D sub { > + my $dst_volid =3D PVE::QemuConfig->lock_config($vmid, $convert_and_= attach_disk, > + { > + vmid =3D> $vmid, > + original_source =3D> $original_source, > + device =3D> $device, > + device_options =3D> $device_options, > + storage =3D> extract_param($param, 'storage'), > + source_as_path =3D> $source_as_path, > + format =3D> extract_param($param, 'format'), > + skiplock =3D> $skiplock, > + }); > + print "Successfully imported disk '$original_source ' as ". > + "$device: $dst_volid\n"; $device is potentially undef here > + }; > + > + return $rpcenv->fork_worker('importdisk', $vmid, $authuser, $worker); > + }}); > + > 1; > diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm > index 282fa86..f71b3d6 100755 > --- a/PVE/CLI/qm.pm > +++ b/PVE/CLI/qm.pm > @@ -31,7 +31,6 @@ 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; > @@ -445,61 +444,6 @@ __PACKAGE__->register_method ({ > return undef; > }}); > =20 > -__PACKAGE__->register_method ({ > - name =3D> 'importdisk', > - path =3D> 'importdisk', > - method =3D> 'POST', > - description =3D> "Import an external disk image as an unused disk in= a VM. The > - image format has to be supported by qemu-img(1).", > - parameters =3D> { > - additionalProperties =3D> 0, > - properties =3D> { > - vmid =3D> get_standard_option('pve-vmid', {completion =3D> \&PVE::Q= emuServer::complete_vmid}), > - source =3D> { > - description =3D> 'Path to the disk image to import', > - type =3D> 'string', > - optional =3D> 0, > - }, > - storage =3D> get_standard_option('pve-storage-id', { > - description =3D> 'Target storage ID', > - completion =3D> \&PVE::QemuServer::complete_storage, > - optional =3D> 0, > - }), > - format =3D> { > - type =3D> 'string', > - description =3D> 'Target format', > - enum =3D> [ 'raw', 'qcow2', 'vmdk' ], > - optional =3D> 1, > - }, > - }, > - }, > - returns =3D> { type =3D> 'null'}, > - code =3D> sub { > - my ($param) =3D @_; > - > - my $vmid =3D extract_param($param, 'vmid'); > - my $source =3D extract_param($param, 'source'); > - my $storeid =3D extract_param($param, 'storage'); > - my $format =3D extract_param($param, 'format'); > - > - my $vm_conf =3D 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 =3D PVE::Storage::config(); > - PVE::Storage::storage_check_enabled($storecfg, $storeid); > - > - my $target_storage_config =3D 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) =3D PVE::QemuServer::ImportDisk::do_import($sour= ce, $vmid, $storeid, { format =3D> $format }); > - print "Successfully imported disk as '$drive_id:$volid'\n"; > - > - return undef; > - }}); > - > __PACKAGE__->register_method ({ > name =3D> 'terminal', > path =3D> 'terminal', > @@ -640,17 +584,21 @@ __PACKAGE__->register_method ({ > $conf->{cores} =3D $parsed->{qm}->{cores} if defined($parsed->{qm}->{co= res}); > =20 > eval { > - # order matters, as do_import() will load_config() internally > + # order matters, as $convert_and_attach_disk will load_config() int= ernally > $conf->{vmgenid} =3D PVE::QemuServer::generate_uuid(); > $conf->{smbios1} =3D PVE::QemuServer::generate_smbios1_uuid(); > PVE::QemuConfig->write_config($vmid, $conf); > =20 > foreach my $disk (@{ $parsed->{disks} }) { > my ($file, $drive) =3D ($disk->{backing_file}, $disk->{disk_address}); > - PVE::QemuServer::ImportDisk::do_import($file, $vmid, $storeid, { > - drive_name =3D> $drive, > + $PVE::API2::Qemu::convert_and_attach_disk->({ > + node =3D> $nodename, > + vmid =3D> $vmid, > + source_as_path =3D> $file, > + storage =3D> $storeid, > + device =3D> $drive, > format =3D> $format, > - skiplock =3D> 1, > + skiplock =3D> 1, # Required to update VM configs > }); > } > =20 > @@ -984,7 +932,7 @@ our $cmddef =3D { > =20 > terminal =3D> [ __PACKAGE__, 'terminal', ['vmid']], > =20 > - importdisk =3D> [ __PACKAGE__, 'importdisk', ['vmid', 'source', 'sto= rage']], > + importdisk =3D> [ "PVE::API2::Qemu", 'importdisk', ['vmid', 'source'= , 'storage'], { node =3D> $nodename }], > =20 > importovf =3D> [ __PACKAGE__, 'importovf', ['vmid', 'manifest', 'sto= rage']], > =20 > diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm > index 2747c66..715c507 100644 > --- a/PVE/QemuServer.pm > +++ b/PVE/QemuServer.pm > @@ -6613,7 +6613,7 @@ sub qemu_img_convert { > $src_path =3D PVE::Storage::path($storecfg, $src_volid, $snapname); > $src_is_iscsi =3D ($src_path =3D~ m|^iscsi://|); > $cachemode =3D 'none' if $src_scfg->{type} eq 'zfspool'; > - } elsif (-f $src_volid) { > + } elsif (-f $src_volid || -b _) { # -b for LVM images for example this is not really related to this patch, right? > $src_path =3D $src_volid; > if ($src_path =3D~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) { > $src_format =3D $1; > diff --git a/PVE/QemuServer/Drive.pm b/PVE/QemuServer/Drive.pm > index 91c33f8..af52035 100644 > --- a/PVE/QemuServer/Drive.pm > +++ b/PVE/QemuServer/Drive.pm > @@ -308,6 +308,19 @@ my $alldrive_fmt =3D { > %wwn_fmt, > }; > =20 > +my %optional_file_drivedesc_base =3D %drivedesc_base; > +$optional_file_drivedesc_base{file}{optional} =3D 1; > +my $drive_options_fmt =3D { > + %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 =3D { > volume =3D> { alias =3D> 'file' }, > file =3D> { > 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) =3D @_; > - > - my $drive_name =3D extract_param($params, 'drive_name'); > - my $format =3D 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 =3D PVE::Storage::file_size_info($src_path); > - > - # get target format, target image's path, and whether it's possible = to sparseinit > - my $storecfg =3D PVE::Storage::config(); > - my $dst_format =3D PVE::QemuServer::resolve_dst_disk_format($storecf= g, $storage_id, undef, $format); > - > - my $dst_volid =3D PVE::Storage::vdisk_alloc($storecfg, $storage_id, = $vmid, $dst_format, undef, $src_size / 1024); > - > - my $zeroinit =3D PVE::Storage::volume_has_feature($storecfg, 'sparse= init', $dst_volid); > - > - my $create_drive =3D sub { > - my $vm_conf =3D 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 publ= ic interface > - die "cowardly refusing to overwrite existing entry: $drive_name\n" = if $vm_conf->{$drive_name}; > - > - my $modified =3D {}; # record what $option we modify > - $modified->{$drive_name} =3D 1; > - $vm_conf->{pending}->{$drive_name} =3D $dst_volid; > - PVE::QemuConfig->write_config($vmid, $vm_conf); > - > - my $running =3D PVE::QemuServer::check_running($vmid); > - if ($running) { > - my $errors =3D {}; > - 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 =3D PVE::QemuConfig->add_unused_volume($vm_conf, $dst_v= olid); > - PVE::QemuConfig->write_config($vmid, $vm_conf); > - } > - }; > - > - eval { > - # trap interrupts so we have a chance to clean up > - local $SIG{INT} =3D > - local $SIG{TERM} =3D > - local $SIG{QUIT} =3D > - local $SIG{HUP} =3D > - local $SIG{PIPE} =3D sub { die "interrupted by signal $!\n"; }; > - > - PVE::Storage::activate_volumes($storecfg, [$dst_volid]); > - PVE::QemuServer::qemu_img_convert($src_path, $dst_volid, $src_size, und= ef, $zeroinit); > - PVE::Storage::deactivate_volumes($storecfg, [$dst_volid]); > - PVE::QemuConfig->lock_config($vmid, $create_drive); > - }; > - if (my $err =3D $@) { > - 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 fd8cfbb..ecdab56 100644 > --- a/PVE/QemuServer/Makefile > +++ b/PVE/QemuServer/Makefile > @@ -1,7 +1,6 @@ > SOURCES=3DPCI.pm \ > USB.pm \ > Memory.pm \ > - ImportDisk.pm \ > OVF.pm \ > Cloudinit.pm \ > Agent.pm \ > --=20 > 2.20.1 >=20 >=20 > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel >=20 =