diff --git a/Jenkinsfile b/Jenkinsfile index d4321470b..a60fcbb20 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ stage ("Builds") { } stage ('Install system packages') { sh "sudo DEBIAN_FRONTEND=noninteractive apt-get install -yq build-essential mtools libssl-dev pkg-config" - sh "sudo apt-get install -yq flex bison libelf-dev" + sh "sudo apt-get install -yq flex bison libelf-dev qemu-utils" } stage ('Install Rust') { sh "nohup curl https://sh.rustup.rs -sSf | sh -s -- -y" diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh index a575b14da..994831611 100755 --- a/scripts/run_integration_tests.sh +++ b/scripts/run_integration_tests.sh @@ -24,6 +24,14 @@ if [ ! -f "$OS_IMAGE" ]; then popd fi +OS_RAW_IMAGE_NAME="clear-29810-cloud-raw.img" +OS_RAW_IMAGE="$WORKLOADS_DIR/$OS_RAW_IMAGE_NAME" +if [ ! -f "$OS_RAW_IMAGE" ]; then + pushd $WORKLOADS_DIR + qemu-img convert -p -f qcow2 -O raw $OS_IMAGE_NAME $OS_RAW_IMAGE_NAME + popd +fi + # Build generic kernel VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux" BZIMAGE_IMAGE="$WORKLOADS_DIR/bzImage" diff --git a/src/main.rs b/src/main.rs index 8049aca88..f6f59d27c 100755 --- a/src/main.rs +++ b/src/main.rs @@ -155,10 +155,11 @@ extern crate credibility; #[cfg(feature = "integration_tests")] mod tests { use ssh2::Session; - use std::fs; - use std::io::Read; + use std::fs::{self, read, OpenOptions}; + use std::io::{Read, Write}; use std::net::TcpStream; use std::process::Command; + use std::string::String; use std::thread; fn ssh_command(command: &str) -> String { @@ -214,12 +215,18 @@ mod tests { 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 = "/tmp/osdisk.img"; + let osdisk_raw_path = "/tmp/osdisk_raw.img"; let cloudinit_path = "/tmp/cloudinit.img"; 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]; + let disks = vec![osdisk_path, cloudinit_path, osdisk_raw_path]; (disks, String::from(fw_path.to_str().unwrap())) } @@ -569,4 +576,129 @@ mod tests { Ok(()) }); } + + #[test] + fn test_virtio_pmem() { + test_block!(tb, "", { + let (disks, _) = prepare_files(); + 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/pmem-file"; + let mut pmem_backend_file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(pmem_backend_path) + .unwrap(); + + let pmem_backend_content = "foo"; + pmem_backend_file + .write_all(pmem_backend_content.as_bytes()) + .unwrap(); + let pmem_backend_file_size = 0x1000; + pmem_backend_file.set_len(pmem_backend_file_size).unwrap(); + + let mut child = Command::new("target/debug/cloud-hypervisor") + .args(&["--cpus", "1"]) + .args(&["--memory", "size=512"]) + .args(&["--kernel", kernel_path.to_str().unwrap()]) + .args(&["--disk", disks[0], disks[1]]) + .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&[ + "--pmem", + format!( + "file={},size={}", + pmem_backend_path, + pmem_backend_file_size + ) + .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(10, 0)); + + // Check for the presence of /dev/pmem0 + aver_eq!(tb, ssh_command("ls /dev/pmem0").trim(), "/dev/pmem0"); + // Check content + aver_eq!( + tb, + &ssh_command("sudo cat /dev/pmem0").trim()[..pmem_backend_content.len()], + pmem_backend_content + ); + // Modify content + let new_content = "bar"; + ssh_command( + format!( + "sudo bash -c 'echo {} > /dev/pmem0' && sudo sync /dev/pmem0", + new_content + ) + .as_str(), + ); + + // Check content from host + aver_eq!( + tb, + &String::from_utf8(read(pmem_backend_path).unwrap()) + .unwrap() + .as_str()[..new_content.len()], + new_content + ); + + ssh_command("sudo reboot"); + let _ = child.wait(); + + // Cleanup the file + fs::remove_file(pmem_backend_path).unwrap(); + + Ok(()) + }); + } + + #[test] + fn test_boot_from_virtio_pmem() { + test_block!(tb, "", { + let (disks, _) = prepare_files(); + 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 mut child = Command::new("target/debug/cloud-hypervisor") + .args(&["--cpus", "1"]) + .args(&["--memory", "size=512"]) + .args(&["--kernel", kernel_path.to_str().unwrap()]) + .args(&["--disk", disks[1]]) + .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&[ + "--pmem", + format!( + "file={},size={}", + disks[2], + fs::metadata(disks[2]).unwrap().len() + ) + .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(10, 0)); + + // Simple checks to validate the VM booted properly + aver_eq!(tb, get_cpu_count(), 1); + aver!(tb, get_total_memory() > 496_000); + + ssh_command("sudo reboot"); + let _ = child.wait(); + + Ok(()) + }); + } }