From 12f48700a2fd6e57fe219ace6c36f9ac62e333b9 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 6 May 2026 11:06:15 +0100 Subject: [PATCH] tests: Wait for boot notification from test_vfio_user L2 guest Reuse the boot notification method we have for the L1 guests for the L2 guest. This removes the need to use SSH based boot tracking for connecfting to the L2 guest and should make the test more reliable. This requires making the L2 guest use a different cloud-init configuration to the L1. Signed-off-by: Rob Bradford --- cloud-hypervisor/tests/integration.rs | 33 ++++++++++----------------- test_infra/src/lib.rs | 27 ++++++++++++++++++---- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/cloud-hypervisor/tests/integration.rs b/cloud-hypervisor/tests/integration.rs index 861044508..34379297f 100644 --- a/cloud-hypervisor/tests/integration.rs +++ b/cloud-hypervisor/tests/integration.rs @@ -2359,13 +2359,11 @@ mod common_parallel { let mut cloud_init_vfio_base_path = vfio_path.clone(); cloud_init_vfio_base_path.push("cloudinit.img"); - // We copy our cloudinit into the vfio mount point, for the nested - // cloud-hypervisor guest to use. - rate_limited_copy( - guest.disk_config.disk(DiskType::CloudInit).unwrap(), - &cloud_init_vfio_base_path, - ) - .expect("copying of cloud-init disk failed"); + // Prepare a separate cloud-init for the L2 guest with its own + // boot notification port. + let (_l2_ci_dir, l2_ci_path) = guest.prepare_l2_cloudinit(); + rate_limited_copy(l2_ci_path, &cloud_init_vfio_base_path) + .expect("copying of L2 cloud-init disk failed"); let mut vfio_disk_path = workload_path.clone(); vfio_disk_path.push("vfio.img"); @@ -2444,24 +2442,17 @@ mod common_parallel { let r = std::panic::catch_unwind(|| { guest.ssh_command_l1("sudo systemctl start vfio").unwrap(); + GuestNetworkConfig::wait_vm_boot_from( + guest.network.l2_tcp_listener_port, + &guest.network.l2_guest_ip2, + DEFAULT_TCP_LISTENER_TIMEOUT, + ) + .unwrap(); + let auth = PasswordAuth { username: String::from("cloud"), password: String::from("cloud123"), }; - wait_for_ssh( - "true", - &auth, - &guest.network.l2_guest_ip1, - Duration::from_secs(120), - ) - .unwrap(); - wait_for_ssh( - "true", - &auth, - &guest.network.l2_guest_ip2, - Duration::from_secs(120), - ) - .unwrap(); // We booted our cloud hypervisor L2 guest with a "VFIOTAG" tag // added to its kernel command line. diff --git a/test_infra/src/lib.rs b/test_infra/src/lib.rs index 19a76bb15..c58e6a9c8 100644 --- a/test_infra/src/lib.rs +++ b/test_infra/src/lib.rs @@ -193,6 +193,7 @@ where } } +#[derive(Clone)] pub struct GuestNetworkConfig { pub guest_ip0: String, pub host_ip0: String, @@ -207,6 +208,7 @@ pub struct GuestNetworkConfig { pub l2_guest_mac2: String, pub l2_guest_mac3: String, pub tcp_listener_port: u16, + pub l2_tcp_listener_port: u16, pub notify_ip: String, } @@ -230,11 +232,13 @@ pub enum WaitForBootError { } impl GuestNetworkConfig { - pub fn wait_vm_boot(&self, custom_timeout: u32) -> Result<(), WaitForBootError> { + pub fn wait_vm_boot_from( + port: u16, + expected_guest_addr: &str, + custom_timeout: u32, + ) -> Result<(), WaitForBootError> { let start = std::time::Instant::now(); - // The 'port' is unique per 'GUEST' and listening to wild-card ip avoids retrying on 'TcpListener::bind()' - let listen_addr = format!("0.0.0.0:{}", self.tcp_listener_port); - let expected_guest_addr = self.guest_ip0.as_str(); + let listen_addr = format!("0.0.0.0:{port}"); let mut s = String::new(); let mut closure = || -> Result<(), WaitForBootError> { @@ -314,6 +318,10 @@ impl GuestNetworkConfig { Ok(_) => Ok(()), } } + + pub fn wait_vm_boot(&self, custom_timeout: u32) -> Result<(), WaitForBootError> { + Self::wait_vm_boot_from(self.tcp_listener_port, &self.guest_ip0, custom_timeout) + } } pub enum DiskType { @@ -1203,6 +1211,7 @@ impl Guest { l2_guest_mac2: format!("de:ad:be:ef:34:{id:02x}"), l2_guest_mac3: format!("de:ad:be:ef:56:{id:02x}"), tcp_listener_port: DEFAULT_TCP_LISTENER_PORT + id as u16, + l2_tcp_listener_port: DEFAULT_TCP_LISTENER_PORT + 1024 + id as u16, notify_ip: format!("{class}.{id}.1"), }; @@ -1463,6 +1472,16 @@ impl Guest { .map_err(Error::WaitForBoot) } + pub fn prepare_l2_cloudinit(&self) -> (TempDir, String) { + let l2_dir = TempDir::new_with_prefix("/tmp/ch-l2").unwrap(); + let l2_network = GuestNetworkConfig { + tcp_listener_port: self.network.l2_tcp_listener_port, + ..self.network.clone() + }; + let path = self.disk_config.prepare_cloudinit(&l2_dir, &l2_network); + (l2_dir, path) + } + pub fn check_numa_node_cpus(&self, node_id: usize, cpus: &[usize]) -> Result<(), Error> { for cpu in cpus.iter() { let cmd = format!("[ -d \"/sys/devices/system/node/node{node_id}/cpu{cpu}\" ]");