File Sharing¶
Every experiment needs to move files between the host and the guest VM: playbook data, agent wheels, test functions, tools, and – after execution – artifacts and logs. ADARE uses a strategy pattern to select the best file transfer mechanism for the current platform and available tooling.
The FileTransferStrategy Interface¶
All strategies implement the abstract base class
adare.hypervisor.qemu.file_transfer.base.FileTransferStrategy.
The lifecycle calls three methods in order:
setup(context)– called before VM boot. Prepares the transfer mechanism: creates shared directories, copies files to a staging area, or writes files directly to the guest disk.post_boot_transfer(context)– called after the VM has booted and the guest agent is ready. Mounts filesystems, uploads files via QGA, or performs other actions that require a running guest.retrieve_artifacts(context)– called at experiment end. Collects logs, screenshots, and test artifacts from the guest.
Each strategy also declares:
requires_vm_stop_for_retrieval()– whether the VM must be shut down before artifacts can be retrieved (Truefor Libguestfs,Falsefor the others).setup_description,post_boot_description,retrieval_description– human-readable labels used in progress output.
Strategies¶
VirtioFS (Linux default)¶
VirtioFSStrategy – the fastest option. Uses the virtiofsd daemon and
the kernel virtiofs driver to expose host directories directly inside the
guest. No file copying is needed; the guest reads and writes the host
filesystem through shared memory.
Setup: creates host directories, writes
config.jsonto the run directory, stores the share list inQEMUVMConfigso the libvirt XML builder addsvirtiofsfilesystem devices.Post-boot: mounts each share inside the guest (
mount -t virtiofs <tag> /adare/<name>on Linux;virtiofs.exe -t <tag> -m C:\adare\<name>on Windows).Retrieval: artifacts are already on the host – the strategy only verifies and copies log files to the expected locations.
SMB (macOS default)¶
SMBStrategy – uses QEMU’s built-in SLIRP SMB support. On macOS,
virtiofsd is typically unavailable, but Samba can be installed via
Homebrew. QEMU starts an embedded smbd process that serves a host
directory to the guest at //10.0.2.4/qemu.
Setup: builds the share list (same as VirtioFS), creates a temporary directory with copies of each share’s host content (Samba 4.x blocks symlinks outside the share root), and sets
smb_share_pathon the VM config.Post-boot: mounts the CIFS share inside the guest (
mount -t cifs //10.0.2.4/qemu /adareon Linux;net use Z:plus directory junctions on Windows). If the mount fails, falls back automatically to the QGA strategy.Retrieval: copies read-write shares back from the temporary directory to the original host paths (the writeback step), then verifies artifacts.
Cleanup: performs a safety writeback and removes the temporary directory.
Note
The fallback to QGA is logged as a warning and relabels the progress
stage. It is a large performance regression (QGA transfers file-by-file
through the guest agent), so it should never pass unnoticed. If you see it,
the two causes worth checking first are a guest image without
cifs-utils/mount.cifs, and a guest with no IPv4 route to
10.0.2.4 – see Guest network repair.
Guest network repair¶
Every SMB mount depends on the guest actually having a working network, and that cannot be taken for granted, because ADARE gives the NIC a different PCI address at run time than the installers use:
the VM creators build a raw QEMU command line with an auto-assigned NIC (
-device virtio-net-pci,netdev=net0), which lands onpcie.0slot0x01, so the guest seesenp0s1– and that is the name the installer bakes into/etc/netplan/*.yaml;experiment runs go through
libvirt_xml_builder._add_network_commandline(), which pinsbus=pcie.0,addr=0x1f, so the same NIC enumerates asenp0s31.
That path is taken for every experiment, because _add_network() defers to
the qemu:commandline builder whenever SMB or port forwarding is active,
and the adarevm websocket forward is always active.
A guest whose network config names one interface therefore ends up with only
lo: systemd-networkd-wait-online burns its full 120 s timeout on
every boot, nothing can reach the SLIRP SMB server, and the adarevm websocket
has no guest-side stack. NetworkManager-managed guests (Fedora) are unaffected,
and Ubuntu 24.04 is rescued by NetworkManager after paying the 120 s.
hypervisor/qemu/guest_network.py repairs this after boot, before anything
depends on the network. It is idempotent and best-effort:
if any non-loopback interface already has an IPv4 address, do nothing;
otherwise bring every non-loopback link up and give the guest’s own DHCP client a few seconds to claim it;
if nothing claims it, assign SLIRP’s fixed addressing directly –
10.0.2.15/24, gateway10.0.2.2– and point the resolver at10.0.2.3viaresolvectl(needed because the agent bootstrap installs from PyPI).
Nothing is written to the guest filesystem on the normal path – only in-memory
kernel and systemd-resolved state – so guest disk state stays clean for
forensic purposes. Only if resolvectl is unavailable does it fall back to
writing /etc/resolv.conf.
The install-time half of the fix is in the autoinstall_* templates, which now
match the interface by pattern (match: {name: "e*"}) instead of by name,
so images built from here on are immune to the PCI address. Images built before
that change still need the run-time repair, and still pay the 120 s
wait-online timeout on boot – which is why the guest-agent readiness budget
escalates per attempt (see lifecycle.py:_ready_timeout_for_attempt).
Libguestfs (Linux fallback)¶
LibguestfsStrategy – manipulates the guest disk offline using the
guestfish CLI. This is the fallback on Linux when virtiofsd is not
installed, or when the QEMU_LIBGUESTFS environment variable is set.
Setup: stops the VM (if running), mounts the disk image with guestfish, copies files to the guest filesystem, and unmounts.
Post-boot: no action needed – files are already on disk.
Retrieval: requires the VM to be stopped first (
requires_vm_stop_for_retrieval()returnsTrue), then extracts artifacts from the disk via guestfish.
QGA (final fallback)¶
QGAStrategy – transfers files through QEMU Guest Agent
guest-file-* operations. This is the fallback on macOS when neither
virtiofsd nor Samba is available. It is the slowest strategy because
every file is serialised and sent individually over the QGA channel.
Setup: builds a file manifest and disables VirtioFS config. The actual upload is deferred because QGA requires a running VM.
Post-boot: uploads all files from the manifest via QGA guest-file operations.
Retrieval: downloads artifacts via QGA before VM shutdown.
Warning
Known limitation: QGA-via-libvirt is not a dependable bulk transport for the
aarch64 Ubuntu/Kubuntu desktop guests. Partway through an upload the agent
stops answering even libvirt’s 5-second guest-sync
(Guest agent is not responding: guest agent didn't respond to synchronization
within '5' seconds), after which every guest-file-write trips
QGA_FILE_OP_TIMEOUT. It fails on the ~2 MB tar first, then again on the
61,828-byte adarevm wheel that the per-file fallback retries — which is the
Failed to upload adarevm-*.whl error seen in adare env verify.
The usual suspects have been measured out:
not throughput — the identical 61,828-byte
guest-file-writecompletes in about 1 ms (45-53 MB/s) on three of the same disks when written straight to the QGA unix socket rather than through libvirt;not the timeout — the agent is unresponsive to a 5 s sync, so a larger
QGA_FILE_OP_TIMEOUTchanges nothing;not the chunk size — tested at both 16 KB (~22 KB base64) and 64 KB (~85 KB); both wedge at the same point.
The root cause inside qemu-ga/libvirt has not been identified. The practical
consequence is that guests must be kept off this path: make sure the image has
cifs-utils/mount.cifs so the SMB strategy is used (~1 s for the same
payload). The autoinstall templates now install it; images built before that
change need a rebuild or an env extend.
Strategy Selection¶
The factory function get_file_transfer_strategy() in
adare.hypervisor.qemu.file_transfer calls detect_file_transfer_mode()
to choose a strategy. The decision logic:
If the
QEMU_LIBGUESTFSenvironment variable is set totrue, force libguestfs mode.If
virtiofsdis onPATH, use virtiofs.On macOS without
virtiofsd:If
smbdis available (and QEMU can find it at its compiled-in path), use smb.If
guestfishis available and its appliance is functional, use libguestfs.Otherwise, use qga.
On Linux without
virtiofsd, use libguestfs.
The smbd detection is macOS-aware: QEMU hardcodes the smbd path at
compile time (typically /opt/local/sbin/smbd for MacPorts builds), so
ADARE checks whether that exact path exists and is executable. If Homebrew’s
Samba is installed at a different path, ADARE logs a symlink instruction.