use std::io;
use std::io::{Seek, SeekFrom};
use std::thread;
use std::time::Duration;
use snapshot::Snapshot;
use utils::tempfile::TempFile;
use vmm::builder::{build_microvm_for_boot, build_microvm_from_snapshot, setup_serial_device};
use vmm::persist::{self, snapshot_state_sanity_check, LoadSnapshotError, MicrovmState};
use vmm::resources::VmResources;
use vmm::seccomp_filters::{get_filters, SeccompConfig};
use vmm::version_map::VERSION_MAP;
use vmm::vmm_config::snapshot::{CreateSnapshotParams, SnapshotType};
use vmm::{EventManager, FC_EXIT_CODE_OK};
use vmm::utilities::mock_devices::MockSerialInput;
use vmm::utilities::mock_resources::{MockVmResources, NOISY_KERNEL_IMAGE};
#[cfg(target_arch = "x86_64")]
use vmm::utilities::test_utils::dirty_tracking_vmm;
use vmm::utilities::test_utils::{create_vmm, default_vmm};
use vmm::vmm_config::instance_info::InstanceInfo;
#[test]
fn test_setup_serial_device() {
let read_tempfile = TempFile::new().unwrap();
let read_handle = MockSerialInput(read_tempfile.into_file());
let mut event_manager = EventManager::new().unwrap();
assert!(setup_serial_device(
&mut event_manager,
Box::new(read_handle),
Box::new(io::stdout()),
)
.is_ok());
}
#[test]
fn test_build_microvm() {
{
let resources: VmResources = MockVmResources::new().into();
let mut event_manager = EventManager::new().unwrap();
let mut empty_seccomp_filters = get_filters(SeccompConfig::None).unwrap();
let vmm_ret = build_microvm_for_boot(
&InstanceInfo::default(),
&resources,
&mut event_manager,
&mut empty_seccomp_filters,
);
assert_eq!(format!("{:?}", vmm_ret.err()), "Some(MissingKernelConfig)");
}
let (vmm, mut _evmgr) = default_vmm(None);
#[cfg(target_arch = "x86_64")]
_evmgr.run_with_timeout(500).unwrap();
#[cfg(target_arch = "aarch64")]
vmm.lock().unwrap().stop(FC_EXIT_CODE_OK);
assert_eq!(
vmm.lock().unwrap().shutdown_exit_code(),
Some(FC_EXIT_CODE_OK)
);
}
#[test]
fn test_pause_resume_microvm() {
let (vmm, _) = default_vmm(None);
assert!(vmm.lock().unwrap().pause_vm().is_ok());
assert!(vmm.lock().unwrap().pause_vm().is_ok());
assert!(vmm.lock().unwrap().resume_vm().is_ok());
vmm.lock().unwrap().stop(FC_EXIT_CODE_OK);
}
#[test]
fn test_dirty_bitmap_error() {
let (vmm, _) = default_vmm(None);
assert_eq!(
format!("{:?}", vmm.lock().unwrap().get_dirty_bitmap().err()),
"Some(DirtyBitmap(Error(2)))"
);
vmm.lock().unwrap().stop(FC_EXIT_CODE_OK);
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_dirty_bitmap_success() {
let (vmm, _) = dirty_tracking_vmm(Some(NOISY_KERNEL_IMAGE));
thread::sleep(Duration::from_millis(100));
let bitmap = vmm.lock().unwrap().get_dirty_bitmap().unwrap();
let num_dirty_pages: u32 = bitmap
.iter()
.map(|(_, bitmap_per_region)| {
let num_dirty_pages_per_region: u32 =
bitmap_per_region.iter().map(|n| n.count_ones()).sum();
num_dirty_pages_per_region
})
.sum();
assert!(num_dirty_pages > 0);
vmm.lock().unwrap().stop(FC_EXIT_CODE_OK);
}
#[test]
fn test_disallow_snapshots_without_pausing() {
let (vmm, _) = default_vmm(Some(NOISY_KERNEL_IMAGE));
match vmm.lock().unwrap().save_state() {
Err(e) => assert!(format!("{:?}", e).contains("NotAllowed")),
Ok(_) => panic!("Should not be allowed."),
};
vmm.lock().unwrap().pause_vm().unwrap();
vmm.lock().unwrap().save_state().unwrap();
vmm.lock().unwrap().stop(FC_EXIT_CODE_OK);
}
fn verify_create_snapshot(is_diff: bool) -> (TempFile, TempFile) {
let snapshot_file = TempFile::new().unwrap();
let memory_file = TempFile::new().unwrap();
let (vmm, _) = create_vmm(Some(NOISY_KERNEL_IMAGE), is_diff);
thread::sleep(Duration::from_millis(200));
vmm.lock().unwrap().pause_vm().unwrap();
let snapshot_type = match is_diff {
true => SnapshotType::Diff,
false => SnapshotType::Full,
};
let snapshot_params = CreateSnapshotParams {
snapshot_type,
snapshot_path: snapshot_file.as_path().to_path_buf(),
mem_file_path: memory_file.as_path().to_path_buf(),
version: Some(String::from("0.24.0")),
};
{
let mut locked_vmm = vmm.lock().unwrap();
persist::create_snapshot(&mut locked_vmm, &snapshot_params, VERSION_MAP.clone()).unwrap();
}
vmm.lock().unwrap().stop(FC_EXIT_CODE_OK);
let snapshot_path = snapshot_file.as_path().to_path_buf();
let snapshot_file_metadata = std::fs::metadata(snapshot_path).unwrap();
let snapshot_len = snapshot_file_metadata.len() as usize;
let restored_microvm_state: MicrovmState = Snapshot::load(
&mut snapshot_file.as_file(),
snapshot_len,
VERSION_MAP.clone(),
)
.unwrap();
let memory_file_size_mib = memory_file.as_file().metadata().unwrap().len() >> 20;
assert_eq!(
restored_microvm_state.vm_info.mem_size_mib,
memory_file_size_mib
);
assert_eq!(restored_microvm_state.device_states.block_devices.len(), 0);
assert_eq!(restored_microvm_state.device_states.net_devices.len(), 0);
assert!(restored_microvm_state.device_states.vsock_device.is_none());
assert_eq!(restored_microvm_state.vcpu_states.len(), 1);
(snapshot_file, memory_file)
}
fn verify_load_snapshot(snapshot_file: TempFile, memory_file: TempFile) {
use vm_memory::GuestMemoryMmap;
use vmm::memory_snapshot::SnapshotMemory;
let mut event_manager = EventManager::new().unwrap();
let mut empty_seccomp_filters = get_filters(SeccompConfig::None).unwrap();
let snapshot_file_metadata = snapshot_file.as_file().metadata().unwrap();
let snapshot_len = snapshot_file_metadata.len() as usize;
snapshot_file.as_file().seek(SeekFrom::Start(0)).unwrap();
let microvm_state: MicrovmState = Snapshot::load(
&mut snapshot_file.as_file(),
snapshot_len,
VERSION_MAP.clone(),
)
.unwrap();
let mem = GuestMemoryMmap::restore(memory_file.as_file(), µvm_state.memory_state, false)
.unwrap();
let vmm = build_microvm_from_snapshot(
&InstanceInfo::default(),
&mut event_manager,
microvm_state,
mem,
false,
&mut empty_seccomp_filters,
)
.unwrap();
vmm.lock().unwrap().stop(FC_EXIT_CODE_OK);
}
#[test]
fn test_create_and_load_snapshot() {
let (snapshot_file, memory_file) = verify_create_snapshot(true);
verify_load_snapshot(snapshot_file, memory_file);
let (snapshot_file, memory_file) = verify_create_snapshot(false);
verify_load_snapshot(snapshot_file, memory_file);
}
#[test]
fn test_snapshot_load_sanity_checks() {
use vmm::vmm_config::machine_config::MAX_SUPPORTED_VCPUS;
let mut microvm_state = get_microvm_state_from_snapshot();
assert!(snapshot_state_sanity_check(µvm_state).is_ok());
microvm_state.memory_state.regions.clear();
let err = snapshot_state_sanity_check(µvm_state).unwrap_err();
match err {
LoadSnapshotError::InvalidSnapshot(err_msg) => {
assert_eq!(err_msg, "No memory region defined.")
}
_ => unreachable!(),
}
for _ in 0..(MAX_SUPPORTED_VCPUS as f64).log2() as usize {
microvm_state
.vcpu_states
.append(&mut microvm_state.vcpu_states.clone());
}
microvm_state
.vcpu_states
.push(microvm_state.vcpu_states[0].clone());
let err = snapshot_state_sanity_check(µvm_state).unwrap_err();
match err {
LoadSnapshotError::InvalidSnapshot(err_msg) => assert_eq!(err_msg, "Invalid vCPU count."),
_ => unreachable!(),
}
microvm_state.vcpu_states.clear();
let err = snapshot_state_sanity_check(µvm_state).unwrap_err();
match err {
LoadSnapshotError::InvalidSnapshot(err_msg) => assert_eq!(err_msg, "Invalid vCPU count."),
_ => unreachable!(),
}
}
fn get_microvm_state_from_snapshot() -> MicrovmState {
let (snapshot_file, _) = verify_create_snapshot(true);
let snapshot_file_metadata = snapshot_file.as_file().metadata().unwrap();
let snapshot_len = snapshot_file_metadata.len() as usize;
snapshot_file.as_file().seek(SeekFrom::Start(0)).unwrap();
Snapshot::load(
&mut snapshot_file.as_file(),
snapshot_len,
VERSION_MAP.clone(),
)
.unwrap()
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_snapshot_cpu_vendor() {
use vmm::persist::validate_cpu_vendor;
let microvm_state = get_microvm_state_from_snapshot();
assert!(validate_cpu_vendor(µvm_state).is_ok());
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_snapshot_cpu_vendor_mismatch() {
use vmm::persist::validate_cpu_vendor;
let mut microvm_state = get_microvm_state_from_snapshot();
assert!(validate_cpu_vendor(µvm_state).is_ok());
for entry in microvm_state.vcpu_states[0].cpuid.as_mut_slice().iter_mut() {
if entry.function == 0 && entry.index == 0 {
assert_ne!(entry.ebx, 0);
assert_ne!(entry.ecx, 0);
assert_ne!(entry.edx, 0);
entry.ebx = 0;
break;
}
}
assert!(validate_cpu_vendor(µvm_state).is_err());
for entry in microvm_state.vcpu_states[0].cpuid.as_mut_slice().iter_mut() {
if entry.function == 0 && entry.index == 0 {
entry.function = 1234;
}
}
assert!(validate_cpu_vendor(µvm_state).is_err());
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_snapshot_cpu_vendor_missing() {
use vmm::persist::validate_cpu_vendor;
let mut microvm_state = get_microvm_state_from_snapshot();
assert!(validate_cpu_vendor(µvm_state).is_ok());
for entry in microvm_state.vcpu_states[0].cpuid.as_mut_slice().iter_mut() {
if entry.function == 0 && entry.index == 0 {
entry.function = 1234;
}
}
assert!(validate_cpu_vendor(µvm_state).is_err());
}
#[cfg(target_arch = "aarch64")]
#[test]
fn test_snapshot_cpu_vendor() {
use vmm::persist::validate_cpu_manufacturer_id;
let microvm_state = get_microvm_state_from_snapshot();
assert!(validate_cpu_manufacturer_id(µvm_state).is_ok());
}
#[cfg(target_arch = "aarch64")]
#[test]
fn test_snapshot_cpu_vendor_missing() {
use arch::regs::MIDR_EL1;
use vmm::persist::validate_cpu_manufacturer_id;
let mut microvm_state = get_microvm_state_from_snapshot();
assert!(validate_cpu_manufacturer_id(µvm_state).is_ok());
for state in microvm_state.vcpu_states.as_mut_slice().iter_mut() {
for reg in state.regs.as_mut_slice().iter_mut() {
if reg.id == MIDR_EL1 {
reg.id = 0;
}
}
}
assert!(validate_cpu_manufacturer_id(µvm_state).is_err());
}
#[cfg(target_arch = "aarch64")]
#[test]
fn test_snapshot_cpu_vendor_mismatch() {
use arch::regs::MIDR_EL1;
use vmm::persist::validate_cpu_manufacturer_id;
let mut microvm_state = get_microvm_state_from_snapshot();
assert!(validate_cpu_manufacturer_id(µvm_state).is_ok());
for state in microvm_state.vcpu_states.as_mut_slice().iter_mut() {
for reg in state.regs.as_mut_slice().iter_mut() {
if reg.id == MIDR_EL1 {
reg.addr = 0x710FD081;
}
}
}
assert!(validate_cpu_manufacturer_id(µvm_state).is_err());
}