[pve-devel] [PATCH qemu-server v2 3/4] api: create: implement extracting disks when needed for import-from
Dominik Csapak
d.csapak at proxmox.com
Fri Apr 19 11:46:06 CEST 2024
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>
---
changes from v1:
* put extraction into branch for managed volids
* adapt to new module location
* add checks for the impor storage if it support 'images'
(otherwise we cannot extract there)
* add cleanup to other 'create_disks' method
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);
+
+ 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)) {
+ 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 = [];
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";
+ push @$delete_sources, $source;
+ }
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);
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);
};
my $check_cpu_model_access = sub {
@@ -1079,6 +1097,7 @@ __PACKAGE__->register_method({
my $createfn = sub {
my $live_import_mapping = {};
+ my $delete_sources = [];
# 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(
$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);
die "$emsg $err";
}
@@ -1165,7 +1185,7 @@ __PACKAGE__->register_method({
warn $@ if $@;
return;
} else {
- return $live_import_mapping;
+ return ($live_import_mapping, $delete_sources);
}
};
@@ -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->() };
if (my $err = $@) {
eval {
my $conffile = PVE::QemuConfig->config_file($vmid);
@@ -1214,7 +1234,10 @@ __PACKAGE__->register_method({
$vmid,
$conf,
$import_options,
+ $delete_sources,
);
+ } else {
+ PVE::QemuServer::Helpers::cleanup_extracted_images($delete_sources);
}
};
}
@@ -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(
$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);
+
# 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 28e630d3..f842451e 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) = @_;
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);
+
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);
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 $@;
+ }
+}
+
1;
--
2.39.2
More information about the pve-devel
mailing list