[pve-devel] [PATCH qemu-server v3 3/4] api: create: implement extracting disks when needed for import-from

Fabian Grünbichler f.gruenbichler at proxmox.com
Wed May 22 14:55:46 CEST 2024


On April 29, 2024 1:21 pm, 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 at proxmox.com>
> ---
>  PVE/API2/Qemu.pm          | 44 ++++++++++++++++++++++++++++++---------
>  PVE/QemuServer.pm         |  5 ++++-
>  PVE/QemuServer/Helpers.pm | 10 +++++++++
>  3 files changed, 48 insertions(+), 11 deletions(-)
> 
> diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
> index 2a349c8c..d32967dc 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) = $plugin->parse_volname($volname);

please use PVE::Storage instead!

> +
> +		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::GuestImport::copy_needs_extraction($src_image)) {

like noted in the patch introducing that helper, it could just be
inlined here..

> +		    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()
> @@ -335,6 +345,7 @@ my sub create_disks : prototype($$$$$$$$$$) {
>      my $res = {};
>  
>      my $live_import_mapping = {};
> +    my $delete_sources = [];

we already have a list of created volumes here that are cleaned up on
error ($vollist), so this is just to also clean them up after importing
if that worked? and then, it's basically just for live importing (since
for non-live imports, we can just free the volume right after the import
was successful?)? but live imports already have their own hash anyway
($live_import_mapping), we could just annotate the volume there?

>  
>      my $code = sub {
>  	my ($ds, $disk) = @_;
> @@ -392,6 +403,12 @@ my sub create_disks : prototype($$$$$$$$$$) {
>  		$needs_creation = $live_import;
>  
>  		if (PVE::Storage::parse_volume_id($source, 1)) { # PVE-managed volume
> +		    if (PVE::GuestImport::copy_needs_extraction($source)) { # needs extraction beforehand
> +			print "extracting $source\n";
> +			$source = PVE::GuestImport::extract_disk_from_import_file($source, $vmid);
> +			print "finished extracting to $source\n";

this is a bit hard to follow, it might be more readable to do

my $extracted_volid = ..;
$source = $extracted_volid;

even if the end result is the same, it makes it much more explicit what
is happening here with $source?

> +			push @$delete_sources, $source;

this could just push to $vollist I think..

> +		    }
>  		    if ($live_import && $ds ne 'efidisk0') {
>  			my $path = PVE::Storage::path($storecfg, $source)
>  			    or die "failed to get a path for '$source'\n";
> @@ -514,13 +531,14 @@ my sub create_disks : prototype($$$$$$$$$$) {
>  	    eval { PVE::Storage::vdisk_free($storecfg, $volid); };
>  	    warn $@ if $@;
>  	}
> +	PVE::QemuServer::Helpers::cleanup_extracted_images($delete_sources);

then this would not be needed, since we cleanup all the freshly
allocated volumes above anyway..

>  	die $err;
>      }
>  
>      # don't return empty import mappings
>      $live_import_mapping = undef if !%$live_import_mapping;
>  
> -    return ($vollist, $res, $live_import_mapping);
> +    return ($vollist, $res, $live_import_mapping, $delete_sources);

this can then be dropped as well..

>  };
>  
>  my $check_cpu_model_access = sub {
> @@ -1079,6 +1097,7 @@ __PACKAGE__->register_method({
>  
>  	my $createfn = sub {
>  	    my $live_import_mapping = {};
> +	    my $delete_sources = [];

so can this

>  
>  	    # ensure no old replication state are exists
>  	    PVE::ReplicationState::delete_guest_states($vmid);
> @@ -1096,7 +1115,7 @@ __PACKAGE__->register_method({
>  
>  		my $vollist = [];
>  		eval {
> -		    ($vollist, my $created_opts, $live_import_mapping) = create_disks(
> +		    ($vollist, my $created_opts, $live_import_mapping, $delete_sources) = create_disks(

and this

>  			$rpcenv,
>  			$authuser,
>  			$conf,
> @@ -1149,6 +1168,7 @@ __PACKAGE__->register_method({
>  			eval { PVE::Storage::vdisk_free($storecfg, $volid); };
>  			warn $@ if $@;
>  		    }
> +		    PVE::QemuServer::Helpers::cleanup_extracted_images($delete_sources);

and this :)

>  		    die "$emsg $err";
>  		}
>  
> @@ -1165,7 +1185,7 @@ __PACKAGE__->register_method({
>  		warn $@ if $@;
>  		return;
>  	    } else {
> -		return $live_import_mapping;
> +		return ($live_import_mapping, $delete_sources);

as well as this

>  	    }
>  	};
>  
> @@ -1192,7 +1212,7 @@ __PACKAGE__->register_method({
>  	    $code = sub {
>  		# If a live import was requested the create function returns
>  		# the mapping for the startup.
> -		my $live_import_mapping = eval { $createfn->() };
> +		my ($live_import_mapping, $delete_sources) = eval { $createfn->() };

this

>  		if (my $err = $@) {
>  		    eval {
>  			my $conffile = PVE::QemuConfig->config_file($vmid);
> @@ -1214,7 +1234,10 @@ __PACKAGE__->register_method({
>  			$vmid,
>  			$conf,
>  			$import_options,
> +			$delete_sources,

this

>  		    );
> +		} else {
> +		    PVE::QemuServer::Helpers::cleanup_extracted_images($delete_sources);

and this?

>  		}
>  	    };
>  	}
> @@ -1939,8 +1962,7 @@ 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(
> +		    my (undef, $created_opts, undef, $delete_sources) = create_disks(

not needed either

>  			$rpcenv,
>  			$authuser,
>  			$conf,
> @@ -1954,6 +1976,8 @@ my $update_vm_api  = sub {
>  		    );
>  		    $conf->{pending}->{$_} = $created_opts->{$_} for keys $created_opts->%*;
>  
> +		    PVE::QemuServer::Helpers::cleanup_extracted_images($delete_sources);

same here

> +
>  		    # default legacy boot order implies all cdroms anyway
>  		    if (@bootorder) {
>  			# append new CD drives to bootorder to mark them bootable
> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
> index 82e7d6a6..4bd0ae85 100644
> --- a/PVE/QemuServer.pm
> +++ b/PVE/QemuServer.pm
> @@ -7303,7 +7303,7 @@ sub pbs_live_restore {
>  # therefore already handled in the `$create_disks()` call happening in the
>  # `create` api call
>  sub live_import_from_files {
> -    my ($mapping, $vmid, $conf, $restore_options) = @_;
> +    my ($mapping, $vmid, $conf, $restore_options, $delete_sources) = @_;

here

>  
>      my $live_restore_backing = {};
>      for my $dev (keys %$mapping) {
> @@ -7364,6 +7364,8 @@ sub live_import_from_files {
>  	    mon_cmd($vmid, 'blockdev-del', 'node-name' => "drive-$ds-restore");
>  	}
>  
> +	PVE::QemuServer::Helpers::cleanup_extracted_images($delete_sources);

and this could then just free based on a flag in the mapping..

> +
>  	close($qmeventd_fd);
>      };
>  
> @@ -7372,6 +7374,7 @@ sub live_import_from_files {
>      if ($err) {
>  	warn "An error occurred during live-restore: $err\n";
>  	_do_vm_stop($storecfg, $vmid, 1, 1, 10, 0, 1);
> +	PVE::QemuServer::Helpers::cleanup_extracted_images($delete_sources);

and here as well..

>  	die "live-restore failed\n";
>      }
>  
> diff --git a/PVE/QemuServer/Helpers.pm b/PVE/QemuServer/Helpers.pm
> index 0afb6317..f6bec1d4 100644
> --- a/PVE/QemuServer/Helpers.pm
> +++ b/PVE/QemuServer/Helpers.pm
> @@ -6,6 +6,7 @@ use warnings;
>  use File::stat;
>  use JSON;
>  
> +use PVE::GuestImport;
>  use PVE::INotify;
>  use PVE::ProcFSTools;
>  
> @@ -225,4 +226,13 @@ sub windows_version {
>      return $winversion;
>  }
>  
> +sub cleanup_extracted_images {
> +    my ($delete_sources) = @_;
> +
> +    for my $source (@$delete_sources) {
> +	eval { PVE::GuestImport::cleanup_extracted_image($source) };
> +	warn $@ if $@;
> +    }
> +}
> +

and this can then be dropped, since it's just a wrapper around a helper
that is itself just a wrapper of vdisk_free..

>  1;
> -- 
> 2.39.2
> 
> 
> 
> _______________________________________________
> pve-devel mailing list
> pve-devel at lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> 
> 
> 




More information about the pve-devel mailing list