tests: Remove potential sources of nested panics

panic()ing after a panic() has already been recovered by the credibility
test system (i.e. after an aver! has failed) results in an abort which
triggers SIGILL.

Adjust the SSH based commands to generate a Result<...,Error> which we
then either propagate through the test block. Or if the function is
directly being evaluated in an aver! macro call .unwrap_with_default()
(or .unwrap_or() in the case where the default would be wrong.)

See #182

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2019-08-15 17:08:24 +01:00 committed by Sebastien Boeuf
parent ab6a8f19f0
commit 08ed88c8d1

View File

@ -548,6 +548,22 @@ mod tests {
(child, virtiofsd_socket_path) (child, virtiofsd_socket_path)
} }
#[derive(Debug)]
enum Error {
Connection,
Authentication,
Command,
Parsing,
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl<'a> Guest<'a> { impl<'a> Guest<'a> {
fn new_from_ip_range(disk_config: &'a mut dyn DiskConfig, class: &str, id: u8) -> Self { fn new_from_ip_range(disk_config: &'a mut dyn DiskConfig, class: &str, id: u8) -> Self {
let tmp_dir = TempDir::new("ch").unwrap(); let tmp_dir = TempDir::new("ch").unwrap();
@ -591,14 +607,8 @@ mod tests {
) )
} }
fn ssh_command_ip(&self, command: &str, ip: &str) -> String { fn ssh_command_ip(&self, command: &str, ip: &str) -> Result<String, Error> {
let mut s = String::new(); let mut s = String::new();
#[derive(Debug)]
enum Error {
Connection,
Authentication,
Command,
};
let mut counter = 0; let mut counter = 0;
loop { loop {
@ -626,114 +636,130 @@ mod tests {
Err(e) => { Err(e) => {
counter += 1; counter += 1;
if counter >= 6 { if counter >= 6 {
panic!("Took too many attempts to run command. Last error: {:?}", e); return Err(e);
} }
} }
}; };
thread::sleep(std::time::Duration::new(10 * counter, 0)); thread::sleep(std::time::Duration::new(10 * counter, 0));
} }
s Ok(s)
} }
fn ssh_command(&self, command: &str) -> String { fn ssh_command(&self, command: &str) -> Result<String, Error> {
self.ssh_command_ip(command, &self.network.guest_ip) self.ssh_command_ip(command, &self.network.guest_ip)
} }
fn ssh_command_l1(&self, command: &str) -> String { fn ssh_command_l1(&self, command: &str) -> Result<String, Error> {
self.ssh_command_ip(command, &self.network.guest_ip) self.ssh_command_ip(command, &self.network.guest_ip)
} }
fn ssh_command_l2(&self, command: &str) -> String { fn ssh_command_l2(&self, command: &str) -> Result<String, Error> {
self.ssh_command_ip(command, &self.network.l2_guest_ip) self.ssh_command_ip(command, &self.network.l2_guest_ip)
} }
fn get_cpu_count(&self) -> u32 { fn get_cpu_count(&self) -> Result<u32, Error> {
self.ssh_command("grep -c processor /proc/cpuinfo") Ok(self
.ssh_command("grep -c processor /proc/cpuinfo")?
.trim() .trim()
.parse() .parse()
.unwrap() .map_err(|_| Error::Parsing)?)
} }
fn get_initial_apicid(&self) -> u32 { fn get_initial_apicid(&self) -> Result<u32, Error> {
self.ssh_command("grep \"initial apicid\" /proc/cpuinfo | grep -o \"[0-9]*\"") Ok(self
.ssh_command("grep \"initial apicid\" /proc/cpuinfo | grep -o \"[0-9]*\"")?
.trim() .trim()
.parse() .parse()
.unwrap() .map_err(|_| Error::Parsing)?)
} }
fn get_total_memory(&self) -> u32 { fn get_total_memory(&self) -> Result<u32, Error> {
self.ssh_command("grep MemTotal /proc/meminfo | grep -o \"[0-9]*\"") Ok(self
.ssh_command("grep MemTotal /proc/meminfo | grep -o \"[0-9]*\"")?
.trim() .trim()
.parse::<u32>() .parse()
.unwrap() .map_err(|_| Error::Parsing)?)
} }
fn get_entropy(&self) -> u32 { fn get_entropy(&self) -> Result<u32, Error> {
self.ssh_command("cat /proc/sys/kernel/random/entropy_avail") Ok(self
.ssh_command("cat /proc/sys/kernel/random/entropy_avail")?
.trim() .trim()
.parse::<u32>() .parse()
.unwrap() .map_err(|_| Error::Parsing)?)
} }
fn get_pci_bridge_class(&self) -> String { fn get_pci_bridge_class(&self) -> Result<String, Error> {
self.ssh_command("cat /sys/bus/pci/devices/0000:00:00.0/class") Ok(self
.trim() .ssh_command("cat /sys/bus/pci/devices/0000:00:00.0/class")?
.to_string() .trim()
} .to_string())
fn get_pci_device_ids(&self) -> String { }
self.ssh_command("cat /sys/bus/pci/devices/*/device")
fn get_pci_device_ids(&self) -> Result<String, Error> {
Ok(self
.ssh_command("cat /sys/bus/pci/devices/*/device")?
.trim()
.to_string())
}
fn get_pci_vendor_ids(&self) -> Result<String, Error> {
Ok(self
.ssh_command("cat /sys/bus/pci/devices/*/vendor")?
.trim()
.to_string())
}
fn is_console_detected(&self) -> Result<bool, Error> {
Ok(!(self
.ssh_command("dmesg | grep \"hvc0] enabled\"")?
.trim() .trim()
.to_string() .to_string()
.is_empty()))
} }
fn get_pci_vendor_ids(&self) -> String { fn does_device_vendor_pair_match(
self.ssh_command("cat /sys/bus/pci/devices/*/vendor") &self,
.trim() device_id: &str,
.to_string() vendor_id: &str,
} ) -> Result<bool, Error> {
fn is_console_detected(&self) -> bool {
!(self
.ssh_command("dmesg | grep \"hvc0] enabled\"")
.trim()
.to_string()
.is_empty())
}
fn does_device_vendor_pair_match(&self, device_id: &str, vendor_id: &str) -> bool {
// We are checking if console device's device id and vendor id pair matches // We are checking if console device's device id and vendor id pair matches
let devices = self.get_pci_device_ids(); let devices = self.get_pci_device_ids()?;
let devices: Vec<&str> = devices.split('\n').collect(); let devices: Vec<&str> = devices.split('\n').collect();
let vendors = self.get_pci_vendor_ids(); let vendors = self.get_pci_vendor_ids()?;
let vendors: Vec<&str> = vendors.split('\n').collect(); let vendors: Vec<&str> = vendors.split('\n').collect();
for (index, d_id) in devices.iter().enumerate() { for (index, d_id) in devices.iter().enumerate() {
if *d_id == device_id { if *d_id == device_id {
if let Some(v_id) = vendors.get(index) { if let Some(v_id) = vendors.get(index) {
if *v_id == vendor_id { if *v_id == vendor_id {
return true; return Ok(true);
} }
} }
} }
} }
false Ok(false)
} }
fn valid_virtio_fs_cache_size(&self, dax: bool, cache_size: Option<u64>) -> bool { fn valid_virtio_fs_cache_size(
&self,
dax: bool,
cache_size: Option<u64>,
) -> Result<bool, Error> {
let shm_region = self let shm_region = self
.ssh_command("sudo -E bash -c 'cat /proc/iomem' | grep virtio-pci-shm") .ssh_command("sudo -E bash -c 'cat /proc/iomem' | grep virtio-pci-shm")?
.trim() .trim()
.to_string(); .to_string();
if shm_region.is_empty() { if shm_region.is_empty() {
return !dax; return Ok(!dax);
} }
// From this point, the region is not empty, hence it is an error // From this point, the region is not empty, hence it is an error
// if DAX is off. // if DAX is off.
if !dax { if !dax {
return false; return Ok(false);
} }
let cache = if let Some(cache) = cache_size { let cache = if let Some(cache) = cache_size {
@ -745,18 +771,18 @@ mod tests {
let args: Vec<&str> = shm_region.split(':').collect(); let args: Vec<&str> = shm_region.split(':').collect();
if args.is_empty() { if args.is_empty() {
return false; return Ok(false);
} }
let args: Vec<&str> = args[0].trim().split('-').collect(); let args: Vec<&str> = args[0].trim().split('-').collect();
if args.len() != 2 { if args.len() != 2 {
return false; return Ok(false);
} }
let start_addr = u64::from_str_radix(args[0], 16).unwrap(); let start_addr = u64::from_str_radix(args[0], 16).map_err(|_| Error::Parsing)?;
let end_addr = u64::from_str_radix(args[1], 16).unwrap(); let end_addr = u64::from_str_radix(args[1], 16).map_err(|_| Error::Parsing)?;
cache == (end_addr - start_addr + 1) Ok(cache == (end_addr - start_addr + 1))
} }
} }
@ -798,13 +824,17 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
aver_eq!(tb, guest.get_cpu_count(), 1); aver_eq!(tb, guest.get_cpu_count().unwrap_or_default(), 1);
aver_eq!(tb, guest.get_initial_apicid(), 0); aver_eq!(tb, guest.get_initial_apicid().unwrap_or(1), 0);
aver!(tb, guest.get_total_memory() > 490_000); aver!(tb, guest.get_total_memory().unwrap_or_default() > 490_000);
aver!(tb, guest.get_entropy() >= 900); aver!(tb, guest.get_entropy().unwrap_or_default() >= 900);
aver_eq!(tb, guest.get_pci_bridge_class(), "0x060000"); aver_eq!(
tb,
guest.get_pci_bridge_class().unwrap_or_default(),
"0x060000"
);
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot").unwrap_or_default();
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -841,9 +871,9 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
aver_eq!(tb, guest.get_cpu_count(), 2); aver_eq!(tb, guest.get_cpu_count().unwrap_or_default(), 2);
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -879,9 +909,9 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
aver!(tb, guest.get_total_memory() > 5_063_000); aver!(tb, guest.get_total_memory().unwrap_or_default() > 5_063_000);
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -921,13 +951,14 @@ mod tests {
tb, tb,
guest guest
.ssh_command("grep -c PCI-MSI /proc/interrupts") .ssh_command("grep -c PCI-MSI /proc/interrupts")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or_default(),
10 10
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -970,20 +1001,21 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
aver_eq!(tb, guest.get_cpu_count(), 1); aver_eq!(tb, guest.get_cpu_count().unwrap_or_default(), 1);
aver!(tb, guest.get_total_memory() > 496_000); aver!(tb, guest.get_total_memory().unwrap_or_default() > 496_000);
aver!(tb, guest.get_entropy() >= 900); aver!(tb, guest.get_entropy().unwrap_or_default() >= 900);
aver_eq!( aver_eq!(
tb, tb,
guest guest
.ssh_command("grep -c PCI-MSI /proc/interrupts") .ssh_command("grep -c PCI-MSI /proc/interrupts")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or_default(),
10 10
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -1026,20 +1058,21 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
aver_eq!(tb, guest.get_cpu_count(), 1); aver_eq!(tb, guest.get_cpu_count().unwrap_or_default(), 1);
aver!(tb, guest.get_total_memory() > 496_000); aver!(tb, guest.get_total_memory().unwrap_or_default() > 496_000);
aver!(tb, guest.get_entropy() >= 900); aver!(tb, guest.get_entropy().unwrap_or_default() >= 900);
aver_eq!( aver_eq!(
tb, tb,
guest guest
.ssh_command("grep -c PCI-MSI /proc/interrupts") .ssh_command("grep -c PCI-MSI /proc/interrupts")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or_default(),
10 10
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -1080,22 +1113,24 @@ mod tests {
tb, tb,
guest guest
.ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'timer'") .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'timer'")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or(1),
0 0
); );
aver_eq!( aver_eq!(
tb, tb,
guest guest
.ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'cascade'") .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'cascade'")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or(1),
0 0
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -1169,21 +1204,48 @@ mod tests {
echo ok", echo ok",
dax_mount_param dax_mount_param
); );
aver_eq!(tb, guest.ssh_command(&mount_cmd).trim(), "ok"); aver_eq!(
tb,
guest.ssh_command(&mount_cmd).unwrap_or_default().trim(),
"ok"
);
// Check the cache size is the expected one // Check the cache size is the expected one
aver_eq!(tb, guest.valid_virtio_fs_cache_size(dax, cache_size), true); aver_eq!(
tb,
guest
.valid_virtio_fs_cache_size(dax, cache_size)
.unwrap_or_default(),
true
);
// Check file1 exists and its content is "foo" // Check file1 exists and its content is "foo"
aver_eq!(tb, guest.ssh_command("cat mount_dir/file1").trim(), "foo"); aver_eq!(
tb,
guest
.ssh_command("cat mount_dir/file1")
.unwrap_or_default()
.trim(),
"foo"
);
// Check file2 does not exist // Check file2 does not exist
aver_ne!( aver_ne!(
tb, tb,
guest.ssh_command("ls mount_dir/file2").trim(), guest
.ssh_command("ls mount_dir/file2")
.unwrap_or_default()
.trim(),
"mount_dir/file2" "mount_dir/file2"
); );
// Check file3 exists and its content is "bar" // Check file3 exists and its content is "bar"
aver_eq!(tb, guest.ssh_command("cat mount_dir/file3").trim(), "bar"); aver_eq!(
tb,
guest
.ssh_command("cat mount_dir/file3")
.unwrap_or_default()
.trim(),
"bar"
);
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
let _ = child.wait(); let _ = child.wait();
let _ = daemon_child.wait(); let _ = daemon_child.wait();
Ok(()) Ok(())
@ -1251,9 +1313,16 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
// Check for the presence of /dev/pmem0 // Check for the presence of /dev/pmem0
aver_eq!(tb, guest.ssh_command("ls /dev/pmem0").trim(), "/dev/pmem0"); aver_eq!(
tb,
guest
.ssh_command("ls /dev/pmem0")
.unwrap_or_default()
.trim(),
"/dev/pmem0"
);
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
let _ = child.wait(); let _ = child.wait();
Ok(()) Ok(())
@ -1293,10 +1362,10 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
// Simple checks to validate the VM booted properly // Simple checks to validate the VM booted properly
aver_eq!(tb, guest.get_cpu_count(), 1); aver_eq!(tb, guest.get_cpu_count().unwrap_or_default(), 1);
aver!(tb, guest.get_total_memory() > 496_000); aver!(tb, guest.get_total_memory().unwrap_or_default() > 496_000);
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
let _ = child.wait(); let _ = child.wait();
Ok(()) Ok(())
@ -1341,13 +1410,14 @@ mod tests {
tb, tb,
guest guest
.ssh_command("ip -o link | wc -l") .ssh_command("ip -o link | wc -l")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or_default(),
4 4
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -1389,9 +1459,10 @@ mod tests {
tb, tb,
guest guest
.ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or(1),
0 0
); );
@ -1400,13 +1471,14 @@ mod tests {
tb, tb,
guest guest
.ssh_command("cat /proc/interrupts | grep -c 'IO-APIC'") .ssh_command("cat /proc/interrupts | grep -c 'IO-APIC'")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or(1),
0 0
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
@ -1451,13 +1523,14 @@ mod tests {
tb, tb,
guest guest
.ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or_default(),
1 1
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
match child.wait_with_output() { match child.wait_with_output() {
@ -1510,13 +1583,14 @@ mod tests {
tb, tb,
guest guest
.ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or_default(),
1 1
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
match child.wait_with_output() { match child.wait_with_output() {
@ -1571,20 +1645,21 @@ mod tests {
tb, tb,
guest guest
.ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'") .ssh_command("cat /proc/interrupts | grep 'IO-APIC' | grep -c 'ttyS0'")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or_default(),
1 1
); );
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
// Do this check after shutdown of the VM as an easy way to ensure // Do this check after shutdown of the VM as an easy way to ensure
// all writes are flushed to disk // all writes are flushed to disk
let mut f = std::fs::File::open(serial_path).unwrap(); let mut f = std::fs::File::open(serial_path)?;
let mut buf = String::new(); let mut buf = String::new();
f.read_to_string(&mut buf).unwrap(); f.read_to_string(&mut buf)?;
aver!(tb, buf.contains("cloud login:")); aver!(tb, buf.contains("cloud login:"));
let _ = child.kill(); let _ = child.kill();
@ -1626,14 +1701,19 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
aver!(tb, guest.does_device_vendor_pair_match("0x1043", "0x1af4")); aver!(
aver!(tb, guest.is_console_detected()); tb,
guest
.does_device_vendor_pair_match("0x1043", "0x1af4")
.unwrap_or_default()
);
aver!(tb, guest.is_console_detected().unwrap_or_default());
let text = String::from("On a branch floating down river a cricket, singing."); let text = String::from("On a branch floating down river a cricket, singing.");
let cmd = format!("sudo -E bash -c 'echo {} > /dev/hvc0'", text); let cmd = format!("sudo -E bash -c 'echo {} > /dev/hvc0'", text);
guest.ssh_command(&cmd); guest.ssh_command(&cmd)?;
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = child.kill(); let _ = child.kill();
@ -1683,16 +1763,16 @@ mod tests {
thread::sleep(std::time::Duration::new(20, 0)); thread::sleep(std::time::Duration::new(20, 0));
// Test that there is a ttyS0 // Test that there is a ttyS0
aver!(tb, guest.is_console_detected()); aver!(tb, guest.is_console_detected().unwrap_or_default());
guest.ssh_command("sudo reboot"); guest.ssh_command("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
// Do this check after shutdown of the VM as an easy way to ensure // Do this check after shutdown of the VM as an easy way to ensure
// all writes are flushed to disk // all writes are flushed to disk
let mut f = std::fs::File::open(console_path).unwrap(); let mut f = std::fs::File::open(console_path)?;
let mut buf = String::new(); let mut buf = String::new();
f.read_to_string(&mut buf).unwrap(); f.read_to_string(&mut buf)?;
aver!(tb, buf.contains("cloud login:")); aver!(tb, buf.contains("cloud login:"));
let _ = child.kill(); let _ = child.kill();
@ -1809,7 +1889,7 @@ mod tests {
thread::sleep(std::time::Duration::new(30, 0)); thread::sleep(std::time::Duration::new(30, 0));
guest.ssh_command_l1("sudo systemctl start vfio"); guest.ssh_command_l1("sudo systemctl start vfio")?;
thread::sleep(std::time::Duration::new(30, 0)); thread::sleep(std::time::Duration::new(30, 0));
// We booted our cloud hypervisor L2 guest with a "VFIOTAG" tag // We booted our cloud hypervisor L2 guest with a "VFIOTAG" tag
@ -1821,16 +1901,17 @@ mod tests {
tb, tb,
guest guest
.ssh_command_l2("cat /proc/cmdline | grep -c 'VFIOTAG'") .ssh_command_l2("cat /proc/cmdline | grep -c 'VFIOTAG'")
.unwrap_or_default()
.trim() .trim()
.parse::<u32>() .parse::<u32>()
.unwrap(), .unwrap_or_default(),
1 1
); );
guest.ssh_command_l2("sudo reboot"); guest.ssh_command_l2("sudo reboot")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
guest.ssh_command_l1("sudo shutdown -h now"); guest.ssh_command_l1("sudo shutdown -h now")?;
thread::sleep(std::time::Duration::new(10, 0)); thread::sleep(std::time::Duration::new(10, 0));
let _ = qemu_child.kill(); let _ = qemu_child.kill();