+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use JSON;
+use Test::More;
+use Test::MockModule;
+
+use PVE::JSONSchema;
+use PVE::Tools qw(file_set_contents file_get_contents run_command);
+
+my $QM_LIB_PATH = '..';
+my $MIGRATE_LIB_PATH = '..';
+my $RUN_DIR_PATH = './MigrationTest/run/';
+
+# test configuration shared by all tests
+
+my $replication_config = {
+ 'ids' => {
+ '105-0' => {
+ 'guest' => '105',
+ 'id' => '105-0',
+ 'jobnum' => '0',
+ 'source' => 'pve0',
+ 'target' => 'pve2',
+ 'type' => 'local'
+ },
+ },
+ 'order' => {
+ '105-0' => 1,
+ }
+};
+
+my $storage_config = {
+ ids => {
+ local => {
+ content => {
+ images => 1,
+ },
+ path => "/var/lib/vz",
+ type => "dir",
+ shared => 0,
+ },
+ "local-lvm" => {
+ content => {
+ images => 1,
+ },
+ nodes => {
+ pve0 => 1,
+ pve1 => 1,
+ },
+ type => "lvmthin",
+ thinpool => "data",
+ vgname => "pve",
+ },
+ "local-zfs" => {
+ content => {
+ images => 1,
+ rootdir => 1,
+ },
+ pool => "rpool/data",
+ sparse => 1,
+ type => "zfspool",
+ },
+ "rbd-store" => {
+ monhost => "127.0.0.42,127.0.0.21,::1",
+ fsid => 'fc4181a6-56eb-4f68-b452-8ba1f381ca2a',
+ content => {
+ images => 1,
+ },
+ type => "rbd",
+ pool => "cpool",
+ username => "admin",
+ shared => 1,
+ },
+ "local-dir" => {
+ content => {
+ images => 1,
+ },
+ path => "/some/dir/",
+ type => "dir",
+ },
+ "other-dir" => {
+ content => {
+ images => 1,
+ },
+ path => "/some/other/dir/",
+ type => "dir",
+ },
+ "zfs-alias-1" => {
+ content => {
+ images => 1,
+ rootdir => 1,
+ },
+ pool => "aliaspool",
+ sparse => 1,
+ type => "zfspool",
+ },
+ "zfs-alias-2" => {
+ content => {
+ images => 1,
+ rootdir => 1,
+ },
+ pool => "aliaspool",
+ sparse => 1,
+ type => "zfspool",
+ },
+ },
+};
+
+my $vm_configs = {
+ 105 => {
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'ide0' => 'local-zfs:vm-105-disk-1,size=103M',
+ 'ide2' => 'none,media=cdrom',
+ 'memory' => 512,
+ 'name' => 'Copy-of-VM-newapache',
+ 'net0' => 'virtio=4A:A3:E4:4C:CF:F0,bridge=vmbr0,firewall=1',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'parent' => 'ohsnap',
+ 'pending' => {},
+ 'scsi0' => 'local-zfs:vm-105-disk-0,size=4G',
+ 'scsihw' => 'virtio-scsi-pci',
+ 'smbios1' => 'uuid=1ddfe18b-77e0-47f6-a4bd-f1761bf6d763',
+ 'snapshots' => {
+ 'ohsnap' => {
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'ide2' => 'none,media=cdrom',
+ 'memory' => 512,
+ 'name' => 'Copy-of-VM-newapache',
+ 'net0' => 'virtio=4A:A3:E4:4C:CF:F0,bridge=vmbr0,firewall=1',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'scsi0' => 'local-zfs:vm-105-disk-0,size=4G',
+ 'scsihw' => 'virtio-scsi-pci',
+ 'smbios1' => 'uuid=1ddfe18b-77e0-47f6-a4bd-f1761bf6d763',
+ 'snaptime' => 1580976924,
+ 'sockets' => 1,
+ 'startup' => 'order=2',
+ 'vmgenid' => '4eb1d535-9381-4ddc-a8aa-af50c4d9177b'
+ },
+ },
+ 'sockets' => 1,
+ 'startup' => 'order=2',
+ 'vmgenid' => '4eb1d535-9381-4ddc-a8aa-af50c4d9177b',
+ },
+ 111 => {
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'ide0' => 'local-lvm:vm-111-disk-0,size=4096M',
+ 'ide2' => 'none,media=cdrom',
+ 'memory' => 512,
+ 'name' => 'pending-test',
+ 'net0' => 'virtio=4A:A3:E4:4C:CF:F0,bridge=vmbr0,firewall=1',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'pending' => {
+ 'scsi0' => 'local-zfs:vm-111-disk-0,size=103M',
+ },
+ 'scsihw' => 'virtio-scsi-pci',
+ 'snapshots' => {},
+ 'smbios1' => 'uuid=5ad71d4d-8f73-4377-853e-2d22c10c96a5',
+ 'sockets' => 1,
+ 'vmgenid' => '2c00c030-0b5b-4988-a371-6ab259893f22',
+ },
+ 123 => {
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'scsi0' => 'zfs-alias-1:vm-123-disk-0,size=4096M',
+ 'scsi1' => 'zfs-alias-2:vm-123-disk-0,size=4096M',
+ 'ide2' => 'none,media=cdrom',
+ 'memory' => 512,
+ 'name' => 'alias-test',
+ 'net0' => 'virtio=4A:A3:E4:4C:CF:F0,bridge=vmbr0,firewall=1',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'pending' => {},
+ 'scsihw' => 'virtio-scsi-pci',
+ 'snapshots' => {},
+ 'smbios1' => 'uuid=5ad71d4d-8f73-4377-853e-2d22c10c96a5',
+ 'sockets' => 1,
+ 'vmgenid' => '2c00c030-0b5b-4988-a371-6ab259893f22',
+ },
+ 149 => {
+ 'agent' => '0',
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'hotplug' => 'disk,network,usb,memory,cpu',
+ 'ide2' => 'none,media=cdrom',
+ 'memory' => 4096,
+ 'name' => 'asdf',
+ 'net0' => 'virtio=52:5D:7E:62:85:97,bridge=vmbr1',
+ 'numa' => 1,
+ 'ostype' => 'l26',
+ 'scsi0' => 'local-lvm:vm-149-disk-0,format=raw,size=4G',
+ 'scsi1' => 'local-dir:149/vm-149-disk-0.qcow2,format=qcow2,size=1G',
+ 'scsihw' => 'virtio-scsi-pci',
+ 'snapshots' => {},
+ 'smbios1' => 'uuid=e980bd43-a405-42e2-b5f4-31efe6517460',
+ 'sockets' => 1,
+ 'startup' => 'order=2',
+ 'vmgenid' => '36c6c50c-6ef5-4adc-9b6f-6ba9c8071db0',
+ },
+ 341 => {
+ 'arch' => 'aarch64',
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'efidisk0' => 'local-lvm:vm-341-disk-0',
+ 'ide2' => 'none,media=cdrom',
+ 'ipconfig0' => 'ip=103.214.69.10/25,gw=103.214.69.1',
+ 'memory' => 4096,
+ 'name' => 'VM1033',
+ 'net0' => 'virtio=4E:F1:82:6D:D7:4B,bridge=vmbr0,firewall=1,rate=10',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'scsi0' => 'rbd-store:vm-341-disk-0,size=1G',
+ 'scsihw' => 'virtio-scsi-pci',
+ 'snapshots' => {},
+ 'smbios1' => 'uuid=e01e4c73-46f1-47c8-af79-288fdf6b7462',
+ 'sockets' => 2,
+ 'vmgenid' => 'af47c000-eb0c-48e8-8991-ca4593cd6916',
+ },
+ 1033 => {
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'ide0' => 'rbd-store:vm-1033-cloudinit,media=cdrom,size=4M',
+ 'ide2' => 'none,media=cdrom',
+ 'ipconfig0' => 'ip=103.214.69.10/25,gw=103.214.69.1',
+ 'memory' => 4096,
+ 'name' => 'VM1033',
+ 'net0' => 'virtio=4E:F1:82:6D:D7:4B,bridge=vmbr0,firewall=1,rate=10',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'scsi0' => 'rbd-store:vm-1033-disk-1,size=1G',
+ 'scsihw' => 'virtio-scsi-pci',
+ 'snapshots' => {},
+ 'smbios1' => 'uuid=e01e4c73-46f1-47c8-af79-288fdf6b7462',
+ 'sockets' => 2,
+ 'vmgenid' => 'af47c000-eb0c-48e8-8991-ca4593cd6916',
+ },
+ 4567 => {
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'ide2' => 'none,media=cdrom',
+ 'memory' => 512,
+ 'name' => 'snapme',
+ 'net0' => 'virtio=A6:D1:F1:EB:7B:C2,bridge=vmbr0,firewall=1',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'parent' => 'snap1',
+ 'pending' => {},
+ 'scsi0' => 'local-dir:4567/vm-4567-disk-0.qcow2,size=4G',
+ 'scsihw' => 'virtio-scsi-pci',
+ 'smbios1' => 'uuid=2925fdec-a066-4228-b46b-eef8662f5e74',
+ 'snapshots' => {
+ 'snap1' => {
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'ide2' => 'none,media=cdrom',
+ 'memory' => 512,
+ 'name' => 'snapme',
+ 'net0' => 'virtio=A6:D1:F1:EB:7B:C2,bridge=vmbr0,firewall=1',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'runningcpu' => 'kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep',
+ 'runningmachine' => 'pc-i440fx-5.0+pve0',
+ 'scsi0' => 'local-dir:4567/vm-4567-disk-0.qcow2,size=4G',
+ 'scsihw' => 'virtio-scsi-pci',
+ 'smbios1' => 'uuid=2925fdec-a066-4228-b46b-eef8662f5e74',
+ 'snaptime' => 1595928799,
+ 'sockets' => 1,
+ 'startup' => 'order=2',
+ 'vmgenid' => '932b227a-8a39-4ede-955a-dbd4bc4385ed',
+ 'vmstate' => 'local-dir:4567/vm-4567-state-snap1.raw',
+ },
+ 'snap2' => {
+ 'bootdisk' => 'scsi0',
+ 'cores' => 1,
+ 'ide2' => 'none,media=cdrom',
+ 'memory' => 512,
+ 'name' => 'snapme',
+ 'net0' => 'virtio=A6:D1:F1:EB:7B:C2,bridge=vmbr0,firewall=1',
+ 'numa' => 0,
+ 'ostype' => 'l26',
+ 'parent' => 'snap1',
+ 'runningcpu' => 'kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep',
+ 'runningmachine' => 'pc-i440fx-5.0+pve0',
+ 'scsi0' => 'local-dir:4567/vm-4567-disk-0.qcow2,size=4G',
+ 'scsi1' => 'local-zfs:vm-4567-disk-0,size=1G',
+ 'scsihw' => 'virtio-scsi-pci',
+ 'smbios1' => 'uuid=2925fdec-a066-4228-b46b-eef8662f5e74',
+ 'snaptime' => 1595928871,
+ 'sockets' => 1,
+ 'startup' => 'order=2',
+ 'vmgenid' => '932b227a-8a39-4ede-955a-dbd4bc4385ed',
+ 'vmstate' => 'local-dir:4567/vm-4567-state-snap2.raw',
+ },
+ },
+ 'sockets' => 1,
+ 'startup' => 'order=2',
+ 'unused0' => 'local-zfs:vm-4567-disk-0',
+ 'vmgenid' => 'e698e60c-9278-4dd9-941f-416075383f2a',
+ },
+};
+
+my $source_vdisks = {
+ 'local-dir' => [
+ {
+ 'ctime' => 1589439681,
+ 'format' => 'qcow2',
+ 'parent' => undef,
+ 'size' => 1073741824,
+ 'used' => 335872,
+ 'vmid' => '149',
+ 'volid' => 'local-dir:149/vm-149-disk-0.qcow2',
+ },
+ {
+ 'ctime' => 1595928898,
+ 'format' => 'qcow2',
+ 'parent' => undef,
+ 'size' => 4294967296,
+ 'used' => 1811664896,
+ 'vmid' => '4567',
+ 'volid' => 'local-dir:4567/vm-4567-disk-0.qcow2',
+ },
+ {
+ 'ctime' => 1595928800,
+ 'format' => 'raw',
+ 'parent' => undef,
+ 'size' => 274666496,
+ 'used' => 274669568,
+ 'vmid' => '4567',
+ 'volid' => 'local-dir:4567/vm-4567-state-snap1.raw',
+ },
+ {
+ 'ctime' => 1595928872,
+ 'format' => 'raw',
+ 'parent' => undef,
+ 'size' => 273258496,
+ 'used' => 273260544,
+ 'vmid' => '4567',
+ 'volid' => 'local-dir:4567/vm-4567-state-snap2.raw',
+ },
+ ],
+ 'local-lvm' => [
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 4294967296,
+ 'vmid' => '149',
+ 'volid' => 'local-lvm:vm-149-disk-0',
+ },
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 4194304,
+ 'vmid' => '341',
+ 'volid' => 'local-lvm:vm-341-disk-0',
+ },
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 4294967296,
+ 'vmid' => '111',
+ 'volid' => 'local-lvm:vm-111-disk-0',
+ },
+ ],
+ 'local-zfs' => [
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 4294967296,
+ 'vmid' => '105',
+ 'volid' => 'local-zfs:vm-105-disk-0',
+ },
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 108003328,
+ 'vmid' => '105',
+ 'volid' => 'local-zfs:vm-105-disk-1',
+ },
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 108003328,
+ 'vmid' => '111',
+ 'volid' => 'local-zfs:vm-111-disk-0',
+ },
+ {
+ 'format' => 'raw',
+ 'name' => 'vm-4567-disk-0',
+ 'parent' => undef,
+ 'size' => 1073741824,
+ 'vmid' => '4567',
+ 'volid' => 'local-zfs:vm-4567-disk-0',
+ },
+ ],
+ 'rbd-store' => [
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 1073741824,
+ 'vmid' => '1033',
+ 'volid' => 'rbd-store:vm-1033-disk-1',
+ },
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 1073741824,
+ 'vmid' => '1033',
+ 'volid' => 'rbd-store:vm-1033-cloudinit',
+ },
+ ],
+ 'zfs-alias-1' => [
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 4294967296,
+ 'vmid' => '123',
+ 'volid' => 'zfs-alias-1:vm-123-disk-0',
+ },
+ ],
+ 'zfs-alias-2' => [
+ {
+ 'ctime' => '1589277334',
+ 'format' => 'raw',
+ 'size' => 4294967296,
+ 'vmid' => '123',
+ 'volid' => 'zfs-alias-2:vm-123-disk-0',
+ },
+ ],
+};
+
+my $default_expected_calls_online = {
+ move_config_to_node => 1,
+ ssh_qm_start => 1,
+ vm_stop => 1,
+};
+
+my $default_expected_calls_offline = {
+ move_config_to_node => 1,
+};
+
+my $replicated_expected_calls_online = {
+ %{$default_expected_calls_online},
+ transfer_replication_state => 1,
+ switch_replication_job_target => 1,
+};
+
+my $replicated_expected_calls_offline = {
+ %{$default_expected_calls_offline},
+ transfer_replication_state => 1,
+ switch_replication_job_target => 1,
+};
+
+# helpers
+
+sub get_patched_config {
+ my ($vmid, $patch) = @_;
+
+ my $new_config = { %{$vm_configs->{$vmid}} };
+ patch_config($new_config, $patch) if defined($patch);
+
+ return $new_config;
+}
+
+sub patch_config {
+ my ($config, $patch) = @_;
+
+ foreach my $key (keys %{$patch}) {
+ if ($key eq 'snapshots' && defined($patch->{$key})) {
+ my $new_snapshot_configs = {};
+ foreach my $snap (keys %{$patch->{snapshots}}) {
+ my $new_snapshot_config = { %{$config->{snapshots}->{$snap}} };
+ patch_config($new_snapshot_config, $patch->{snapshots}->{$snap});
+ $new_snapshot_configs->{$snap} = $new_snapshot_config;
+ }
+ $config->{snapshots} = $new_snapshot_configs;
+ } elsif (defined($patch->{$key})) {
+ $config->{$key} = $patch->{$key};
+ } else { # use undef value for deletion
+ delete $config->{$key};
+ }
+ }
+}
+
+sub local_volids_for_vm {
+ my ($vmid) = @_;
+
+ my $res = {};
+ foreach my $storeid (keys %{$source_vdisks}) {
+ next if $storage_config->{ids}->{$storeid}->{shared};
+ $res = {
+ %{$res},
+ map { $_->{vmid} eq $vmid ? ($_->{volid} => 1) : () } @{$source_vdisks->{$storeid}}
+ };
+ }
+ return $res;
+}
+
+my $tests = [
+# each test consists of the following:
+# name - unique name for the test which also serves as a dir name.
+# NOTE: gets passed to make, so don't use whitespace or slash
+# and adapt buildsys (regex) on code structure changes
+# target - hostname of target node
+# vmid - ID of the VM to migrate
+# opts - options for the migrate() call
+# target_volids - hash of volids on the target at the beginning
+# vm_status - hash with running, runningmachine and optionally runningcpu
+# expected_calls - hash whose keys are calls which are required
+# to be made if the migration gets far enough
+# expect_die - expect the migration call to fail, and an error message
+# matching the specified text in the log
+# expected - hash consisting of:
+# source_volids - hash of volids expected on the source
+# target_volids - hash of volids expected on the target
+# vm_config - vm configuration hash
+# vm_status - hash with running, runningmachine and optionally runningcpu
+ {
+ # NOTE get_efivars_size is mocked and returns 128K
+ name => '341_running_efidisk_targetstorage_dir',
+ target => 'pve1',
+ vmid => 341,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ targetstorage => 'local-dir',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-dir:341/vm-341-disk-10.raw' => 1,
+ },
+ vm_config => get_patched_config(341, {
+ efidisk0 => 'local-dir:341/vm-341-disk-10.raw,format=raw,size=128K',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ # NOTE get_efivars_size is mocked and returns 128K
+ name => '341_running_efidisk',
+ target => 'pve1',
+ vmid => 341,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-lvm:vm-341-disk-10' => 1,
+ },
+ vm_config => get_patched_config(341, {
+ efidisk0 => 'local-lvm:vm-341-disk-10,format=raw,size=128K',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_vdisk_alloc_and_pvesm_free_fail',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ fail_config => {
+ vdisk_alloc => 'local-dir:149/vm-149-disk-11.qcow2',
+ pvesm_free => 'local-lvm:vm-149-disk-10',
+ },
+ expected_calls => {},
+ expect_die => "remote command failed with exit code",
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {
+ 'local-lvm:vm-149-disk-10' => 1,
+ },
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_vdisk_alloc_fail',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ fail_config => {
+ vdisk_alloc => 'local-lvm:vm-149-disk-10',
+ },
+ expected_calls => {},
+ expect_die => "remote command failed with exit code",
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {},
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_vdisk_free_fail',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 0,
+ },
+ opts => {
+ 'with-local-disks' => 1,
+ },
+ fail_config => {
+ 'vdisk_free' => 'local-lvm:vm-149-disk-0',
+ },
+ expected_calls => $default_expected_calls_offline,
+ expect_die => "vdisk_free 'local-lvm:vm-149-disk-0' error",
+ expected => {
+ source_volids => {
+ 'local-lvm:vm-149-disk-0' => 1,
+ },
+ target_volids => local_volids_for_vm(149),
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '105_replicated_run_replication_fail',
+ target => 'pve2',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ target_volids => local_volids_for_vm(105),
+ fail_config => {
+ run_replication => 1,
+ },
+ expected_calls => {},
+ expect_die => 'run_replication error',
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => local_volids_for_vm(105),
+ vm_config => $vm_configs->{105},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '1033_running_query_migrate_fail',
+ target => 'pve2',
+ vmid => 1033,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ },
+ fail_config => {
+ 'query-migrate' => 1,
+ },
+ expected_calls => {},
+ expect_die => 'online migrate failure - aborting',
+ expected => {
+ source_volids => {},
+ target_volids => {},
+ vm_config => $vm_configs->{1033},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '4567_targetstorage_dirotherdir',
+ target => 'pve1',
+ vmid => 4567,
+ vm_status => {
+ running => 0,
+ },
+ opts => {
+ targetstorage => 'local-dir:other-dir,local-zfs:local-zfs',
+ },
+ storage_migrate_map => {
+ 'local-dir:4567/vm-4567-disk-0.qcow2' => '4567/vm-4567-disk-0.qcow2',
+ 'local-dir:4567/vm-4567-state-snap1.raw' => '4567/vm-4567-state-snap1.raw',
+ 'local-dir:4567/vm-4567-state-snap2.raw' => '4567/vm-4567-state-snap2.raw',
+ },
+ expected_calls => $default_expected_calls_offline,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'other-dir:4567/vm-4567-disk-0.qcow2' => 1,
+ 'other-dir:4567/vm-4567-state-snap1.raw' => 1,
+ 'other-dir:4567/vm-4567-state-snap2.raw' => 1,
+ 'local-zfs:vm-4567-disk-0' => 1,
+ },
+ vm_config => get_patched_config(4567, {
+ 'scsi0' => 'other-dir:4567/vm-4567-disk-0.qcow2,size=4G',
+ snapshots => {
+ snap1 => {
+ 'scsi0' => 'other-dir:4567/vm-4567-disk-0.qcow2,size=4G',
+ 'vmstate' => 'other-dir:4567/vm-4567-state-snap1.raw',
+ },
+ snap2 => {
+ 'scsi0' => 'other-dir:4567/vm-4567-disk-0.qcow2,size=4G',
+ 'scsi1' => 'local-zfs:vm-4567-disk-0,size=1G',
+ 'vmstate' => 'other-dir:4567/vm-4567-state-snap2.raw',
+ },
+ },
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '4567_running',
+ target => 'pve1',
+ vmid => 4567,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-i440fx-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ expected_calls => {},
+ expect_die => 'online storage migration not possible if non-replicated snapshot exists',
+ expected => {
+ source_volids => local_volids_for_vm(4567),
+ target_volids => {},
+ vm_config => $vm_configs->{4567},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-i440fx-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '4567_offline',
+ target => 'pve1',
+ vmid => 4567,
+ vm_status => {
+ running => 0,
+ },
+ expected_calls => $default_expected_calls_offline,
+ expected => {
+ source_volids => {},
+ target_volids => local_volids_for_vm(4567),
+ vm_config => $vm_configs->{4567},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '149_running_orphaned_disk_targetstorage_zfs',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ targetstorage => 'local-zfs',
+ },
+ config_patch => {
+ scsi1 => undef,
+ },
+ storage_migrate_map => {
+ 'local-dir:149/vm-149-disk-0.qcow2' => 'vm-149-disk-0',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {
+ 'local-dir:149/vm-149-disk-0.qcow2' => 1,
+ },
+ target_volids => {
+ 'local-zfs:vm-149-disk-10' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-zfs:vm-149-disk-10,format=raw,size=4G',
+ scsi1 => undef,
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_orphaned_disk',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ config_patch => {
+ scsi1 => undef,
+ },
+ storage_migrate_map => {
+ 'local-dir:149/vm-149-disk-0.qcow2' => '149/vm-149-disk-0.qcow2',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {
+ 'local-dir:149/vm-149-disk-0.qcow2' => 1,
+ },
+ target_volids => {
+ 'local-lvm:vm-149-disk-10' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-lvm:vm-149-disk-10,format=raw,size=4G',
+ scsi1 => undef,
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ # FIXME: This test is not (yet) a realistic situation, because
+ # storage_migrate currently never changes the format (AFAICT)
+ # But if such migrations become possible, we need to either update
+ # the 'format' property or simply remove it for drives migrated
+ # with storage_migrate (the property is optional, so it shouldn't be a problem)
+ name => '149_targetstorage_map_lvmzfs_defaultlvm',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 0,
+ },
+ opts => {
+ targetstorage => 'local-lvm:local-zfs,local-lvm',
+ },
+ storage_migrate_map => {
+ 'local-lvm:vm-149-disk-0' => 'vm-149-disk-0',
+ 'local-dir:149/vm-149-disk-0.qcow2' => 'vm-149-disk-0',
+ },
+ expected_calls => $default_expected_calls_offline,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-zfs:vm-149-disk-0' => 1,
+ 'local-lvm:vm-149-disk-0' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-zfs:vm-149-disk-0,format=raw,size=4G',
+ scsi1 => 'local-lvm:vm-149-disk-0,format=qcow2,size=1G',
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ # FIXME same as for the previous test
+ name => '149_targetstorage_map_dirzfs_lvmdir',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 0,
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ targetstorage => 'local-dir:local-zfs,local-lvm:local-dir',
+ },
+ storage_migrate_map => {
+ 'local-lvm:vm-149-disk-0' => '149/vm-149-disk-0.raw',
+ 'local-dir:149/vm-149-disk-0.qcow2' => 'vm-149-disk-0',
+ },
+ expected_calls => $default_expected_calls_offline,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-dir:149/vm-149-disk-0.raw' => 1,
+ 'local-zfs:vm-149-disk-0' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-dir:149/vm-149-disk-0.raw,format=raw,size=4G',
+ scsi1 => 'local-zfs:vm-149-disk-0,format=qcow2,size=1G',
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '149_running_targetstorage_map_lvmzfs_defaultlvm',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ targetstorage => 'local-lvm:local-zfs,local-lvm',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-zfs:vm-149-disk-10' => 1,
+ 'local-lvm:vm-149-disk-11' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-zfs:vm-149-disk-10,format=raw,size=4G',
+ scsi1 => 'local-lvm:vm-149-disk-11,format=raw,size=1G',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_targetstorage_map_lvmzfs_dirdir',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ targetstorage => 'local-lvm:local-zfs,local-dir:local-dir',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-zfs:vm-149-disk-10' => 1,
+ 'local-dir:149/vm-149-disk-11.qcow2' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-zfs:vm-149-disk-10,format=raw,size=4G',
+ scsi1 => 'local-dir:149/vm-149-disk-11.qcow2,format=qcow2,size=1G',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_targetstorage_zfs',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ targetstorage => 'local-zfs',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-zfs:vm-149-disk-10' => 1,
+ 'local-zfs:vm-149-disk-11' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-zfs:vm-149-disk-10,format=raw,size=4G',
+ scsi1 => 'local-zfs:vm-149-disk-11,format=raw,size=1G',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_wrong_size',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ config_patch => {
+ scsi0 => 'local-lvm:vm-149-disk-0,size=123T',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-lvm:vm-149-disk-10' => 1,
+ 'local-dir:149/vm-149-disk-11.qcow2' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-lvm:vm-149-disk-10,format=raw,size=4G',
+ scsi1 => 'local-dir:149/vm-149-disk-11.qcow2,format=qcow2,size=1G',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_missing_size',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ config_patch => {
+ scsi0 => 'local-lvm:vm-149-disk-0',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-lvm:vm-149-disk-10' => 1,
+ 'local-dir:149/vm-149-disk-11.qcow2' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-lvm:vm-149-disk-10,format=raw,size=4G',
+ scsi1 => 'local-dir:149/vm-149-disk-11.qcow2,format=qcow2,size=1G',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '105_local_device_shared',
+ target => 'pve1',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ config_patch => {
+ ide2 => '/dev/sde,shared=1',
+ },
+ expected_calls => $default_expected_calls_offline,
+ expected => {
+ source_volids => {},
+ target_volids => local_volids_for_vm(105),
+ vm_config => get_patched_config(105, {
+ ide2 => '/dev/sde,shared=1',
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '105_local_device_in_snapshot',
+ target => 'pve1',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ config_patch => {
+ snapshots => {
+ ohsnap => {
+ ide2 => '/dev/sde',
+ },
+ },
+ },
+ expected_calls => {},
+ expect_die => "can't migrate local disk '/dev/sde': local file/device",
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => {},
+ vm_config => get_patched_config(105, {
+ snapshots => {
+ ohsnap => {
+ ide2 => '/dev/sde',
+ },
+ },
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '105_local_device',
+ target => 'pve1',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ config_patch => {
+ ide2 => '/dev/sde',
+ },
+ expected_calls => {},
+ expect_die => "can't migrate local disk '/dev/sde': local file/device",
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => {},
+ vm_config => get_patched_config(105, {
+ ide2 => '/dev/sde',
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '105_cdrom_in_snapshot',
+ target => 'pve1',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ config_patch => {
+ snapshots => {
+ ohsnap => {
+ ide2 => 'cdrom,media=cdrom',
+ },
+ },
+ },
+ expected_calls => {},
+ expect_die => "can't migrate local cdrom drive (referenced in snapshot - ohsnap",
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => {},
+ vm_config => get_patched_config(105, {
+ snapshots => {
+ ohsnap => {
+ ide2 => 'cdrom,media=cdrom',
+ },
+ },
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '105_cdrom',
+ target => 'pve1',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ config_patch => {
+ ide2 => 'cdrom,media=cdrom',
+ },
+ expected_calls => {},
+ expect_die => "can't migrate local cdrom drive",
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => {},
+ vm_config => get_patched_config(105, {
+ ide2 => 'cdrom,media=cdrom',
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '149_running_missing_option_withlocaldisks',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ },
+ expected_calls => {},
+ expect_die => "can't live migrate attached local disks without with-local-disks option",
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {},
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_missing_option_online',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ 'with-local-disks' => 1,
+ },
+ expected_calls => {},
+ expect_die => "can't migrate running VM without --online",
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {},
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '1033_running_customcpu',
+ target => 'pve1',
+ vmid => 1033,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ runningcpu => 'host,+kvm_pv_eoi,+kvm_pv_unhalt',
+ },
+ opts => {
+ online => 1,
+ },
+ config_patch => {
+ cpu => 'custom-mycpu',
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {},
+ vm_config => get_patched_config(1033, {
+ cpu => 'custom-mycpu',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ runningcpu => 'host,+kvm_pv_eoi,+kvm_pv_unhalt',
+ },
+ },
+ },
+ {
+ name => '105_replicated_to_non_replication_target',
+ target => 'pve1',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ target_volids => {},
+ expected_calls => $replicated_expected_calls_offline,
+ expected => {
+ source_volids => {},
+ target_volids => local_volids_for_vm(105),
+ vm_config => $vm_configs->{105},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '105_running_replicated',
+ target => 'pve2',
+ vmid => 105,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-i440fx-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ target_volids => local_volids_for_vm(105),
+ expected_calls => {
+ %{$replicated_expected_calls_online},
+ 'block-dirty-bitmap-add-drive-scsi0' => 1,
+ 'block-dirty-bitmap-add-drive-ide0' => 1,
+ },
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => local_volids_for_vm(105),
+ vm_config => $vm_configs->{105},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-i440fx-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '105_replicated',
+ target => 'pve2',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ target_volids => local_volids_for_vm(105),
+ expected_calls => $replicated_expected_calls_offline,
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => local_volids_for_vm(105),
+ vm_config => $vm_configs->{105},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '105_running_replicated_without_snapshot',
+ target => 'pve2',
+ vmid => 105,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-i440fx-5.0+pve0',
+ },
+ config_patch => {
+ snapshots => undef,
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ target_volids => local_volids_for_vm(105),
+ expected_calls => {
+ %{$replicated_expected_calls_online},
+ 'block-dirty-bitmap-add-drive-scsi0' => 1,
+ 'block-dirty-bitmap-add-drive-ide0' => 1,
+ },
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => local_volids_for_vm(105),
+ vm_config => get_patched_config(105, {
+ snapshots => {},
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-i440fx-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '105_replicated_without_snapshot',
+ target => 'pve2',
+ vmid => 105,
+ vm_status => {
+ running => 0,
+ },
+ config_patch => {
+ snapshots => undef,
+ },
+ opts => {
+ online => 1,
+ },
+ target_volids => local_volids_for_vm(105),
+ expected_calls => $replicated_expected_calls_offline,
+ expected => {
+ source_volids => local_volids_for_vm(105),
+ target_volids => local_volids_for_vm(105),
+ vm_config => get_patched_config(105, {
+ snapshots => {},
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '1033_running',
+ target => 'pve2',
+ vmid => 1033,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {},
+ vm_config => $vm_configs->{1033},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_locked',
+ target => 'pve2',
+ vmid => 149,
+ vm_status => {
+ running => 0,
+ },
+ config_patch => {
+ lock => 'locked',
+ },
+ expected_calls => {},
+ expect_die => "VM is locked",
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {},
+ vm_config => get_patched_config(149, {
+ lock => 'locked',
+ }),
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '149_storage_not_available',
+ target => 'pve2',
+ vmid => 149,
+ vm_status => {
+ running => 0,
+ },
+ expected_calls => {},
+ expect_die => "storage 'local-lvm' is not available on node 'pve2'",
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {},
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '149_running',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-lvm:vm-149-disk-10' => 1,
+ 'local-dir:149/vm-149-disk-11.qcow2' => 1,
+ },
+ vm_config => get_patched_config(149, {
+ scsi0 => 'local-lvm:vm-149-disk-10,format=raw,size=4G',
+ scsi1 => 'local-dir:149/vm-149-disk-11.qcow2,format=qcow2,size=1G',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_drive_mirror_fail',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ expected_calls => {},
+ expect_die => "qemu_drive_mirror 'scsi1' error",
+ fail_config => {
+ 'qemu_drive_mirror' => 'scsi1',
+ },
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {},
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_running_unused_block_job_cancel_fail',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ config_patch => {
+ scsi1 => undef,
+ unused0 => 'local-dir:149/vm-149-disk-0.qcow2',
+ },
+ expected_calls => {},
+ expect_die => "qemu_drive_mirror_monitor 'cancel' error",
+ # note that 'cancel' is also used to finish and that's what this test is about
+ fail_config => {
+ 'qemu_drive_mirror_monitor' => 'cancel',
+ },
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {},
+ vm_config => get_patched_config(149, {
+ scsi1 => undef,
+ unused0 => 'local-dir:149/vm-149-disk-0.qcow2',
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '149_offline',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 0,
+ },
+ opts => {
+ 'with-local-disks' => 1,
+ },
+ expected_calls => $default_expected_calls_offline,
+ expected => {
+ source_volids => {},
+ target_volids => local_volids_for_vm(149),
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '149_storage_migrate_fail',
+ target => 'pve1',
+ vmid => 149,
+ vm_status => {
+ running => 0,
+ },
+ opts => {
+ 'with-local-disks' => 1,
+ },
+ fail_config => {
+ 'storage_migrate' => 'local-lvm:vm-149-disk-0',
+ },
+ expected_calls => {},
+ expect_die => "storage_migrate 'local-lvm:vm-149-disk-0' error",
+ expected => {
+ source_volids => local_volids_for_vm(149),
+ target_volids => {},
+ vm_config => $vm_configs->{149},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+ {
+ name => '111_running_pending',
+ target => 'pve1',
+ vmid => 111,
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ opts => {
+ online => 1,
+ 'with-local-disks' => 1,
+ },
+ expected_calls => $default_expected_calls_online,
+ expected => {
+ source_volids => {},
+ target_volids => {
+ 'local-zfs:vm-111-disk-0' => 1,
+ 'local-lvm:vm-111-disk-10' => 1,
+ },
+ vm_config => get_patched_config(111, {
+ ide0 => 'local-lvm:vm-111-disk-10,format=raw,size=4G',
+ pending => {
+ scsi0 => 'local-zfs:vm-111-disk-0,size=103M',
+ },
+ }),
+ vm_status => {
+ running => 1,
+ runningmachine => 'pc-q35-5.0+pve0',
+ },
+ },
+ },
+ {
+ name => '123_alias_fail',
+ target => 'pve1',
+ vmid => 123,
+ vm_status => {
+ running => 0,
+ },
+ opts => {
+ 'with-local-disks' => 1,
+ },
+ expected_calls => {},
+ expect_die => "detected not supported aliased volumes",
+ expected => {
+ source_volids => local_volids_for_vm(123),
+ target_volids => {},
+ vm_config => $vm_configs->{123},
+ vm_status => {
+ running => 0,
+ },
+ },
+ },
+];
+
+my $single_test_name = shift;
+
+mkdir $RUN_DIR_PATH;
+
+foreach my $test (@{$tests}) {
+ my $name = $test->{name};
+ next if defined($single_test_name) && $name ne $single_test_name;
+
+ my $run_dir = "${RUN_DIR_PATH}/${name}";
+
+ mkdir $run_dir;
+ 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));
+
+ my $expect_die = $test->{expect_die};
+ my $expected = $test->{expected};
+
+ my $source_volids = local_volids_for_vm($test->{vmid});
+ my $target_volids = $test->{target_volids} // {};
+
+ my $config_patch = $test->{config_patch};
+ my $vm_config = get_patched_config($test->{vmid}, $test->{config_patch});
+
+ my $fail_config = $test->{fail_config} // {};
+ my $storage_migrate_map = $test->{storage_migrate_map} // {};
+
+ if (my $targetstorage = $test->{opts}->{targetstorage}) {
+ $test->{opts}->{storagemap} = PVE::JSONSchema::parse_idmap($targetstorage, 'pve-storage-id');
+ }
+
+ my $migrate_params = {
+ target => $test->{target},
+ vmid => $test->{vmid},
+ opts => $test->{opts},
+ };
+
+ file_set_contents("${run_dir}/nbd_info", to_json({}));
+ file_set_contents("${run_dir}/source_volids", to_json($source_volids));
+ file_set_contents("${run_dir}/target_volids", to_json($target_volids));
+ file_set_contents("${run_dir}/vm_config", to_json($vm_config));
+ file_set_contents("${run_dir}/vm_status", to_json($test->{vm_status}));
+ file_set_contents("${run_dir}/expected_calls", to_json($test->{expected_calls}));
+ file_set_contents("${run_dir}/fail_config", to_json($fail_config));
+ file_set_contents("${run_dir}/storage_migrate_map", to_json($storage_migrate_map));
+ file_set_contents("${run_dir}/migrate_params", to_json($migrate_params));
+
+ $ENV{QM_LIB_PATH} = $QM_LIB_PATH;
+ $ENV{RUN_DIR_PATH} = $run_dir;
+ my $exitcode = run_command([
+ '/usr/bin/perl',
+ "-I${MIGRATE_LIB_PATH}",
+ "-I${MIGRATE_LIB_PATH}/test",
+ "${MIGRATE_LIB_PATH}/test/MigrationTest/QemuMigrateMock.pm",
+ ], noerr => 1, errfunc => sub {print "#$name - $_[0]\n"} );
+
+ if (defined($expect_die) && $exitcode) {
+ my $log = file_get_contents("${run_dir}/log");
+ my @lines = split /\n/, $log;
+
+ my $matched = 0;
+ foreach my $line (@lines) {
+ $matched = 1 if $line =~ m/^err:.*\Q${expect_die}\E/;
+ $matched = 1 if $line =~ m/^warn:.*\Q${expect_die}\E/;
+ }
+ if (!$matched) {
+ fail($name);
+ note("expected error message is not present in log");
+ }
+ } elsif (defined($expect_die) && !$exitcode) {
+ fail($name);
+ note("mocked migrate call didn't fail, but it was expected to - check log");
+ } elsif (!defined($expect_die) && $exitcode) {
+ fail($name);
+ note("mocked migrate call failed, but it was not expected - check log");
+ }
+
+ my $expected_calls = decode_json(file_get_contents("${run_dir}/expected_calls"));
+ foreach my $call (keys %{$expected_calls}) {
+ fail($name);
+ note("expected call '$call' was not made");
+ }
+
+ if (!defined($expect_die)) {
+ my $nbd_info = decode_json(file_get_contents("${run_dir}/nbd_info"));
+ foreach my $drive (keys %{$nbd_info}) {
+ fail($name);
+ note("drive '$drive' was not mirrored");
+ }
+ }
+
+ my $actual = {
+ source_volids => decode_json(file_get_contents("${run_dir}/source_volids")),
+ target_volids => decode_json(file_get_contents("${run_dir}/target_volids")),
+ vm_config => decode_json(file_get_contents("${run_dir}/vm_config")),
+ vm_status => decode_json(file_get_contents("${run_dir}/vm_status")),
+ };
+
+ is_deeply($actual, $expected, $name);
+}
+
+done_testing();