Reference KVM domain disks by host path so AppArmor-confined QEMU can open them

The first run against a real hypervisor (beelink, Ubuntu, libvirt 11.6)
failed at DomainCreate with "Could not open base image: Permission denied"
even with correct DAC ownership. libvirt's virt-aa-helper only grants QEMU
access to disk sources that carry a path; the <disk type="volume"> form
chosen in Step 2 gives it pool and volume names instead, so the overlay and
its backing image get no AppArmor rule on any stock Debian/Ubuntu host.

buildDomainXML now takes DiskPath/SeedPath and emits type="file" disks.
ensureOverlay and writeSeed return the volume handles and Create resolves
both paths with StorageVolGetPath before defining the domain. The domain
XML test rejects any volume/pool reference so this cannot drift back; the
Create test asserts the sources equal the paths libvirt reported.

Execution log: Step 2 note marked reverted, Step 7 entry opened with the
two failed runs and the diagnosis.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 19:13:51 +00:00
co-authored by Claude Fable 5.1
parent 791988b5a0
commit cd51886139
5 changed files with 125 additions and 40 deletions
+7
View File
@@ -210,6 +210,13 @@ func TestCreate_freshHost(t *testing.T) {
if d.Devices.Interfaces[0].Source.Bridge != "br0" {
t.Errorf("bridge = %q, want br0", d.Devices.Interfaces[0].Source.Bridge)
}
wantDisk, wantSeed := api.poolPath+"/"+overlayVolumeName(testName), api.poolPath+"/"+seedVolumeName(testName)
if got := d.Devices.Disks[0].Source.File; got != wantDisk {
t.Errorf("overlay disk source = %q, want the path libvirt reported, %q", got, wantDisk)
}
if got := d.Devices.Disks[1].Source.File; got != wantSeed {
t.Errorf("seed cdrom source = %q, want the path libvirt reported, %q", got, wantSeed)
}
if m := d.Metadata.Instance; m.UID != testUID || !m.managed() || !m.createdAt().Equal(testNow) {
t.Errorf("metadata = %+v, want managed, uid %s, createdAt %s", m, testUID, testNow)
}