diff --git a/src/main.rs b/src/main.rs index 5d7e0ee58..7192f2341 100755 --- a/src/main.rs +++ b/src/main.rs @@ -164,133 +164,13 @@ mod tests { use std::thread; use tempdir::TempDir; - fn ssh_command(command: &str) -> String { - let mut s = String::new(); - #[derive(Debug)] - enum Error { - Connection, - Authentication, - Command, - }; - - let mut counter = 0; - loop { - match (|| -> Result<(), Error> { - let tcp = TcpStream::connect("192.168.2.2:22").map_err(|_| Error::Connection)?; - let mut sess = Session::new().unwrap(); - sess.handshake(&tcp).map_err(|_| Error::Connection)?; - - sess.userauth_password("admin", "cloud123") - .map_err(|_| Error::Authentication)?; - assert!(sess.authenticated()); - - let mut channel = sess.channel_session().map_err(|_| Error::Command)?; - channel.exec(command).map_err(|_| Error::Command)?; - - // Intentionally ignore these results here as their failure - // does not precipitate a repeat - let _ = channel.read_to_string(&mut s); - let _ = channel.close(); - let _ = channel.wait_close(); - Ok(()) - })() { - Ok(_) => break, - Err(e) => { - counter += 1; - if counter >= 6 { - panic!("Took too many attempts to run command. Last error: {:?}", e); - } - } - }; - thread::sleep(std::time::Duration::new(10 * counter, 0)); - } - s - } - - fn prepare_cloudinit( - tmp_dir: &TempDir, - guest_ip_addr: &str, - host_ip_addr: &str, - mac_addr: &str, - ) -> String { - let cloudinit_file_path = String::from(tmp_dir.path().join("cloudinit").to_str().unwrap()); - - let cloud_init_directory = tmp_dir.path().join("cloud-init").join("openstack"); - - fs::create_dir_all(&cloud_init_directory.join("latest")) - .expect("Expect creating cloud-init directory to succeed"); - - let source_file_dir = std::env::current_dir() - .unwrap() - .join("test_data") - .join("cloud-init") - .join("openstack") - .join("latest"); - - fs::copy( - source_file_dir.join("meta_data.json"), - cloud_init_directory.join("latest").join("meta_data.json"), - ) - .expect("Expect copying cloud-init meta_data.json to succeed"); - - let mut user_data_string = String::new(); - - fs::File::open(source_file_dir.join("user_data")) - .unwrap() - .read_to_string(&mut user_data_string) - .expect("Expected reading user_data file in to succeed"); - - user_data_string = user_data_string.replace("192.168.2.1", host_ip_addr); - user_data_string = user_data_string.replace("192.168.2.2", guest_ip_addr); - user_data_string = user_data_string.replace("12:34:56:78:90:ab", mac_addr); - - fs::File::create(cloud_init_directory.join("latest").join("user_data")) - .unwrap() - .write_all(&user_data_string.as_bytes()) - .expect("Expected writing out user_data to succeed"); - - std::process::Command::new("mkdosfs") - .args(&["-n", "config-2"]) - .args(&["-C", cloudinit_file_path.as_str()]) - .arg("8192") - .spawn() - .expect("Expect creating disk image to succeed"); - - std::process::Command::new("mcopy") - .arg("-o") - .args(&["-i", cloudinit_file_path.as_str()]) - .args(&["-s", cloud_init_directory.to_str().unwrap(), "::"]) - .spawn() - .expect("Expect copying files to disk image to succeed"); - - cloudinit_file_path - } - - fn prepare_files(tmp_dir: &TempDir) -> (Vec, String) { - let mut workload_path = dirs::home_dir().unwrap(); - workload_path.push("workloads"); - - let mut fw_path = workload_path.clone(); - fw_path.push("hypervisor-fw"); - - let mut osdisk_base_path = workload_path.clone(); - osdisk_base_path.push("clear-29810-cloud.img"); - - let mut osdisk_raw_base_path = workload_path.clone(); - osdisk_raw_base_path.push("clear-29810-cloud-raw.img"); - - let osdisk_path = String::from(tmp_dir.path().join("osdisk.img").to_str().unwrap()); - let osdisk_raw_path = String::from(tmp_dir.path().join("osdisk_raw.img").to_str().unwrap()); - let cloudinit_path = - prepare_cloudinit(&tmp_dir, "192.168.2.2", "192.168.2.1", "12:34:56:78:90:ab"); - - fs::copy(osdisk_base_path, &osdisk_path).expect("copying of OS source disk image failed"); - fs::copy(osdisk_raw_base_path, &osdisk_raw_path) - .expect("copying of OS source disk raw image failed"); - - let disks = vec![osdisk_path, cloudinit_path, osdisk_raw_path]; - - (disks, String::from(fw_path.to_str().unwrap())) + struct Guest { + tmp_dir: TempDir, + disks: Vec, + fw_path: String, + guest_ip: String, + host_ip: String, + guest_mac: String, } fn prepare_virtiofsd(tmp_dir: &TempDir) -> (std::process::Child, String) { @@ -322,66 +202,218 @@ mod tests { (child, virtiofsd_socket_path) } - fn get_cpu_count() -> u32 { - ssh_command("grep -c processor /proc/cpuinfo") - .trim() - .parse() - .unwrap() - } + impl Guest { + fn new() -> Self { + let tmp_dir = TempDir::new("ch").unwrap(); - fn get_initial_apicid() -> u32 { - ssh_command("grep \"initial apicid\" /proc/cpuinfo | grep -o \"[0-9]*\"") - .trim() - .parse() - .unwrap() - } + let mut guest = Guest { + tmp_dir, + disks: Vec::new(), + fw_path: String::new(), + guest_ip: String::from("192.168.2.2"), + host_ip: String::from("192.168.2.1"), + guest_mac: String::from("12:34:56:78:90:ab"), + }; - fn get_total_memory() -> u32 { - ssh_command("grep MemTotal /proc/meminfo | grep -o \"[0-9]*\"") - .trim() - .parse::() - .unwrap() - } + guest.prepare_files(); - fn get_entropy() -> u32 { - ssh_command("cat /proc/sys/kernel/random/entropy_avail") - .trim() - .parse::() - .unwrap() - } + guest + } - fn get_pci_bridge_class() -> String { - ssh_command("cat /sys/bus/pci/devices/0000:00:00.0/class") - .trim() - .to_string() + fn prepare_cloudinit(&self) -> String { + let cloudinit_file_path = + String::from(self.tmp_dir.path().join("cloudinit").to_str().unwrap()); + + let cloud_init_directory = self.tmp_dir.path().join("cloud-init").join("openstack"); + + fs::create_dir_all(&cloud_init_directory.join("latest")) + .expect("Expect creating cloud-init directory to succeed"); + + let source_file_dir = std::env::current_dir() + .unwrap() + .join("test_data") + .join("cloud-init") + .join("openstack") + .join("latest"); + + fs::copy( + source_file_dir.join("meta_data.json"), + cloud_init_directory.join("latest").join("meta_data.json"), + ) + .expect("Expect copying cloud-init meta_data.json to succeed"); + + let mut user_data_string = String::new(); + + fs::File::open(source_file_dir.join("user_data")) + .unwrap() + .read_to_string(&mut user_data_string) + .expect("Expected reading user_data file in to succeed"); + + user_data_string = user_data_string.replace("192.168.2.1", &self.host_ip); + user_data_string = user_data_string.replace("192.168.2.2", &self.guest_ip); + user_data_string = user_data_string.replace("12:34:56:78:90:ab", &self.guest_mac); + + fs::File::create(cloud_init_directory.join("latest").join("user_data")) + .unwrap() + .write_all(&user_data_string.as_bytes()) + .expect("Expected writing out user_data to succeed"); + + std::process::Command::new("mkdosfs") + .args(&["-n", "config-2"]) + .args(&["-C", cloudinit_file_path.as_str()]) + .arg("8192") + .output() + .expect("Expect creating disk image to succeed"); + + std::process::Command::new("mcopy") + .arg("-o") + .args(&["-i", cloudinit_file_path.as_str()]) + .args(&["-s", cloud_init_directory.to_str().unwrap(), "::"]) + .output() + .expect("Expect copying files to disk image to succeed"); + + cloudinit_file_path + } + + fn prepare_files(&mut self) { + let mut workload_path = dirs::home_dir().unwrap(); + workload_path.push("workloads"); + + let mut fw_path = workload_path.clone(); + fw_path.push("hypervisor-fw"); + + let mut osdisk_base_path = workload_path.clone(); + osdisk_base_path.push("clear-29810-cloud.img"); + + let mut osdisk_raw_base_path = workload_path.clone(); + osdisk_raw_base_path.push("clear-29810-cloud-raw.img"); + + let osdisk_path = + String::from(self.tmp_dir.path().join("osdisk.img").to_str().unwrap()); + let osdisk_raw_path = + String::from(self.tmp_dir.path().join("osdisk_raw.img").to_str().unwrap()); + let cloudinit_path = self.prepare_cloudinit(); + + fs::copy(osdisk_base_path, &osdisk_path) + .expect("copying of OS source disk image failed"); + fs::copy(osdisk_raw_base_path, &osdisk_raw_path) + .expect("copying of OS source disk raw image failed"); + + let disks = vec![osdisk_path, cloudinit_path, osdisk_raw_path]; + + self.disks = disks; + self.fw_path = String::from(fw_path.to_str().unwrap()); + } + + fn default_net_string(&self) -> String { + format!( + "tap=,mac={},ip={},mask=255.255.255.0", + self.guest_mac, self.host_ip + ) + } + + fn ssh_command(&self, command: &str) -> String { + let mut s = String::new(); + #[derive(Debug)] + enum Error { + Connection, + Authentication, + Command, + }; + + let mut counter = 0; + loop { + match (|| -> Result<(), Error> { + let tcp = TcpStream::connect(format!("{}:22", self.guest_ip)) + .map_err(|_| Error::Connection)?; + let mut sess = Session::new().unwrap(); + sess.handshake(&tcp).map_err(|_| Error::Connection)?; + + sess.userauth_password("admin", "cloud123") + .map_err(|_| Error::Authentication)?; + assert!(sess.authenticated()); + + let mut channel = sess.channel_session().map_err(|_| Error::Command)?; + channel.exec(command).map_err(|_| Error::Command)?; + + // Intentionally ignore these results here as their failure + // does not precipitate a repeat + let _ = channel.read_to_string(&mut s); + let _ = channel.close(); + let _ = channel.wait_close(); + Ok(()) + })() { + Ok(_) => break, + Err(e) => { + counter += 1; + if counter >= 6 { + panic!("Took too many attempts to run command. Last error: {:?}", e); + } + } + }; + thread::sleep(std::time::Duration::new(10 * counter, 0)); + } + s + } + + fn get_cpu_count(&self) -> u32 { + self.ssh_command("grep -c processor /proc/cpuinfo") + .trim() + .parse() + .unwrap() + } + + fn get_initial_apicid(&self) -> u32 { + self.ssh_command("grep \"initial apicid\" /proc/cpuinfo | grep -o \"[0-9]*\"") + .trim() + .parse() + .unwrap() + } + + fn get_total_memory(&self) -> u32 { + self.ssh_command("grep MemTotal /proc/meminfo | grep -o \"[0-9]*\"") + .trim() + .parse::() + .unwrap() + } + + fn get_entropy(&self) -> u32 { + self.ssh_command("cat /proc/sys/kernel/random/entropy_avail") + .trim() + .parse::() + .unwrap() + } + + fn get_pci_bridge_class(&self) -> String { + self.ssh_command("cat /sys/bus/pci/devices/0000:00:00.0/class") + .trim() + .to_string() + } } #[test] fn test_simple_launch() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, fw_path) = prepare_files(&tmp_dir); + let guest = Guest::new(); + let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) - .args(&["--kernel", fw_path.as_str()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&[ - "--net", - "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", - ]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .spawn() .unwrap(); thread::sleep(std::time::Duration::new(20, 0)); - aver_eq!(tb, get_cpu_count(), 1); - aver_eq!(tb, get_initial_apicid(), 0); - aver!(tb, get_total_memory() > 496_000); - aver!(tb, get_entropy() >= 1000); - aver_eq!(tb, get_pci_bridge_class(), "0x060000"); + aver_eq!(tb, guest.get_cpu_count(), 1); + aver_eq!(tb, guest.get_initial_apicid(), 0); + aver!(tb, guest.get_total_memory() > 496_000); + aver!(tb, guest.get_entropy() >= 1000); + aver_eq!(tb, guest.get_pci_bridge_class(), "0x060000"); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -392,25 +424,21 @@ mod tests { #[test] fn test_multi_cpu() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, fw_path) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "2"]) .args(&["--memory", "size=512M"]) - .args(&["--kernel", fw_path.as_str()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&[ - "--net", - "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", - ]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .spawn() .unwrap(); thread::sleep(std::time::Duration::new(20, 0)); - aver_eq!(tb, get_cpu_count(), 2); + aver_eq!(tb, guest.get_cpu_count(), 2); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -421,25 +449,21 @@ mod tests { #[test] fn test_large_memory() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, fw_path) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) .args(&["--memory", "size=5120M"]) - .args(&["--kernel", fw_path.as_str()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&[ - "--net", - "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", - ]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .spawn() .unwrap(); thread::sleep(std::time::Duration::new(20, 0)); - aver!(tb, get_total_memory() > 5_063_000); + aver!(tb, guest.get_total_memory() > 5_063_000); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -450,17 +474,13 @@ mod tests { #[test] fn test_pci_msi() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, fw_path) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) - .args(&["--kernel", fw_path.as_str()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&[ - "--net", - "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", - ]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .spawn() .unwrap(); @@ -468,14 +488,15 @@ mod tests { aver_eq!( tb, - ssh_command("grep -c PCI-MSI /proc/interrupts") + guest + .ssh_command("grep -c PCI-MSI /proc/interrupts") .trim() .parse::() .unwrap(), 8 ); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -486,8 +507,7 @@ mod tests { #[test] fn test_vmlinux_boot() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, _) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut workload_path = dirs::home_dir().unwrap(); workload_path.push("workloads"); @@ -498,27 +518,28 @@ mod tests { .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .args(&["--cmdline", "root=PARTUUID=3cb0e0a5-925d-405e-bc55-edf0cec8f10a console=tty0 console=ttyS0,115200n8 console=hvc0 quiet init=/usr/lib/systemd/systemd-bootchart initcall_debug tsc=reliable no_timer_check noreplace-smp cryptomgr.notests rootfstype=ext4,btrfs,xfs kvm-intel.nested=1 rw"]) .spawn() .unwrap(); thread::sleep(std::time::Duration::new(20, 0)); - aver_eq!(tb, get_cpu_count(), 1); - aver!(tb, get_total_memory() > 496_000); - aver!(tb, get_entropy() >= 1000); + aver_eq!(tb, guest.get_cpu_count(), 1); + aver!(tb, guest.get_total_memory() > 496_000); + aver!(tb, guest.get_entropy() >= 1000); aver_eq!( tb, - ssh_command("grep -c PCI-MSI /proc/interrupts") + guest + .ssh_command("grep -c PCI-MSI /proc/interrupts") .trim() .parse::() .unwrap(), 8 ); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -529,8 +550,7 @@ mod tests { #[test] fn test_bzimage_boot() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, _) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut workload_path = dirs::home_dir().unwrap(); workload_path.push("workloads"); @@ -541,27 +561,28 @@ mod tests { .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .args(&["--cmdline", "root=PARTUUID=3cb0e0a5-925d-405e-bc55-edf0cec8f10a console=tty0 console=ttyS0,115200n8 console=hvc0 quiet init=/usr/lib/systemd/systemd-bootchart initcall_debug tsc=reliable no_timer_check noreplace-smp cryptomgr.notests rootfstype=ext4,btrfs,xfs kvm-intel.nested=1 rw"]) .spawn() .unwrap(); thread::sleep(std::time::Duration::new(20, 0)); - aver_eq!(tb, get_cpu_count(), 1); - aver!(tb, get_total_memory() > 496_000); - aver!(tb, get_entropy() >= 1000); + aver_eq!(tb, guest.get_cpu_count(), 1); + aver!(tb, guest.get_total_memory() > 496_000); + aver!(tb, guest.get_entropy() >= 1000); aver_eq!( tb, - ssh_command("grep -c PCI-MSI /proc/interrupts") + guest + .ssh_command("grep -c PCI-MSI /proc/interrupts") .trim() .parse::() .unwrap(), 8 ); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -572,17 +593,13 @@ mod tests { #[test] fn test_split_irqchip() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, fw_path) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) - .args(&["--kernel", fw_path.as_str()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&[ - "--net", - "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", - ]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .spawn() .unwrap(); @@ -590,7 +607,8 @@ mod tests { aver_eq!( tb, - ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'timer'") + guest + .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'timer'") .trim() .parse::() .unwrap(), @@ -598,14 +616,15 @@ mod tests { ); aver_eq!( tb, - ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'cascade'") + guest + .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'cascade'") .trim() .parse::() .unwrap(), 0 ); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -616,9 +635,8 @@ mod tests { #[test] fn test_virtio_fs() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, _) = prepare_files(&tmp_dir); - let (mut daemon_child, virtiofsd_socket_path) = prepare_virtiofsd(&tmp_dir); + let guest = Guest::new(); + let (mut daemon_child, virtiofsd_socket_path) = prepare_virtiofsd(&guest.tmp_dir); let mut workload_path = dirs::home_dir().unwrap(); workload_path.push("workloads"); @@ -629,8 +647,8 @@ mod tests { .args(&["--cpus", "1"]) .args(&["--memory", "size=512M,file=/dev/shm"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .args(&[ "--fs", format!( @@ -648,22 +666,22 @@ mod tests { // Mount shared directory through virtio_fs filesystem aver_eq!( tb, - ssh_command("mkdir -p mount_dir && sudo mount -t virtio_fs /dev/null mount_dir/ -o tag=virtiofs,rootmode=040000,user_id=1001,group_id=1001 && echo ok") + guest.ssh_command("mkdir -p mount_dir && sudo mount -t virtio_fs /dev/null mount_dir/ -o tag=virtiofs,rootmode=040000,user_id=1001,group_id=1001 && echo ok") .trim(), "ok" ); // Check file1 exists and its content is "foo" - aver_eq!(tb, ssh_command("cat mount_dir/file1").trim(), "foo"); + aver_eq!(tb, guest.ssh_command("cat mount_dir/file1").trim(), "foo"); // Check file2 does not exist aver_ne!( tb, - ssh_command("ls mount_dir/file2").trim(), + guest.ssh_command("ls mount_dir/file2").trim(), "mount_dir/file2" ); // Check file3 exists and its content is "bar" - aver_eq!(tb, ssh_command("cat mount_dir/file3").trim(), "bar"); + aver_eq!(tb, guest.ssh_command("cat mount_dir/file3").trim(), "bar"); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); let _ = child.wait(); let _ = daemon_child.wait(); Ok(()) @@ -673,15 +691,14 @@ mod tests { #[test] fn test_virtio_pmem() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, _) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut workload_path = dirs::home_dir().unwrap(); workload_path.push("workloads"); let mut kernel_path = workload_path.clone(); kernel_path.push("vmlinux-custom"); - let pmem_backend_path = tmp_dir.path().join("/tmp/pmem-file"); + let pmem_backend_path = guest.tmp_dir.path().join("/tmp/pmem-file"); let mut pmem_backend_file = OpenOptions::new() .read(true) .write(true) @@ -700,8 +717,8 @@ mod tests { .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .args(&[ "--pmem", format!( @@ -718,16 +735,16 @@ mod tests { thread::sleep(std::time::Duration::new(20, 0)); // Check for the presence of /dev/pmem0 - aver_eq!(tb, ssh_command("ls /dev/pmem0").trim(), "/dev/pmem0"); + aver_eq!(tb, guest.ssh_command("ls /dev/pmem0").trim(), "/dev/pmem0"); // Check content aver_eq!( tb, - &ssh_command("sudo cat /dev/pmem0").trim()[..pmem_backend_content.len()], + &guest.ssh_command("sudo cat /dev/pmem0").trim()[..pmem_backend_content.len()], pmem_backend_content ); // Modify content let new_content = "bar"; - ssh_command( + guest.ssh_command( format!( "sudo bash -c 'echo {} > /dev/pmem0' && sudo sync /dev/pmem0", new_content @@ -744,7 +761,7 @@ mod tests { new_content ); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); let _ = child.wait(); Ok(()) @@ -754,8 +771,7 @@ mod tests { #[test] fn test_boot_from_virtio_pmem() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, _) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut workload_path = dirs::home_dir().unwrap(); workload_path.push("workloads"); @@ -766,14 +782,14 @@ mod tests { .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) - .args(&["--disk", disks[1].as_str()]) - .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--disk", guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .args(&[ "--pmem", format!( "file={},size={}", - disks[2], - fs::metadata(&disks[2]).unwrap().len() + guest.disks[2], + fs::metadata(&guest.disks[2]).unwrap().len() ) .as_str(), ]) @@ -784,10 +800,10 @@ mod tests { thread::sleep(std::time::Duration::new(20, 0)); // Simple checks to validate the VM booted properly - aver_eq!(tb, get_cpu_count(), 1); - aver!(tb, get_total_memory() > 496_000); + aver_eq!(tb, guest.get_cpu_count(), 1); + aver!(tb, guest.get_total_memory() > 496_000); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); let _ = child.wait(); Ok(()) @@ -797,16 +813,15 @@ mod tests { #[test] fn test_multiple_network_interfaces() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, fw_path) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) - .args(&["--kernel", fw_path.as_str()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) .args(&[ "--net", - "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", + guest.default_net_string().as_str(), "tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0", "tap=,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0", ]) @@ -818,14 +833,15 @@ mod tests { // 3 network interfaces + default localhost ==> 4 interfaces aver_eq!( tb, - ssh_command("ip -o link | wc -l") + guest + .ssh_command("ip -o link | wc -l") .trim() .parse::() .unwrap(), 4 ); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -836,17 +852,13 @@ mod tests { #[test] fn test_serial_disable() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, fw_path) = prepare_files(&tmp_dir); + let guest = Guest::new(); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) - .args(&["--kernel", fw_path.as_str()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&[ - "--net", - "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", - ]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .args(&["--serial", "off"]) .spawn() .unwrap(); @@ -856,7 +868,8 @@ mod tests { // Test that there is no ttyS0 aver_eq!( tb, - ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") + guest + .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") .trim() .parse::() .unwrap(), @@ -866,14 +879,15 @@ mod tests { // Further test that we're MSI only now aver_eq!( tb, - ssh_command("cat /proc/interrupts | grep -c 'IO-APIC'") + guest + .ssh_command("cat /proc/interrupts | grep -c 'IO-APIC'") .trim() .parse::() .unwrap(), 0 ); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); let _ = child.kill(); let _ = child.wait(); @@ -884,19 +898,15 @@ mod tests { #[test] fn test_serial_file() { test_block!(tb, "", { - let tmp_dir = TempDir::new("ch").unwrap(); - let (disks, fw_path) = prepare_files(&tmp_dir); + let guest = Guest::new(); - let serial_path = tmp_dir.path().join("/tmp/serial-output"); + let serial_path = guest.tmp_dir.path().join("/tmp/serial-output"); let mut child = Command::new("target/debug/cloud-hypervisor") .args(&["--cpus", "1"]) .args(&["--memory", "size=512M"]) - .args(&["--kernel", fw_path.as_str()]) - .args(&["--disk", disks[0].as_str(), disks[1].as_str()]) - .args(&[ - "--net", - "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", - ]) + .args(&["--kernel", guest.fw_path.as_str()]) + .args(&["--disk", guest.disks[0].as_str(), guest.disks[1].as_str()]) + .args(&["--net", guest.default_net_string().as_str()]) .args(&[ "--serial", format!("file={}", serial_path.to_str().unwrap()).as_str(), @@ -909,14 +919,15 @@ mod tests { // Test that there is a ttyS0 aver_eq!( tb, - ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") + guest + .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") .trim() .parse::() .unwrap(), 1 ); - ssh_command("sudo reboot"); + guest.ssh_command("sudo reboot"); thread::sleep(std::time::Duration::new(10, 0)); // Do this check after shutdown of the VM as an easy way to ensure