Compare commits

...
10 Commits
Author SHA1 Message Date
dualfroz c54622f1ec Describe this repository 2026-10-22 21:06:26 +02:00
dualfroz df951c01eb partially fix #4012: migrate: mention snapshots referencing a local ISO image
When a VM has no local ISO image attached anymore, but one of its
snapshots still references it, migration fails with:

  can't migrate local disk 'local:iso/debian.iso': local cdrom image

which does not tell where the image is coming from, since it is not
visible in the current configuration of the VM.

Add the names of the snapshots referencing the image to the error in
that case, like it is already done for a physical CD/DVD drive. The
check for the physical drive now reuses the same hint, which also
uses the plural form if more than one snapshot references it.

Add a migration test with an ISO image on the 'local' storage that is
only referenced in a snapshot. This requires the 'iso' content type
for the 'local' storage in the test.

Signed-off-by: Michal Fox <me@dualfroz.com>
Link: https://lore.proxmox.com/20261008094650.7-3-me@dualfroz.com
[FE: note that it's a partial fix as the UI still needs to be improved]
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2026-10-09 16:31:29 +02:00
upstream 89276a207f Upstream proxmox-git/qemu-server state of the touched files before a4efa5f9ad 2026-10-08 09:46:50 +00:00
dualfroz ac29cc9716 test: migration: support volumes other than images
The mocked vdisk_list() is based on $source_vdisks, but the real one
only returns images. Add a separate list for other volumes on the
source, e.g. ISO images, which is only considered by the mocked
volume_size_info(). This is in preparation for a test with an ISO
image that is only referenced by a snapshot.

Suggested-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Michal Fox <me@dualfroz.com>
Link: https://lore.proxmox.com/20261008094650.7-2-me@dualfroz.com
[FE: avoid ambiguous term 'format' in comment]
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2026-10-09 16:20:38 +02:00
upstream b5d4ad6a33 Upstream proxmox-git/qemu-server state of the touched files before 5abda8579b 2026-10-08 09:46:49 +00:00
dualfroz 55a24d220e fix #4012: migrate: mention snapshots referencing a local ISO image
When a VM has no local ISO image attached anymore, but one of its
snapshots still references it, migration fails with:

  can't migrate local disk 'local:iso/debian.iso': local cdrom image

which does not tell where the image is coming from, since it is not
visible in the current configuration of the VM.

Add the names of the snapshots referencing the image to the error in
that case, like it is already done for a physical CD/DVD drive. The
check for the physical drive now reuses the same hint, which also
uses the plural form if more than one snapshot references it.

Add a migration test with an ISO image on the 'local' storage that is
only referenced in a snapshot. This requires the 'iso' content type
for the 'local' storage in the test.

Signed-off-by: Michal Fox <me@dualfroz.com>
2026-10-08 01:28:21 +02:00
upstream 2b5f393932 Upstream proxmox-git/qemu-server state of the touched files before 6f34dddd1c 2026-10-08 01:28:21 +02:00
dualfroz 23657b8b24 test: migration: support volumes other than images
The mocked vdisk_list() is based on $source_vdisks, but the real one
only returns images. Add a separate list for other volumes on the
source, e.g. ISO images, which is only considered by the mocked
volume_size_info(). This is in preparation for a test with an ISO
image that is only referenced by a snapshot.

Suggested-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Michal Fox <me@dualfroz.com>
2026-10-08 01:24:03 +02:00
upstream a6c6b6e0f2 Upstream proxmox-git/qemu-server state of the touched files before 07b2af3b17 2026-10-08 01:24:03 +02:00
dualfroz 5f10cfd754 fix #7525: vga: allow setting the number of outputs for VirtIO GPU
With SPICE and QXL, Linux guests get 'max_outputs=4' on the display
device, so they can use up to four displays with a single device. For
the VirtIO GPU, there is no way to change QEMU's default of a single
output, so using multiple displays requires passing the whole device
via custom arguments.

Add a 'max-outputs' option to the 'vga' property, which sets the
'max_outputs' property of the VirtIO GPU, for both 'virtio' and
'virtio-gl'. QEMU supports up to 16 outputs. Other display types
reject the option, as QXL handles multiple displays on its own.

Signed-off-by: Michal Fox <me@dualfroz.com>
2026-10-03 01:53:20 +02:00
8 changed files with 506 additions and 14 deletions

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
# Contributions to proxmox-git/qemu-server
This repository holds only my commits to the upstream project, with their original author, date and diff.
Each of my commits is preceded by a snapshot of the upstream files it touched, so every diff shows exactly what I changed.
+2 -1
View File
@@ -415,7 +415,8 @@ sub scan_local_volumes {
my $snapshot_hint = '';
if (defined($snaprefs) && !$attr->{is_attached}) {
my $snapnames = join(', ', sort keys %$snaprefs);
$snapshot_hint = " (referenced in snapshot - $snapnames)";
my $plural = scalar(keys %$snaprefs) > 1 ? 's' : '';
$snapshot_hint = " (referenced in snapshot$plural - $snapnames)";
}
if ($attr->{cdrom}) {
+15
View File
@@ -187,6 +187,15 @@ my $vga_fmt = {
enum => ['vnc'],
optional => 1,
},
'max-outputs' => {
description => "Sets the maximum number of displays the guest can use. Only supported"
. " with 'virtio' and 'virtio-gl'.",
type => 'integer',
optional => 1,
minimum => 1,
maximum => 16,
default => 1,
},
};
my $ivshmem_fmt = {
@@ -1513,6 +1522,12 @@ sub print_vga_device {
}
}
if (my $outputs = $vga->{'max-outputs'}) {
die "'max-outputs' is only supported with VGA type 'virtio' or 'virtio-gl'\n"
if $vga->{type} !~ m/^virtio/;
$max_outputs = ",max_outputs=$outputs";
}
die "no device-type for $vga->{type}\n" if !$type;
my $memory = "";
+409
View File
@@ -0,0 +1,409 @@
package MigrationTest::QemuMigrateMock;
use strict;
use warnings;
use JSON;
use Test::MockModule;
use MigrationTest::Shared;
use PVE::API2::Qemu;
use PVE::QemuServer::Drive;
use PVE::Storage;
use PVE::Tools qw(file_set_contents file_get_contents);
use PVE::CLIHandler;
use base qw(PVE::CLIHandler);
my $RUN_DIR_PATH = $ENV{RUN_DIR_PATH} or die "no RUN_DIR_PATH set\n";
my $QM_LIB_PATH = $ENV{QM_LIB_PATH} or die "no QM_LIB_PATH set\n";
my $source_volids = decode_json(file_get_contents("${RUN_DIR_PATH}/source_volids"));
my $source_vdisks = decode_json(file_get_contents("${RUN_DIR_PATH}/source_vdisks"));
my $source_other_volumes = decode_json(file_get_contents("${RUN_DIR_PATH}/source_other_volumes"));
my $vm_status = decode_json(file_get_contents("${RUN_DIR_PATH}/vm_status"));
my $expected_calls = decode_json(file_get_contents("${RUN_DIR_PATH}/expected_calls"));
my $fail_config = decode_json(file_get_contents("${RUN_DIR_PATH}/fail_config"));
my $storage_migrate_map = decode_json(file_get_contents("${RUN_DIR_PATH}/storage_migrate_map"));
my $migrate_params = decode_json(file_get_contents("${RUN_DIR_PATH}/migrate_params"));
my $test_vmid = $migrate_params->{vmid};
my $test_target = $migrate_params->{target};
my $test_opts = $migrate_params->{opts};
my $current_log = '';
my $vm_stop_executed = 0;
# mocked modules
my $inotify_module = Test::MockModule->new("PVE::INotify");
$inotify_module->mock(
nodename => sub {
return 'pve0';
},
);
$MigrationTest::Shared::qemu_config_module->mock(
move_config_to_node => sub {
my ($self, $vmid, $target) = @_;
die "moving wrong config: '$vmid'\n" if $vmid ne $test_vmid;
die "moving config to wrong node: '$target'\n" if $target ne $test_target;
delete $expected_calls->{move_config_to_node};
},
);
my $tunnel_module = Test::MockModule->new("PVE::Tunnel");
$tunnel_module->mock(
finish_tunnel => sub {
delete $expected_calls->{'finish_tunnel'};
return;
},
write_tunnel => sub {
my ($tunnel, $timeout, $command) = @_;
if ($command =~ m/^resume (\d+)$/) {
my $vmid = $1;
die "resuming wrong VM '$vmid'\n" if $vmid ne $test_vmid;
return;
}
die "write_tunnel (mocked) - implement me: $command\n";
},
);
my $qemu_migrate_module = Test::MockModule->new("PVE::QemuMigrate");
$qemu_migrate_module->mock(
fork_tunnel => sub {
die "fork_tunnel (mocked) - implement me\n"; # currently no call should lead here
},
start_remote_tunnel => sub {
my ($self, $raddr, $rport, $ruri, $unix_socket_info) = @_;
$expected_calls->{'finish_tunnel'} = 1;
$self->{tunnel} = {
writer => "mocked",
reader => "mocked",
pid => 123456,
version => 1,
};
},
log => sub {
my ($self, $level, $message) = @_;
$current_log .= "$level: $message\n";
},
mon_cmd => sub {
my ($vmid, $command, %params) = @_;
if ($command eq 'nbd-server-start') {
return;
} elsif ($command eq 'block-dirty-bitmap-add') {
my $drive = $params{node};
delete $expected_calls->{"block-dirty-bitmap-add-${drive}"};
return;
} elsif ($command eq 'block-dirty-bitmap-remove') {
return;
} elsif ($command eq 'query-migrate') {
return { status => 'failed' } if $fail_config->{'query-migrate'};
return { status => 'completed' };
} elsif ($command eq 'migrate') {
return;
} elsif ($command eq 'migrate-set-parameters') {
return;
} elsif ($command eq 'migrate_cancel') {
return;
}
die "mon_cmd (mocked) - implement me: $command";
},
transfer_replication_state => sub {
delete $expected_calls->{transfer_replication_state};
},
switch_replication_job_target => sub {
delete $expected_calls->{switch_replication_job_target};
},
);
$MigrationTest::Shared::qemu_server_module->mock(
kvm_user_version => sub {
return "5.0.0";
},
vm_stop => sub {
$vm_stop_executed = 1;
delete $expected_calls->{'vm_stop'};
},
);
my sub common_mirror_mock {
my ($vmid, $drive_id) = @_;
die "drive_mirror with wrong vmid: '$vmid'\n" if $vmid ne $test_vmid;
die "qemu_drive_mirror '$drive_id' error\n"
if $fail_config->{qemu_drive_mirror} && $fail_config->{qemu_drive_mirror} eq $drive_id;
my $nbd_info = decode_json(file_get_contents("${RUN_DIR_PATH}/nbd_info"));
die "target does not expect drive mirror for '$drive_id'\n"
if !defined($nbd_info->{$drive_id});
delete $nbd_info->{$drive_id};
file_set_contents("${RUN_DIR_PATH}/nbd_info", to_json($nbd_info));
}
my $qemu_server_blockjob_module = Test::MockModule->new("PVE::QemuServer::BlockJob");
$qemu_server_blockjob_module->mock(
qemu_blockjobs_cancel => sub {
return;
},
qemu_drive_mirror => sub {
my (
$vmid,
$drive_id,
$dst_volid,
$vmiddst,
$is_zero_initialized,
$jobs,
$completion,
$qga,
$bwlimit,
$src_bitmap,
) = @_;
common_mirror_mock($vmid, $drive_id);
},
blockdev_mirror => sub {
my ($source, $dest, $jobs, $completion, $options) = @_;
my $drive_id = PVE::QemuServer::Drive::get_drive_id($source->{drive});
common_mirror_mock($source->{vmid}, $drive_id);
},
monitor => sub {
my ($qmp_peer, $vmiddst, $jobs, $completion, $qga, $op) = @_;
if (
$fail_config->{block_job_monitor}
&& $fail_config->{block_job_monitor} eq $completion
) {
die "block_job_monitor '$completion' error\n";
}
return;
},
qemu_drive_mirror_switch_to_active_mode => sub {
return;
},
);
my $qemu_server_cpuconfig_module = Test::MockModule->new("PVE::QemuServer::CPUConfig");
$qemu_server_cpuconfig_module->mock(
get_cpu_from_running_vm => sub {
die "invalid test: if you specify a custom CPU model you need to "
. "specify runningcpu as well\n"
if !defined($vm_status->{runningcpu});
return $vm_status->{runningcpu};
},
);
my $qemu_server_helpers_module = Test::MockModule->new("PVE::QemuServer::Helpers");
$qemu_server_helpers_module->mock(
vm_running_locally => sub {
return $vm_status->{running} && !$vm_stop_executed;
},
);
my $qemu_server_machine_module = Test::MockModule->new("PVE::QemuServer::Machine");
$qemu_server_machine_module->mock(
qemu_machine_pxe => sub {
die "invalid test: no runningmachine specified\n"
if !defined($vm_status->{runningmachine});
return $vm_status->{runningmachine};
},
get_current_qemu_machine => sub {
die "invalid test: no runningmachine specified\n"
if !defined($vm_status->{runningmachine});
return $vm_status->{runningmachine};
},
);
my $qemu_server_network_module = Test::MockModule->new("PVE::QemuServer::Network");
$qemu_server_network_module->mock(
del_nets_bridge_fdb => sub { return; },
mon_cmd => sub {
my ($vmid, $command, %params) = @_;
if ($command eq 'qom-get') {
if (
$params{path} =~ m|^/machine/peripheral/net\d+$|
&& $params{property} eq 'host_mtu'
) {
return 1500;
}
die "mon_cmd (mocked) - implement me: $command for path '$params{path}' property"
. " '$params{property}'";
}
die "mon_cmd (mocked) - implement me: $command";
},
);
my $qemu_server_qmphelpers_module = Test::MockModule->new("PVE::QemuServer::QMPHelpers");
$qemu_server_qmphelpers_module->mock(
runs_at_least_qemu_version => sub {
return 1;
},
);
my $ssh_info_module = Test::MockModule->new("PVE::SSHInfo");
$ssh_info_module->mock(
get_ssh_info => sub {
my ($node, $network_cidr) = @_;
return {
ip => '1.2.3.4',
name => $node,
network => $network_cidr,
};
},
);
$MigrationTest::Shared::storage_module->mock(
storage_migrate => sub {
my ($cfg, $volid, $target_sshinfo, $target_storeid, $opts, $logfunc) = @_;
die "storage_migrate '$volid' error\n"
if $fail_config->{storage_migrate} && $fail_config->{storage_migrate} eq $volid;
my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
die "invalid test: need to add entry for '$volid' to storage_migrate_map\n"
if $storeid ne $target_storeid && !defined($storage_migrate_map->{$volid});
my $target_volname = $storage_migrate_map->{$volid} // $opts->{target_volname}
// $volname;
my $target_volid = "${target_storeid}:${target_volname}";
MigrationTest::Shared::add_target_volid($target_volid);
return $target_volid;
},
vdisk_list => sub { # expects vmid to be set
my ($cfg, $storeid, $vmid, $vollist) = @_;
my @storeids = defined($storeid) ? ($storeid) : keys %{$source_vdisks};
my $res = {};
foreach my $storeid (@storeids) {
my $list_for_storeid = $source_vdisks->{$storeid};
my @list_for_vm = grep { $_->{vmid} eq $vmid } @{$list_for_storeid};
$res->{$storeid} = \@list_for_vm;
}
return $res;
},
vdisk_free => sub {
my ($scfg, $volid) = @_;
PVE::Storage::parse_volume_id($volid);
die "vdisk_free '$volid' error\n"
if defined($fail_config->{vdisk_free}) && $fail_config->{vdisk_free} eq $volid;
delete $source_volids->{$volid};
},
volume_size_info => sub {
my ($scfg, $volid) = @_;
my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
for my $v ($source_vdisks->{$storeid}->@*, $source_other_volumes->{$storeid}->@*) {
return wantarray ? ($v->{size}, $v->{format}, $v->{used}, $v->{parent}) : $v->{size}
if $v->{volid} eq $volid;
}
die "could not find '$volid' in mock 'source_vdisks' or 'source_other_volumes'\n";
},
);
$MigrationTest::Shared::tools_module->mock(
get_host_address_family => sub {
die "get_host_address_family (mocked) - implement me\n"; # currently no call should lead here
},
next_migrate_port => sub {
die "next_migrate_port (mocked) - implement me\n"; # currently no call should lead here
},
run_command => sub {
my ($cmd_tail, %param) = @_;
my $cmd_msg = to_json($cmd_tail);
my $cmd = shift @{$cmd_tail};
if ($cmd =~ m@^(?:/usr/bin/)?ssh$@) {
while (scalar(@{$cmd_tail})) {
$cmd = shift @{$cmd_tail};
if ($cmd eq '/bin/true') {
return 0;
} elsif ($cmd eq 'qm') {
$cmd = shift @{$cmd_tail};
if ($cmd eq 'start') {
delete $expected_calls->{ssh_qm_start};
delete $vm_status->{runningmachine};
delete $vm_status->{runningcpu};
my @options = (@{$cmd_tail});
while (scalar(@options)) {
my $opt = shift @options;
if ($opt eq '--machine') {
$vm_status->{runningmachine} = shift @options;
} elsif ($opt eq '--force-cpu') {
$vm_status->{runningcpu} = shift @options;
}
}
return $MigrationTest::Shared::tools_module->original('run_command')->(
[
'/usr/bin/perl',
"-I${QM_LIB_PATH}",
"-I${QM_LIB_PATH}/test",
"${QM_LIB_PATH}/test/MigrationTest/QmMock.pm",
'start',
@{$cmd_tail},
],
%param,
);
} elsif ($cmd eq 'nbdstop') {
delete $expected_calls->{ssh_nbdstop};
return 0;
} elsif ($cmd eq 'resume') {
return 0;
} elsif ($cmd eq 'unlock') {
my $vmid = shift @{$cmd_tail};
die "unlocking wrong vmid: $vmid\n" if $vmid ne $test_vmid;
PVE::QemuConfig->remove_lock($vmid);
return 0;
} elsif ($cmd eq 'stop') {
return 0;
}
die "run_command (mocked) ssh qm command - implement me: ${cmd_msg}";
} elsif ($cmd eq 'pvesm') {
$cmd = shift @{$cmd_tail};
if ($cmd eq 'free') {
my $volid = shift @{$cmd_tail};
PVE::Storage::parse_volume_id($volid);
return 1
if $fail_config->{ssh_pvesm_free}
&& $fail_config->{ssh_pvesm_free} eq $volid;
MigrationTest::Shared::remove_target_volid($volid);
return 0;
}
die "run_command (mocked) ssh pvesm command - implement me: ${cmd_msg}";
}
}
die "run_command (mocked) ssh command - implement me: ${cmd_msg}";
}
die "run_command (mocked) - implement me: ${cmd_msg}";
},
);
eval { PVE::QemuMigrate->migrate($test_target, undef, $test_vmid, $test_opts) };
my $error = $@;
file_set_contents("${RUN_DIR_PATH}/source_volids", to_json($source_volids));
file_set_contents("${RUN_DIR_PATH}/vm_status", to_json($vm_status));
file_set_contents("${RUN_DIR_PATH}/expected_calls", to_json($expected_calls));
file_set_contents("${RUN_DIR_PATH}/log", $current_log);
die $error if $error;
1;
+11
View File
@@ -0,0 +1,11 @@
# TEST: Test that max-outputs is rejected for non-VirtIO GPUs
# EXPECT_ERROR: 'max-outputs' is only supported with VGA type 'virtio' or 'virtio-gl'
cores: 2
memory: 2048
name: qxldisplays
net0: virtio=A2:C0:43:67:08:A1,bridge=vmbr0
ostype: l26
scsihw: virtio-scsi-pci
smbios1: uuid=7b10d7af-b932-4c66-b2c3-3996152ec465
vmgenid: c773c261-d800-4348-9f5d-167fadd53cf8
vga: qxl,max-outputs=2
@@ -0,0 +1,10 @@
# TEST: Test for VirtIO GPU with multiple outputs
cores: 2
memory: 2048
name: virtiodisplays
net0: virtio=A2:C0:43:67:08:A1,bridge=vmbr0
ostype: l26
scsihw: virtio-scsi-pci
smbios1: uuid=7b10d7af-b932-4c66-b2c3-3996152ec465
vmgenid: c773c261-d800-4348-9f5d-167fadd53cf8
vga: virtio,max-outputs=2
@@ -0,0 +1,35 @@
/usr/bin/kvm \
-id 8006 \
-name virtiodisplays \
-no-shutdown \
-chardev 'socket,id=qmp,path=/var/run/qemu-server/8006.qmp,server=on,wait=off' \
-object 'monitor-qmp,chardev=qmp,id=monitor-qmp' \
-chardev 'socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect-ms=5000' \
-object 'monitor-qmp,chardev=qmp-event,id=monitor-qmp-event' \
-pidfile /var/run/qemu-server/8006.pid \
-daemonize \
-smbios 'type=1,uuid=7b10d7af-b932-4c66-b2c3-3996152ec465' \
-smp '2,sockets=1,cores=2,maxcpus=2' \
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc 'unix:/var/run/qemu-server/8006.vnc,password=on' \
-cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 2048 \
-global 'PIIX4_PM.disable_s3=1' \
-global 'PIIX4_PM.disable_s4=1' \
-device 'pci-bridge,id=pci.1,chassis_nr=1,bus=pci.0,addr=0x1e' \
-device 'pci-bridge,id=pci.2,chassis_nr=2,bus=pci.0,addr=0x1f' \
-device 'pci-bridge,id=pci.4,chassis_nr=4,bus=pci.1,addr=0x1c' \
-device 'vmgenid,guid=c773c261-d800-4348-9f5d-167fadd53cf8' \
-device 'piix3-usb-uhci,id=uhci,bus=pci.0,addr=0x1.0x2' \
-device 'usb-tablet,id=tablet,bus=uhci.0,port=1' \
-device 'virtio-vga,id=vga,max_outputs=2,bus=pci.0,addr=0x2' \
-device 'virtio-serial,id=spice,bus=pci.0,addr=0x9' \
-chardev 'spicevmc,id=vdagent,name=vdagent' \
-device 'virtserialport,chardev=vdagent,name=com.redhat.spice.0' \
-spice 'tls-port=61000,addr=127.0.0.1,tls-ciphers=HIGH,seamless-migration=on' \
-device 'virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3,free-page-reporting=on' \
-iscsi 'initiator-name=iqn.1993-08.org.debian:01:aabbccddeeff' \
-netdev 'type=tap,id=net0,ifname=tap8006i0,script=/usr/libexec/qemu-server/pve-bridge,downscript=/usr/libexec/qemu-server/pve-bridgedown,vhost=on' \
-device 'virtio-net-pci,mac=A2:C0:43:67:08:A1,netdev=net0,bus=pci.0,addr=0x12,id=net0,rx_queue_size=1024,tx_queue_size=256,bootindex=300,host_mtu=1500,host_tunnel=off' \
-machine 'hpet=off,type=pc+pve0'
+18 -13
View File
@@ -347,17 +347,6 @@ my $source_vdisks = {
'volid' => 'local-dir:4567/vm-4567-state-snap2.raw',
},
],
'local' => [
{
'ctime' => 1589439681,
'format' => 'iso',
'parent' => undef,
'size' => 663748608,
'used' => 663748608,
'vmid' => '0',
'volid' => 'local:iso/debian.iso',
},
],
'local-lvm' => [
{
'ctime' => '1589277334',
@@ -448,6 +437,21 @@ my $source_vdisks = {
],
};
# Non-image volumes on the source, e.g. ISO images, with the same structure as $source_vdisks. They
# are only used for the mocked volume_size_info(), because vdisk_list() only returns images.
my $source_other_volumes = {
'local' => [
{
'ctime' => 1589439681,
'format' => 'iso',
'parent' => undef,
'size' => 663748608,
'used' => 663748608,
'volid' => 'local:iso/debian.iso',
},
],
};
my $default_expected_calls_online = {
move_config_to_node => 1,
ssh_qm_start => 1,
@@ -1263,8 +1267,8 @@ my $tests = [
},
},
expected_calls => {},
expect_die =>
"can't migrate local disk 'local:iso/debian.iso': local cdrom image (referenced in snapshot - ohsnap)",
expect_die => "can't migrate local disk 'local:iso/debian.iso': local cdrom image"
. " (referenced in snapshot - ohsnap)",
expected => {
source_volids => local_volids_for_vm(105),
target_volids => {},
@@ -1779,6 +1783,7 @@ foreach my $test (@{$tests}) {
file_set_contents("${run_dir}/replication_config", to_json($replication_config));
file_set_contents("${run_dir}/storage_config", to_json($storage_config));
file_set_contents("${run_dir}/source_vdisks", to_json($source_vdisks));
file_set_contents("${run_dir}/source_other_volumes", to_json($source_other_volumes));
my $expect_die = $test->{expect_die};
my $expected = $test->{expected};