From e5256696d6100219a7792aadf13d6b09a25487a8 Mon Sep 17 00:00:00 2001 From: sjat Date: Thu, 18 Jun 2026 14:56:35 +0200 Subject: [PATCH] fix(integration-vm): place VM disk/seed/console in CACHE_DIR for system-qemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under qemu:///system the hypervisor runs as libvirt-qemu, which cannot traverse /home/claude — so the overlay/seed/console must live in /var/lib/boma-integration (group libvirt, world-traversable, created by the integration_test role), not the repo/home RUN_DIR. The inventory (hosts.yml + group_vars symlink, read by ansible as claude) stays in RUN_DIR. Verified: virt-install now creates the domain. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/integration-vm.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/scripts/integration-vm.py b/scripts/integration-vm.py index d71c781..a425c40 100644 --- a/scripts/integration-vm.py +++ b/scripts/integration-vm.py @@ -182,14 +182,16 @@ def up(host, name=None, mem_mib=DEFAULT_MEM_MIB, vcpus=DEFAULT_VCPUS): img = ensure_image() net_ensure() RUN_DIR.mkdir(parents=True, exist_ok=True) - overlay = RUN_DIR / f"{name}.qcow2" + # VM disk/seed/console must live where the SYSTEM hypervisor (libvirt-qemu) can reach + # them — NOT under the repo/home (qemu cannot traverse /home/claude). CACHE_DIR is + # group-libvirt + world-traversable (created by the integration_test role). + overlay = CACHE_DIR / f"{name}.qcow2" sh(["qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", str(img), str(overlay)]) (RUN_DIR / "user-data").write_text(render_user_data(_ssh_pubkey(), "ansible")) (RUN_DIR / "meta-data").write_text(render_meta_data(f"iid-{name}", name)) - seed = RUN_DIR / f"{name}-seed.img" + seed = CACHE_DIR / f"{name}-seed.img" sh(["cloud-localds", str(seed), str(RUN_DIR / "user-data"), str(RUN_DIR / "meta-data")]) - DIAG_ROOT.mkdir(parents=True, exist_ok=True) - console = DIAG_ROOT / f"{name}-console.log" + console = CACHE_DIR / f"{name}-console.log" sh(["virt-install", "--name", name, "--memory", str(mem_mib), "--vcpus", str(vcpus), "--import", "--disk", f"path={overlay},format=qcow2", @@ -327,7 +329,7 @@ def dump_diagnostics(name, ip): "-o", "UserKnownHostsFile=/dev/null", f"ansible@{ip}", "sudo " + cmd], check=False, capture=True) (d / f"{label}.txt").write_text((r.stdout or "") + (r.stderr or "")) - console = DIAG_ROOT / f"{name}-console.log" + console = CACHE_DIR / f"{name}-console.log" if console.exists(): shutil.copy(console, d / "console.log") print(f"diagnostics written to {d}", file=sys.stderr) @@ -336,8 +338,9 @@ def dump_diagnostics(name, ip): def _destroy(name): sh(["virsh", "destroy", name], check=False) sh(["virsh", "undefine", name, "--nvram"], check=False) - for f in RUN_DIR.glob(f"{name}*"): - f.unlink(missing_ok=True) + for base in (RUN_DIR, CACHE_DIR): + for f in base.glob(f"{name}*"): + f.unlink(missing_ok=True) def down(host=None, keep=False): @@ -363,7 +366,7 @@ def prune(): def console(): name = (RUN_DIR / "current").read_text().splitlines()[0] - log = DIAG_ROOT / f"{name}-console.log" + log = CACHE_DIR / f"{name}-console.log" print(log.read_text() if log.exists() else f"no console log at {log}")