From 1924153185dc5a7bda99998398d211787ff0e418 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 19 May 2026 10:56:34 +0200 Subject: [PATCH] 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 --- cloud-hypervisor/tests/integration.rs | 60 ++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/cloud-hypervisor/tests/integration.rs b/cloud-hypervisor/tests/integration.rs index 096e6ea9a..26d833964 100644 --- a/cloud-hypervisor/tests/integration.rs +++ b/cloud-hypervisor/tests/integration.rs @@ -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::().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"))]