tests: cover restored VM disk hotplug after TCP migration

Extend the TCP live-migration test with a hotplugged block device using
a stable ID before migration. After migration, verify that the disk
still exists on the restored destination VM.

Then hot-remove the disk and add it again with the same ID. This covers
the stale restore snapshot case because the disk ID exists in the
migration snapshot, but the live device tree no longer contains it after
hot-remove.

Assisted-by: Codex:GPT-5.5
On-behalf-of: SAP philipp.schuster@sap.com
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
This commit is contained in:
Philipp Schuster
2026-05-19 10:56:34 +02:00
committed by Rob Bradford
parent 44ed81e66f
commit 1924153185

View File

@@ -6730,6 +6730,23 @@ mod common_parallel {
.output()
.expect("Expect creating disk image to succeed");
let pmem_path = String::from("/dev/pmem0");
let mut hotplug_blk_file_path = dirs::home_dir().unwrap();
hotplug_blk_file_path.push("workloads");
hotplug_blk_file_path.push("blk.img");
let hotplug_disk_id = "test0";
let hotplug_disk_params = format!(
"path={},id={hotplug_disk_id},readonly=true",
hotplug_blk_file_path.to_str().unwrap()
);
// The hotplugged disk is expected to appear as /dev/vdc and blk.img is
// the 16 MiB workload image used by the disk hotplug tests.
let hotplug_disk_count_is = |expected| {
guest
.ssh_command("lsblk | grep -c 'vdc.*16M' || true")
.is_ok_and(|s| s.trim().parse::<u32>().is_ok_and(|count| count == expected))
};
let hotplug_disk_exists = || hotplug_disk_count_is(1);
let hotplug_disk_absent = || hotplug_disk_count_is(0);
// Start the source VM
let src_vm_path = clh_command("cloud-hypervisor");
@@ -6773,7 +6790,10 @@ mod common_parallel {
assert!(guest.get_total_memory().unwrap_or_default() > 1_400_000);
guest.check_devices_common(None, Some(&console_text), Some(&pmem_path));
// On x86_64 architecture, remove and re-add the virtio-net device
// Test hot(re)plugging works before a migration.
//
// This currently excludes ARM, because on ARM we boot without OVMF,
// using direct kernel boot, where ACPI support is missing.
#[cfg(target_arch = "x86_64")]
{
assert!(remote_command(
@@ -6791,6 +6811,14 @@ mod common_parallel {
Some(net_params.as_str()),
));
guest.wait_for_ssh(Duration::from_secs(10)).unwrap();
assert!(hotplug_disk_absent());
assert!(remote_command(
&src_api_socket,
"add-disk",
Some(hotplug_disk_params.as_str()),
));
assert!(wait_until(Duration::from_secs(10), hotplug_disk_exists));
}
// Start TCP live migration
assert!(
@@ -6827,6 +6855,29 @@ mod common_parallel {
assert_eq!(guest.get_cpu_count().unwrap_or_default(), boot_vcpus);
assert!(guest.get_total_memory().unwrap_or_default() > 1_400_000);
guest.check_devices_common(None, Some(&console_text), Some(&pmem_path));
// Test hot(re)plugging works after a migration.
//
// This currently excludes ARM, because on ARM we boot without OVMF,
// using direct kernel boot, where ACPI support is missing.
#[cfg(target_arch = "x86_64")]
{
assert!(hotplug_disk_exists());
assert!(remote_command(
&dest_api_socket,
"remove-device",
Some(hotplug_disk_id),
));
assert!(wait_until(Duration::from_secs(10), hotplug_disk_absent));
assert!(remote_command(
&dest_api_socket,
"add-disk",
Some(hotplug_disk_params.as_str()),
));
assert!(wait_until(Duration::from_secs(10), hotplug_disk_exists));
}
});
// Clean up the destination VM and ensure it terminates properly
@@ -6839,6 +6890,13 @@ mod common_parallel {
assert!(String::from_utf8_lossy(&dest_output.stdout).contains(&console_text));
});
handle_child_output(r, &dest_output);
#[cfg(not(target_arch = "x86_64"))]
{
let _ = hotplug_disk_params;
let _ = hotplug_disk_exists;
let _ = hotplug_disk_absent;
}
}
#[cfg(not(feature = "mshv"))]