optional => 1,
description => "Emulated CPU type.",
type => 'string',
- format => $PVE::QemuServer::CPUConfig::cpu_fmt,
+ format => 'pve-vm-cpu-conf',
},
parent => get_standard_option('pve-snapshot-name', {
optional => 1,
$confdesc->{$key} = $PVE::QemuServer::Drive::drivedesc_hash->{$key};
}
-for (my $i = 0; $i < $PVE::QemuServer::Drive::MAX_UNUSED_DISKS; $i++) {
- $confdesc->{"unused$i"} = $PVE::QemuServer::Drive::unuseddesc;
-}
-
for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
$confdesc->{"usb$i"} = $usbdesc;
}
my $version_guard = sub {
my ($major, $minor, $pve) = @_;
return 0 if !min_version($machine_version, $major, $minor, $pve);
+ my $max_pve = PVE::QemuServer::Machine::get_pve_version("$major.$minor");
+ return 1 if min_version($machine_version, $major, $minor, $max_pve+1);
$required_pve_version = $pve if $pve && $pve > $required_pve_version;
return 1;
};
$format = 'raw';
}
+ my $size_str = "";
+
+ if ($format eq 'raw' && $version_guard->(4, 1, 2)) {
+ $size_str = ",size=" . (-s $ovmf_vars);
+ }
+
push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmf_code";
- push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0,file=$path";
+ push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0$size_str,file=$path";
}
# load q35 config
sub vm_start {
my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused,
$forcemachine, $spice_ticket, $migration_network, $migration_type,
- $targetstorage, $timeout, $nbd_protocol_version) = @_;
+ $targetstorage, $timeout, $nbd_protocol_version, $replicated_volumes) = @_;
PVE::QemuConfig->lock_config($vmid, sub {
my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
foreach my $opt (sort keys %$local_volumes) {
my ($volid, $storeid, $volname) = @{$local_volumes->{$opt}};
+ if ($replicated_volumes->{$volid}) {
+ # re-use existing, replicated volume with bitmap on source side
+ $local_volumes->{$opt} = $conf->{${opt}};
+ print "re-using replicated volume: $opt - $volid\n";
+ next;
+ }
my $drive = parse_drive($opt, $conf->{$opt});
# If a remote storage is specified and the format of the original
foreach my $opt (sort keys %$local_volumes) {
my $drivestr = $local_volumes->{$opt};
mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
- if ($nbd_protocol_version > 0 && $migration_type eq 'secure') {
- print "storage migration listens on $migrate_storage_uri:exportname=drive-$opt volume:$drivestr\n";
- } else {
- print "storage migration listens on $migrate_storage_uri:exportname=drive-$opt volume:$drivestr\n";
- }
+ print "storage migration listens on $migrate_storage_uri:exportname=drive-$opt volume:$drivestr\n";
}
}
}
sub qemu_drive_mirror {
- my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit) = @_;
+ my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
$jobs = {} if !$jobs;
my $opts = { timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $qemu_target };
$opts->{format} = $format if $format;
+ if (defined($src_bitmap)) {
+ $opts->{sync} = 'incremental';
+ $opts->{bitmap} = $src_bitmap;
+ print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
+ }
+
if (defined($bwlimit)) {
$opts->{speed} = $bwlimit * 1024;
print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $completion, $qga);
}
+# $completion can be either
+# 'complete': wait until all jobs are ready, block-job-complete them (default)
+# 'cancel': wait until all jobs are ready, block-job-cancel them
+# 'skip': wait until all jobs are ready, return with block jobs in ready state
sub qemu_drive_mirror_monitor {
my ($vmid, $vmiddst, $jobs, $completion, $qga) = @_;
- $completion //= 'wait'; # same semantic as with 'skipcomplete' before
+ $completion //= 'complete';
eval {
my $err_complete = 0;
print "$job: Completing block job...\n";
my $op;
- if ($completion eq 'wait') {
+ if ($completion eq 'complete') {
$op = 'block-job-complete';
- } elsif ($completion eq 'wait_noswap') {
+ } elsif ($completion eq 'cancel') {
$op = 'block-job-cancel';
} else {
die "invalid completion value: $completion\n";
sub clone_disk {
my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
- $newvmid, $storage, $format, $full, $newvollist, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
+ $newvmid, $storage, $format, $full, $newvollist, $jobs, $completion, $qga, $bwlimit, $conf) = @_;
my $newvolid;
$name .= ".$dst_format" if $dst_format ne 'raw';
$snapname = undef;
$size = PVE::QemuServer::Cloudinit::CLOUDINIT_DISK_SIZE;
+ } elsif ($drivename eq 'efidisk0') {
+ $size = get_efivars_size($conf);
}
$newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024));
push @$newvollist, $newvolid;
my $sparseinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $newvolid);
if (!$running || $snapname) {
# TODO: handle bwlimits
- qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
+ if ($drivename eq 'efidisk0') {
+ # the relevant data on the efidisk may be smaller than the source
+ # e.g. on RBD/ZFS, so we use dd to copy only the amount
+ # that is given by the OVMF_VARS.fd
+ my $src_path = PVE::Storage::path($storecfg, $drive->{file});
+ my $dst_path = PVE::Storage::path($storecfg, $newvolid);
+ run_command(['qemu-img', 'dd', '-n', '-O', $dst_format, "bs=1", "count=$size", "if=$src_path", "of=$dst_path"]);
+ } else {
+ qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
+ }
} else {
my $kvmver = get_running_qemu_version ($vmid);
if $drive->{iothread};
}
- qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $skipcomplete, $qga, $bwlimit);
+ qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $completion, $qga, $bwlimit);
}
}
return ($use_old_bios_files, $machine_type);
}
+sub get_efivars_size {
+ my ($conf) = @_;
+ my $arch = get_vm_arch($conf);
+ my (undef, $ovmf_vars) = get_ovmf_files($arch);
+ die "uefi vars image '$ovmf_vars' not found\n" if ! -f $ovmf_vars;
+ return -s $ovmf_vars;
+}
+
+sub update_efidisk_size {
+ my ($conf) = @_;
+
+ return if !defined($conf->{efidisk0});
+
+ my $disk = PVE::QemuServer::parse_drive('efidisk0', $conf->{efidisk0});
+ $disk->{size} = get_efivars_size($conf);
+ $conf->{efidisk0} = print_drive($disk);
+
+ return;
+}
+
sub create_efidisk($$$$$) {
my ($storecfg, $storeid, $vmid, $fmt, $arch) = @_;