mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-10-03 20:15:45 +00:00
tests: Extend existing tests with checks on event monitor
In this way, we can cover a broad range of events from the event monitor while avoiding code duplication. Fixes: #4054 Signed-off-by: Bo Chen <chen.bo@intel.com>
This commit is contained in:
parent
3bcd3e7f4c
commit
2eb984b45d
@ -151,6 +151,10 @@ fn temp_api_path(tmp_dir: &TempDir) -> String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn temp_event_monitor_path(tmp_dir: &TempDir) -> String {
|
||||||
|
String::from(tmp_dir.as_path().join("event.json").to_str().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
// Creates the directory and returns the path.
|
// Creates the directory and returns the path.
|
||||||
fn temp_snapshot_dir_path(tmp_dir: &TempDir) -> String {
|
fn temp_snapshot_dir_path(tmp_dir: &TempDir) -> String {
|
||||||
let snapshot_dir = String::from(tmp_dir.as_path().join("snapshot").to_str().unwrap());
|
let snapshot_dir = String::from(tmp_dir.as_path().join("snapshot").to_str().unwrap());
|
||||||
@ -460,6 +464,103 @@ fn fw_path(_fw_type: FwType) -> String {
|
|||||||
fw_path.to_str().unwrap().to_string()
|
fw_path.to_str().unwrap().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MetaEvent {
|
||||||
|
event: String,
|
||||||
|
device_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MetaEvent {
|
||||||
|
pub fn match_with_json_event(&self, v: &serde_json::Value) -> bool {
|
||||||
|
let mut matched = false;
|
||||||
|
if v["event"].as_str().unwrap() == self.event {
|
||||||
|
if let Some(device_id) = &self.device_id {
|
||||||
|
if v["properties"]["id"].as_str().unwrap() == device_id {
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matched
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the event_monitor file based on the format that each event
|
||||||
|
// is surrounded by '{' and '}'
|
||||||
|
fn parse_event_file(event_file: &str) -> Vec<serde_json::Value> {
|
||||||
|
let content = fs::read(event_file).unwrap();
|
||||||
|
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
let mut entry_start = 0;
|
||||||
|
let mut count = 0;
|
||||||
|
for (idx, &c) in content.iter().enumerate() {
|
||||||
|
if c as char == '{' {
|
||||||
|
count += 1;
|
||||||
|
} else if c as char == '}' {
|
||||||
|
assert!(count > 0);
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
let entry = String::from_utf8_lossy(&content[entry_start..idx + 1]);
|
||||||
|
ret.push(serde_json::from_str(&entry).unwrap());
|
||||||
|
entry_start = idx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if all events from the input 'expected_events' are matched sequentially
|
||||||
|
// with events from the 'event_file'
|
||||||
|
fn check_sequential_events(expected_events: &[&MetaEvent], event_file: &str) -> bool {
|
||||||
|
let json_events = parse_event_file(event_file);
|
||||||
|
let len = expected_events.len();
|
||||||
|
let mut idx = 0;
|
||||||
|
for e in &json_events {
|
||||||
|
if idx == len {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if expected_events[idx].match_with_json_event(e) {
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idx == len
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if all events from the input 'expected_events' are matched exactly
|
||||||
|
// with events from the 'event_file'
|
||||||
|
fn check_sequential_events_exact(expected_events: &[&MetaEvent], event_file: &str) -> bool {
|
||||||
|
let json_events = parse_event_file(event_file);
|
||||||
|
assert!(expected_events.len() <= json_events.len());
|
||||||
|
let json_events = &json_events[..expected_events.len()];
|
||||||
|
|
||||||
|
for (idx, e) in json_events.iter().enumerate() {
|
||||||
|
if !expected_events[idx].match_with_json_event(e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if events from the input 'expected_events' are matched exactly
|
||||||
|
// with the most recent events from the 'event_file'
|
||||||
|
fn check_latest_events_exact(latest_events: &[&MetaEvent], event_file: &str) -> bool {
|
||||||
|
let json_events = parse_event_file(event_file);
|
||||||
|
assert!(latest_events.len() <= json_events.len());
|
||||||
|
let json_events = &json_events[(json_events.len() - latest_events.len())..];
|
||||||
|
|
||||||
|
for (idx, e) in json_events.iter().enumerate() {
|
||||||
|
if !latest_events[idx].match_with_json_event(e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn test_cpu_topology(threads_per_core: u8, cores_per_package: u8, packages: u8, use_fw: bool) {
|
fn test_cpu_topology(threads_per_core: u8, cores_per_package: u8, packages: u8, use_fw: bool) {
|
||||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
let guest = Guest::new(Box::new(focal));
|
let guest = Guest::new(Box::new(focal));
|
||||||
@ -1900,6 +2001,7 @@ mod parallel {
|
|||||||
fn test_simple_launch(fw_path: String, disk_path: &str) {
|
fn test_simple_launch(fw_path: String, disk_path: &str) {
|
||||||
let disk_config = Box::new(UbuntuDiskConfig::new(disk_path.to_string()));
|
let disk_config = Box::new(UbuntuDiskConfig::new(disk_path.to_string()));
|
||||||
let guest = Guest::new(disk_config);
|
let guest = Guest::new(disk_config);
|
||||||
|
let event_path = temp_event_monitor_path(&guest.tmp_dir);
|
||||||
|
|
||||||
let mut child = GuestCommand::new(&guest)
|
let mut child = GuestCommand::new(&guest)
|
||||||
.args(&["--cpus", "boot=1"])
|
.args(&["--cpus", "boot=1"])
|
||||||
@ -1908,6 +2010,7 @@ mod parallel {
|
|||||||
.default_disks()
|
.default_disks()
|
||||||
.default_net()
|
.default_net()
|
||||||
.args(&["--serial", "tty", "--console", "off"])
|
.args(&["--serial", "tty", "--console", "off"])
|
||||||
|
.args(&["--event-monitor", format!("path={}", event_path).as_str()])
|
||||||
.capture_output()
|
.capture_output()
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1920,6 +2023,51 @@ mod parallel {
|
|||||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||||
assert!(guest.get_entropy().unwrap_or_default() >= 900);
|
assert!(guest.get_entropy().unwrap_or_default() >= 900);
|
||||||
assert_eq!(guest.get_pci_bridge_class().unwrap_or_default(), "0x060000");
|
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
|
||||||
|
));
|
||||||
|
|
||||||
|
guest.ssh_command("sudo poweroff").unwrap();
|
||||||
|
thread::sleep(std::time::Duration::new(5, 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));
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = child.kill();
|
let _ = child.kill();
|
||||||
@ -5225,9 +5373,11 @@ mod parallel {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let socket = temp_vsock_path(&guest.tmp_dir);
|
let socket = temp_vsock_path(&guest.tmp_dir);
|
||||||
|
let event_path = temp_event_monitor_path(&guest.tmp_dir);
|
||||||
|
|
||||||
let mut child = GuestCommand::new(&guest)
|
let mut child = GuestCommand::new(&guest)
|
||||||
.args(&["--api-socket", &api_socket_source])
|
.args(&["--api-socket", &api_socket_source])
|
||||||
|
.args(&["--event-monitor", format!("path={}", event_path).as_str()])
|
||||||
.args(&["--cpus", "boot=4"])
|
.args(&["--cpus", "boot=4"])
|
||||||
.args(&[
|
.args(&[
|
||||||
"--memory",
|
"--memory",
|
||||||
@ -5290,6 +5440,11 @@ mod parallel {
|
|||||||
Some(net_id),
|
Some(net_id),
|
||||||
));
|
));
|
||||||
thread::sleep(std::time::Duration::new(10, 0));
|
thread::sleep(std::time::Duration::new(10, 0));
|
||||||
|
let latest_events = [&MetaEvent {
|
||||||
|
event: "device-removed".to_string(),
|
||||||
|
device_id: Some(net_id.to_string()),
|
||||||
|
}];
|
||||||
|
assert!(check_latest_events_exact(&latest_events, &event_path));
|
||||||
|
|
||||||
// Plug the virtio-net device again
|
// Plug the virtio-net device again
|
||||||
assert!(remote_command(
|
assert!(remote_command(
|
||||||
@ -5302,6 +5457,17 @@ mod parallel {
|
|||||||
|
|
||||||
// Pause the VM
|
// Pause the VM
|
||||||
assert!(remote_command(&api_socket_source, "pause", None));
|
assert!(remote_command(&api_socket_source, "pause", None));
|
||||||
|
let latest_events = [
|
||||||
|
&MetaEvent {
|
||||||
|
event: "pausing".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
},
|
||||||
|
&MetaEvent {
|
||||||
|
event: "paused".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
assert!(check_latest_events_exact(&latest_events, &event_path));
|
||||||
|
|
||||||
// Take a snapshot from the VM
|
// Take a snapshot from the VM
|
||||||
assert!(remote_command(
|
assert!(remote_command(
|
||||||
@ -5312,6 +5478,18 @@ mod parallel {
|
|||||||
|
|
||||||
// Wait to make sure the snapshot is completed
|
// Wait to make sure the snapshot is completed
|
||||||
thread::sleep(std::time::Duration::new(10, 0));
|
thread::sleep(std::time::Duration::new(10, 0));
|
||||||
|
|
||||||
|
let latest_events = [
|
||||||
|
&MetaEvent {
|
||||||
|
event: "snapshotting".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
},
|
||||||
|
&MetaEvent {
|
||||||
|
event: "snapshotted".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
assert!(check_latest_events_exact(&latest_events, &event_path));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Shutdown the source VM and check console output
|
// Shutdown the source VM and check console output
|
||||||
@ -5333,10 +5511,15 @@ mod parallel {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let api_socket_restored = format!("{}.2", temp_api_path(&guest.tmp_dir));
|
let api_socket_restored = format!("{}.2", temp_api_path(&guest.tmp_dir));
|
||||||
|
let event_path_restored = format!("{}.2", temp_event_monitor_path(&guest.tmp_dir));
|
||||||
|
|
||||||
// Restore the VM from the snapshot
|
// Restore the VM from the snapshot
|
||||||
let mut child = GuestCommand::new(&guest)
|
let mut child = GuestCommand::new(&guest)
|
||||||
.args(&["--api-socket", &api_socket_restored])
|
.args(&["--api-socket", &api_socket_restored])
|
||||||
|
.args(&[
|
||||||
|
"--event-monitor",
|
||||||
|
format!("path={}", event_path_restored).as_str(),
|
||||||
|
])
|
||||||
.args(&[
|
.args(&[
|
||||||
"--restore",
|
"--restore",
|
||||||
format!("source_url=file://{}", snapshot_dir).as_str(),
|
format!("source_url=file://{}", snapshot_dir).as_str(),
|
||||||
@ -5347,10 +5530,46 @@ mod parallel {
|
|||||||
|
|
||||||
// Wait for the VM to be restored
|
// Wait for the VM to be restored
|
||||||
thread::sleep(std::time::Duration::new(10, 0));
|
thread::sleep(std::time::Duration::new(10, 0));
|
||||||
|
let expected_events = [
|
||||||
|
&MetaEvent {
|
||||||
|
event: "starting".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
},
|
||||||
|
&MetaEvent {
|
||||||
|
event: "restoring".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
assert!(check_sequential_events_exact(
|
||||||
|
&expected_events,
|
||||||
|
&event_path_restored
|
||||||
|
));
|
||||||
|
let latest_events = [&MetaEvent {
|
||||||
|
event: "restored".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
}];
|
||||||
|
assert!(check_latest_events_exact(
|
||||||
|
&latest_events,
|
||||||
|
&event_path_restored
|
||||||
|
));
|
||||||
|
|
||||||
let r = std::panic::catch_unwind(|| {
|
let r = std::panic::catch_unwind(|| {
|
||||||
// Resume the VM
|
// Resume the VM
|
||||||
assert!(remote_command(&api_socket_restored, "resume", None));
|
assert!(remote_command(&api_socket_restored, "resume", None));
|
||||||
|
let latest_events = [
|
||||||
|
&MetaEvent {
|
||||||
|
event: "resuming".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
},
|
||||||
|
&MetaEvent {
|
||||||
|
event: "resumed".to_string(),
|
||||||
|
device_id: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
assert!(check_latest_events_exact(
|
||||||
|
&latest_events,
|
||||||
|
&event_path_restored
|
||||||
|
));
|
||||||
|
|
||||||
// Perform same checks to validate VM has been properly restored
|
// Perform same checks to validate VM has been properly restored
|
||||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 4);
|
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 4);
|
||||||
|
Loading…
Reference in New Issue
Block a user