From e5ad85d7def285d093f85916b7096ec5cb412297 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Mon, 13 Apr 2026 23:34:26 +0200 Subject: [PATCH] test_infra: Prepare QCOW2 overlay for Windows guests WindowsDiskConfig now creates a qcow2 overlay backed by the raw Windows image during prepare_files(). The overlay is placed under ~/workloads alongside the raw image. Writes go into the overlay so the backing raw image stays unmodified, matching the CoW semantics already provided by the dm snapshot for raw tests. Drop removes the qcow2 file. The DiskConfig trait gains a qcow2_disk() default method returning None. WindowsDiskConfig overrides it to expose the overlay path. Signed-off-by: Anatol Belski --- test_infra/src/lib.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test_infra/src/lib.rs b/test_infra/src/lib.rs index e32ee791f..091fef7c8 100644 --- a/test_infra/src/lib.rs +++ b/test_infra/src/lib.rs @@ -226,6 +226,9 @@ pub trait DiskConfig { fn prepare_files(&mut self, tmp_dir: &TempDir, network: &GuestNetworkConfig); fn prepare_cloudinit(&self, tmp_dir: &TempDir, network: &GuestNetworkConfig) -> String; fn disk(&self, disk_type: DiskType) -> Option; + fn qcow2_disk(&self) -> Option { + None + } } #[derive(Clone)] @@ -248,6 +251,7 @@ impl UbuntuDiskConfig { pub struct WindowsDiskConfig { image_name: String, osdisk_path: String, + osdisk_qcow2_path: String, loopback_device: String, windows_snapshot_cow: String, windows_snapshot: String, @@ -258,6 +262,7 @@ impl WindowsDiskConfig { WindowsDiskConfig { image_name, osdisk_path: String::new(), + osdisk_qcow2_path: String::new(), loopback_device: String::new(), windows_snapshot_cow: String::new(), windows_snapshot: String::new(), @@ -286,6 +291,10 @@ impl Drop for WindowsDiskConfig { .args(["-d", self.loopback_device.as_str()]) .output() .expect("Expect removing loopback device to succeed"); + + if !self.osdisk_qcow2_path.is_empty() { + let _ = fs::remove_file(&self.osdisk_qcow2_path); + } } } @@ -451,7 +460,7 @@ impl DiskConfig for WindowsDiskConfig { let mut osdisk_path = workload_path; osdisk_path.push(&self.image_name); - let osdisk_blk_size = fs::metadata(osdisk_path) + let osdisk_blk_size = fs::metadata(&osdisk_path) .expect("Expect retrieving Windows image metadata") .len() >> 9; @@ -530,6 +539,27 @@ impl DiskConfig for WindowsDiskConfig { self.osdisk_path = format!("/dev/mapper/{windows_snapshot}"); self.windows_snapshot_cow = windows_snapshot_cow; self.windows_snapshot = windows_snapshot; + + // Create a qcow2 overlay backed by the raw image. + let mut workload_path = dirs::home_dir().unwrap(); + workload_path.push("workloads"); + let qcow2_name = format!("windows-qcow2-{}.qcow2", random_extension.to_str().unwrap()); + let qcow2_path = workload_path.join(&qcow2_name); + let output = Command::new("qemu-img") + .args([ + "create", + "-f", + "qcow2", + "-b", + osdisk_path.to_str().unwrap(), + "-F", + "raw", + qcow2_path.to_str().unwrap(), + ]) + .output() + .expect("Expect creating qcow2 overlay to succeed"); + assert!(output.status.success(), "qemu-img create failed"); + self.osdisk_qcow2_path = qcow2_path.to_str().unwrap().to_string(); } fn disk(&self, disk_type: DiskType) -> Option { @@ -538,6 +568,10 @@ impl DiskConfig for WindowsDiskConfig { DiskType::CloudInit => None, } } + + fn qcow2_disk(&self) -> Option { + Some(self.osdisk_qcow2_path.clone()) + } } pub fn rate_limited_copy, Q: AsRef>(from: P, to: Q) -> io::Result {