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"))]