qemu-server/PVE/QemuServer/ImportDisk.pm
Fiona Ebner e4cbea4436 import: add source size parameter to do_import()
The file_size_info() will return the size of the image based on
guessing the format. When importing via API, the correct size is
already known, so it's better to pass it in. The root-only 'qm'
commands for disk import and OVF import will still use auto-detection
for backwards compatibility. It might make sense to be able to
explicitly specify the format there too to get the correct size in all
cases.

New callers should detect the size according to the appropriate format
first.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2024-12-09 09:07:17 +01:00

87 lines
3.2 KiB
Perl
Executable File

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 ...
# $params->{skiplock} may be used to skip checking for a lock in the VM config
# $params->{'skip-config-update'} may be used to import the disk without updating the VM config
sub do_import {
my ($src_path, $src_size, $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 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);
warn "format '$format' is not supported by the target storage - using '$dst_format' instead\n"
if $format && $format ne $dst_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 !$params->{'skip-config-update'};
};
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;