diff --git a/tests/integration.rs b/tests/integration.rs index 01cd4c16c..d7145bba4 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -23,6 +23,7 @@ mod tests { use std::io; use std::io::BufRead; use std::io::Read; + use std::io::Seek; use std::io::Write; use std::os::unix::io::AsRawFd; use std::path::PathBuf; @@ -70,6 +71,10 @@ mod tests { const FOCAL_IMAGE_NAME_VHD: &str = "focal-server-cloudimg-arm64-custom.vhd"; #[cfg(target_arch = "x86_64")] const FOCAL_IMAGE_NAME_VHD: &str = "focal-server-cloudimg-amd64-custom-20210609-0.vhd"; + #[cfg(target_arch = "aarch64")] + const FOCAL_IMAGE_NAME_VHDX: &str = "focal-server-cloudimg-arm64-custom.vhdx"; + #[cfg(target_arch = "x86_64")] + const FOCAL_IMAGE_NAME_VHDX: &str = "focal-server-cloudimg-amd64-custom-20210609-0.vhdx"; #[cfg(target_arch = "x86_64")] const WINDOWS_IMAGE_NAME: &str = "windows-server-2019.raw"; #[cfg(target_arch = "x86_64")] @@ -1983,6 +1988,8 @@ mod tests { } mod parallel { + use std::io::SeekFrom; + use crate::tests::*; #[test] @@ -2562,6 +2569,120 @@ mod tests { _test_virtio_block(FOCAL_IMAGE_NAME_VHD, false) } + #[test] + fn test_virtio_block_vhdx() { + let mut workload_path = dirs::home_dir().unwrap(); + workload_path.push("workloads"); + + let mut raw_file_path = workload_path.clone(); + let mut vhdx_file_path = workload_path; + raw_file_path.push(FOCAL_IMAGE_NAME); + vhdx_file_path.push(FOCAL_IMAGE_NAME_VHDX); + + // Generate dynamic VHDX file from RAW file + std::process::Command::new("qemu-img") + .arg("convert") + .arg("-p") + .args(&["-f", "raw"]) + .args(&["-O", "vhdx"]) + .arg(raw_file_path.to_str().unwrap()) + .arg(vhdx_file_path.to_str().unwrap()) + .output() + .expect("Expect generating dynamic VHDx image from RAW image"); + + _test_virtio_block(FOCAL_IMAGE_NAME_VHDX, false) + } + + #[test] + fn test_virtio_block_dynamic_vhdx_expand() { + const VIRTUAL_DISK_SIZE: u64 = 100 << 20; + const EMPTY_VHDX_FILE_SIZE: u64 = 8 << 20; + const FULL_VHDX_FILE_SIZE: u64 = 112 << 20; + const DYNAMIC_VHDX_NAME: &str = "dynamic.vhdx"; + + let mut workload_path = dirs::home_dir().unwrap(); + workload_path.push("workloads"); + + let mut vhdx_file_path = workload_path; + vhdx_file_path.push(DYNAMIC_VHDX_NAME); + let vhdx_path = vhdx_file_path.to_str().unwrap(); + + // Generate a 100 MiB dynamic VHDX file + std::process::Command::new("qemu-img") + .arg("create") + .args(&["-f", "vhdx"]) + .arg(vhdx_path) + .arg(VIRTUAL_DISK_SIZE.to_string()) + .output() + .expect("Expect generating dynamic VHDx image from RAW image"); + + // Check if the size matches with empty VHDx file size + assert_eq!(vhdx_image_size(vhdx_path), EMPTY_VHDX_FILE_SIZE); + + let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string()); + let guest = Guest::new(Box::new(focal)); + let kernel_path = direct_kernel_boot_path(); + + let mut cloud_child = GuestCommand::new(&guest) + .args(&["--cpus", "boot=1"]) + .args(&["--memory", "size=512M"]) + .args(&["--kernel", kernel_path.to_str().unwrap()]) + .args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) + .args(&[ + "--disk", + format!( + "path={}", + guest.disk_config.disk(DiskType::OperatingSystem).unwrap() + ) + .as_str(), + format!( + "path={}", + guest.disk_config.disk(DiskType::CloudInit).unwrap() + ) + .as_str(), + format!("path={}", vhdx_path).as_str(), + ]) + .default_net() + .capture_output() + .spawn() + .unwrap(); + + let r = std::panic::catch_unwind(|| { + guest.wait_vm_boot(None).unwrap(); + + // Check both if /dev/vdc exists and if the block size is 100 MiB. + assert_eq!( + guest + .ssh_command("lsblk | grep vdc | grep -c 100M") + .unwrap() + .trim() + .parse::() + .unwrap_or_default(), + 1 + ); + + // Write 100 MB of data to the VHDx disk + guest + .ssh_command("sudo dd if=/dev/urandom of=/dev/vdc bs=1M count=100") + .unwrap(); + }); + + // Check if the size matches with expected expanded VHDx file size + assert_eq!(vhdx_image_size(vhdx_path), FULL_VHDX_FILE_SIZE); + + let _ = cloud_child.kill(); + let output = cloud_child.wait_with_output().unwrap(); + + handle_child_output(r, &output); + } + + fn vhdx_image_size(disk_name: &str) -> u64 { + std::fs::File::open(disk_name) + .unwrap() + .seek(SeekFrom::End(0)) + .unwrap() + } + #[test] fn test_vhost_user_net_default() { test_vhost_user_net(None, 2, &prepare_vhost_user_net_daemon, false, false)