diff --git a/cloud-hypervisor/tests/integration.rs b/cloud-hypervisor/tests/integration.rs index 6c580bac0..59db256f1 100644 --- a/cloud-hypervisor/tests/integration.rs +++ b/cloud-hypervisor/tests/integration.rs @@ -2539,6 +2539,88 @@ EOF assert_eq!(test_message_write, file_message); } +fn _test_simple_launch(guest: &Guest) { + let event_path = temp_event_monitor_path(&guest.tmp_dir); + + let mut child = GuestCommand::new(guest) + .args(["--cpus", "boot=1"]) + .args(["--memory", "size=512M"]) + .default_kernel_cmdline() + .default_disks() + .default_net() + .args(["--serial", "tty", "--console", "off"]) + .args(["--event-monitor", format!("path={event_path}").as_str()]) + .capture_output() + .spawn() + .unwrap(); + + let r = std::panic::catch_unwind(|| { + guest.wait_vm_boot(None).unwrap(); + + assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1); + assert!(guest.get_total_memory().unwrap_or_default() > 480_000); + assert_eq!(guest.get_pci_bridge_class().unwrap_or_default(), "0x060000"); + + let expected_sequential_events = [ + &MetaEvent { + event: "starting".to_string(), + device_id: None, + }, + &MetaEvent { + event: "booting".to_string(), + device_id: None, + }, + &MetaEvent { + event: "booted".to_string(), + device_id: None, + }, + &MetaEvent { + event: "activated".to_string(), + device_id: Some("_disk0".to_string()), + }, + &MetaEvent { + event: "reset".to_string(), + device_id: Some("_disk0".to_string()), + }, + ]; + assert!(check_sequential_events( + &expected_sequential_events, + &event_path + )); + + // It's been observed on the Bionic image that udev and snapd + // services can cause some delay in the VM's shutdown. Disabling + // them improves the reliability of this test. + let _ = guest.ssh_command("sudo systemctl disable udev"); + let _ = guest.ssh_command("sudo systemctl stop udev"); + let _ = guest.ssh_command("sudo systemctl disable snapd"); + let _ = guest.ssh_command("sudo systemctl stop snapd"); + + guest.ssh_command("sudo poweroff").unwrap(); + thread::sleep(std::time::Duration::new(20, 0)); + let latest_events = [ + &MetaEvent { + event: "shutdown".to_string(), + device_id: None, + }, + &MetaEvent { + event: "deleted".to_string(), + device_id: None, + }, + &MetaEvent { + event: "shutdown".to_string(), + device_id: None, + }, + ]; + assert!(check_latest_events_exact(&latest_events, &event_path)); + }); + + kill_child(&mut child); + let output = child.wait_with_output().unwrap(); + + handle_child_output(r, &output); +} + mod common_parallel { use std::cmp; use std::fs::{File, OpenOptions, copy}; @@ -2550,100 +2632,19 @@ mod common_parallel { #[test] #[cfg(target_arch = "x86_64")] fn test_focal_hypervisor_fw() { - let path = fw_path(FwType::RustHypervisorFirmware); - test_simple_launch(&path, FOCAL_IMAGE_NAME); + let disk_config = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string()); + let mut guest = Guest::new(Box::new(disk_config)); + guest.kernel_path = Some(fw_path(FwType::RustHypervisorFirmware)); + _test_simple_launch(&guest) } #[test] #[cfg(target_arch = "x86_64")] fn test_focal_ovmf() { - let path = fw_path(FwType::Ovmf); - test_simple_launch(&path, FOCAL_IMAGE_NAME); - } - - #[cfg(target_arch = "x86_64")] - fn test_simple_launch(fw_path: &str, disk_path: &str) { - let disk_config = Box::new(UbuntuDiskConfig::new(disk_path.to_string())); - let guest = Guest::new(disk_config); - let event_path = temp_event_monitor_path(&guest.tmp_dir); - - let mut child = GuestCommand::new(&guest) - .args(["--cpus", "boot=1"]) - .args(["--memory", "size=512M"]) - .args(["--kernel", fw_path]) - .default_disks() - .default_net() - .args(["--serial", "tty", "--console", "off"]) - .args(["--event-monitor", format!("path={event_path}").as_str()]) - .capture_output() - .spawn() - .unwrap(); - - let r = std::panic::catch_unwind(|| { - guest.wait_vm_boot(Some(120)).unwrap(); - - assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1); - assert!(guest.get_total_memory().unwrap_or_default() > 480_000); - assert_eq!(guest.get_pci_bridge_class().unwrap_or_default(), "0x060000"); - - let expected_sequential_events = [ - &MetaEvent { - event: "starting".to_string(), - device_id: None, - }, - &MetaEvent { - event: "booting".to_string(), - device_id: None, - }, - &MetaEvent { - event: "booted".to_string(), - device_id: None, - }, - &MetaEvent { - event: "activated".to_string(), - device_id: Some("_disk0".to_string()), - }, - &MetaEvent { - event: "reset".to_string(), - device_id: Some("_disk0".to_string()), - }, - ]; - assert!(check_sequential_events( - &expected_sequential_events, - &event_path - )); - - // It's been observed on the Bionic image that udev and snapd - // services can cause some delay in the VM's shutdown. Disabling - // them improves the reliability of this test. - let _ = guest.ssh_command("sudo systemctl disable udev"); - let _ = guest.ssh_command("sudo systemctl stop udev"); - let _ = guest.ssh_command("sudo systemctl disable snapd"); - let _ = guest.ssh_command("sudo systemctl stop snapd"); - - guest.ssh_command("sudo poweroff").unwrap(); - thread::sleep(std::time::Duration::new(20, 0)); - let latest_events = [ - &MetaEvent { - event: "shutdown".to_string(), - device_id: None, - }, - &MetaEvent { - event: "deleted".to_string(), - device_id: None, - }, - &MetaEvent { - event: "shutdown".to_string(), - device_id: None, - }, - ]; - assert!(check_latest_events_exact(&latest_events, &event_path)); - }); - - kill_child(&mut child); - let output = child.wait_with_output().unwrap(); - - handle_child_output(r, &output); + let disk_config = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string()); + let mut guest = Guest::new(Box::new(disk_config)); + guest.kernel_path = Some(fw_path(FwType::Ovmf)); + _test_simple_launch(&guest) } #[test] diff --git a/test_infra/src/lib.rs b/test_infra/src/lib.rs index 2cd8527c7..25548b557 100644 --- a/test_infra/src/lib.rs +++ b/test_infra/src/lib.rs @@ -74,7 +74,7 @@ pub struct GuestNetworkConfig { pub const DEFAULT_TCP_LISTENER_MESSAGE: &str = "booted"; pub const DEFAULT_TCP_LISTENER_PORT: u16 = 8000; -pub const DEFAULT_TCP_LISTENER_TIMEOUT: i32 = 120; +pub const DEFAULT_TCP_LISTENER_TIMEOUT: u32 = 120; #[derive(Error, Debug)] pub enum WaitForBootError { @@ -91,7 +91,7 @@ pub enum WaitForBootError { } impl GuestNetworkConfig { - pub fn wait_vm_boot(&self, custom_timeout: Option) -> Result<(), WaitForBootError> { + pub fn wait_vm_boot(&self, custom_timeout: Option) -> 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); @@ -122,14 +122,15 @@ impl GuestNetworkConfig { .expect("Cannot add 'tcp_listener' event to epoll"); let mut events = [epoll::Event::new(epoll::Events::empty(), 0); 1]; loop { - let num_events = match epoll::wait(epoll_fd, timeout * 1000_i32, &mut events[..]) { - Ok(num_events) => Ok(num_events), - Err(e) => match e.raw_os_error() { - Some(libc::EAGAIN) | Some(libc::EINTR) => continue, - _ => Err(e), - }, - } - .map_err(WaitForBootError::EpollWait)?; + let num_events = + match epoll::wait(epoll_fd, (timeout * 1000) as i32, &mut events[..]) { + Ok(num_events) => Ok(num_events), + Err(e) => match e.raw_os_error() { + Some(libc::EAGAIN) | Some(libc::EINTR) => continue, + _ => Err(e), + }, + } + .map_err(WaitForBootError::EpollWait)?; if num_events == 0 { return Err(WaitForBootError::EpollWaitTimeout); } @@ -887,6 +888,10 @@ pub struct Guest { pub tmp_dir: TempDir, pub disk_config: Box, pub network: GuestNetworkConfig, + pub vm_type: GuestVmType, + pub boot_timeout: u32, + pub kernel_path: Option, + pub kernel_cmdline: Option, } // Return the next id that can be used for this guest. This is stored in a @@ -951,6 +956,10 @@ impl Guest { tmp_dir, disk_config, network, + vm_type: GuestVmType::Regular, + boot_timeout: DEFAULT_TCP_LISTENER_TIMEOUT, + kernel_path: None, + kernel_cmdline: None, } } @@ -1076,7 +1085,7 @@ impl Guest { .map_err(Error::Parsing) } - pub fn wait_vm_boot(&self, custom_timeout: Option) -> Result<(), Error> { + pub fn wait_vm_boot(&self, custom_timeout: Option) -> Result<(), Error> { self.network .wait_vm_boot(custom_timeout) .map_err(Error::WaitForBoot) @@ -1214,7 +1223,7 @@ impl Guest { ); } - pub fn reboot_linux(&self, current_reboot_count: u32, custom_timeout: Option) { + pub fn reboot_linux(&self, current_reboot_count: u32, custom_timeout: Option) { let list_boots_cmd = "sudo last | grep -c reboot"; let boot_count = self .ssh_command(list_boots_cmd) @@ -1461,6 +1470,17 @@ impl<'a> GuestCommand<'a> { pub fn default_net(&mut self) -> &mut Self { self.args(["--net", self.guest.default_net_string().as_str()]) } + + pub fn default_kernel_cmdline(&mut self) -> &mut Self { + if let Some(kernel) = &self.guest.kernel_path { + self.command.args(["--kernel", kernel]); + if let Some(cmdline) = &self.guest.kernel_cmdline { + self.command.args(["--cmdline", cmdline]); + } + } + + self + } } /// Returns the absolute path into the workspaces target directory to locate the desired @@ -1858,3 +1878,9 @@ pub fn extract_bar_address(output: &str, device_desc: &str, bar_index: usize) -> } None } + +#[derive(PartialEq, Clone, Copy)] +pub enum GuestVmType { + Regular, + Confidential, +}