kvm2: domain and volume XML, metadata element and NoCloud seed builders

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 16:57:30 +00:00
co-authored by Claude Fable 5.1
parent 95768281be
commit 1f10a51f82
9 changed files with 1473 additions and 2 deletions
+256
View File
@@ -0,0 +1,256 @@
package kvm2
import (
"encoding/xml"
"time"
"gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider"
)
// domainParams is everything buildDomainXML needs, already resolved: the
// paths are what StorageVolGetPath returned for the overlay and the seed.
type domainParams struct {
Name string
UID string
CreatedAt time.Time
VCPUs int32
MemoryMiB int64
Firmware string
Bridge string
DiskPath string
SeedPath string
DiskFormat string
}
const (
domainArch = "x86_64"
domainMachine = "q35"
guestAgentChannelName = "org.qemu.guest_agent.0"
)
type domainXML struct {
XMLName xml.Name `xml:"domain"`
Type string `xml:"type,attr"`
Name string `xml:"name"`
Memory domainMemoryXML `xml:"memory"`
VCPU int32 `xml:"vcpu"`
Metadata domainMetadataXML `xml:"metadata"`
OS domainOSXML `xml:"os"`
Features domainFeaturesXML `xml:"features"`
CPU domainCPUXML `xml:"cpu"`
OnPoweroff string `xml:"on_poweroff"`
OnReboot string `xml:"on_reboot"`
OnCrash string `xml:"on_crash"`
Devices domainDevicesXML `xml:"devices"`
}
type domainMemoryXML struct {
Unit string `xml:"unit,attr"`
Value int64 `xml:",chardata"`
}
type domainMetadataXML struct {
Instance instanceMetadata `xml:"instance"`
}
type domainOSXML struct {
// Firmware is the attribute firmware="efi"; FirmwareFeatures is the
// <firmware> child that turns secure boot off. Both are absent for BIOS.
Firmware string `xml:"firmware,attr,omitempty"`
Type domainOSTypeXML `xml:"type"`
FirmwareFeatures *domainFirmwareXML `xml:"firmware"`
}
type domainOSTypeXML struct {
Arch string `xml:"arch,attr"`
Machine string `xml:"machine,attr"`
Value string `xml:",chardata"`
}
type domainFirmwareXML struct {
Features []domainFirmwareFeatureXML `xml:"feature"`
}
type domainFirmwareFeatureXML struct {
Enabled string `xml:"enabled,attr"`
Name string `xml:"name,attr"`
}
type domainFeaturesXML struct {
ACPI *struct{} `xml:"acpi"`
APIC *struct{} `xml:"apic"`
}
type domainCPUXML struct {
Mode string `xml:"mode,attr"`
}
type domainDevicesXML struct {
Disks []domainDiskXML `xml:"disk"`
Interfaces []domainInterfaceXML `xml:"interface"`
Serials []domainSerialXML `xml:"serial"`
Consoles []domainConsoleXML `xml:"console"`
Channels []domainChannelXML `xml:"channel"`
RNGs []domainRNGXML `xml:"rng"`
}
type domainDiskXML struct {
Type string `xml:"type,attr"`
Device string `xml:"device,attr"`
Driver domainDiskDriverXML `xml:"driver"`
Source domainDiskSourceXML `xml:"source"`
Target domainDiskTargetXML `xml:"target"`
ReadOnly *struct{} `xml:"readonly"`
}
type domainDiskDriverXML struct {
Name string `xml:"name,attr"`
Type string `xml:"type,attr"`
}
type domainDiskSourceXML struct {
File string `xml:"file,attr"`
}
type domainDiskTargetXML struct {
Dev string `xml:"dev,attr"`
Bus string `xml:"bus,attr"`
}
type domainInterfaceXML struct {
Type string `xml:"type,attr"`
Source domainInterfaceSourceXML `xml:"source"`
Model domainModelXML `xml:"model"`
}
type domainInterfaceSourceXML struct {
Bridge string `xml:"bridge,attr"`
}
type domainModelXML struct {
Type string `xml:"type,attr"`
}
type domainSerialXML struct {
Type string `xml:"type,attr"`
Target domainPortXML `xml:"target"`
}
type domainPortXML struct {
Port int `xml:"port,attr"`
}
type domainConsoleXML struct {
Type string `xml:"type,attr"`
Target domainConsoleTargetXML `xml:"target"`
}
type domainConsoleTargetXML struct {
Type string `xml:"type,attr"`
Port int `xml:"port,attr"`
}
type domainChannelXML struct {
Type string `xml:"type,attr"`
Target domainChannelTargetXML `xml:"target"`
}
type domainChannelTargetXML struct {
Type string `xml:"type,attr"`
Name string `xml:"name,attr"`
}
type domainRNGXML struct {
Model string `xml:"model,attr"`
Backend domainRNGBackendXML `xml:"backend"`
}
type domainRNGBackendXML struct {
Model string `xml:"model,attr"`
Value string `xml:",chardata"`
}
// buildDomainXML renders the XML for DomainDefineXML. Both disks are
// type="file" with <source file>: AppArmor derives QEMU's profile from those
// paths, and type="volume" fails with permission denied on stock
// Debian/Ubuntu hosts. UEFI boots with secure boot off; libvirt picks the
// OVMF binary itself.
func buildDomainXML(p domainParams) (string, error) {
if p.Name == "" {
return "", permanentf("domain XML: name is empty")
}
if p.DiskPath == "" {
return "", permanentf("domain XML for %q: disk path is empty", p.Name)
}
if p.SeedPath == "" {
return "", permanentf("domain XML for %q: seed path is empty", p.Name)
}
if p.DiskFormat == "" {
p.DiskFormat = provider.KVMFormatQcow2
}
if p.DiskFormat != provider.KVMFormatQcow2 && p.DiskFormat != provider.KVMFormatRaw {
return "", permanentf("domain XML for %q: unknown disk format %q", p.Name, p.DiskFormat)
}
osx := domainOSXML{
Type: domainOSTypeXML{Arch: domainArch, Machine: domainMachine, Value: "hvm"},
}
switch p.Firmware {
case provider.KVMFirmwareUEFI:
osx.Firmware = "efi"
osx.FirmwareFeatures = &domainFirmwareXML{
Features: []domainFirmwareFeatureXML{{Enabled: "no", Name: "secure-boot"}},
}
case provider.KVMFirmwareBIOS:
default:
return "", permanentf("domain XML for %q: unknown firmware %q (want %s or %s)", p.Name, p.Firmware, provider.KVMFirmwareUEFI, provider.KVMFirmwareBIOS)
}
d := domainXML{
Type: "kvm",
Name: p.Name,
Memory: domainMemoryXML{Unit: "MiB", Value: p.MemoryMiB},
VCPU: p.VCPUs,
Metadata: domainMetadataXML{Instance: newInstanceMetadata(p.UID, p.CreatedAt)},
OS: osx,
Features: domainFeaturesXML{ACPI: &struct{}{}, APIC: &struct{}{}},
CPU: domainCPUXML{Mode: "host-passthrough"},
OnPoweroff: "destroy",
OnReboot: "restart",
OnCrash: "destroy",
Devices: domainDevicesXML{
Disks: []domainDiskXML{
{
Type: "file",
Device: "disk",
Driver: domainDiskDriverXML{Name: "qemu", Type: p.DiskFormat},
Source: domainDiskSourceXML{File: p.DiskPath},
Target: domainDiskTargetXML{Dev: "vda", Bus: "virtio"},
},
{
Type: "file",
Device: "cdrom",
Driver: domainDiskDriverXML{Name: "qemu", Type: provider.KVMFormatRaw},
Source: domainDiskSourceXML{File: p.SeedPath},
Target: domainDiskTargetXML{Dev: "sda", Bus: "sata"},
ReadOnly: &struct{}{},
},
},
Interfaces: []domainInterfaceXML{{
Type: "bridge",
Source: domainInterfaceSourceXML{Bridge: p.Bridge},
Model: domainModelXML{Type: "virtio"},
}},
Serials: []domainSerialXML{{Type: "pty", Target: domainPortXML{Port: 0}}},
Consoles: []domainConsoleXML{{Type: "pty", Target: domainConsoleTargetXML{Type: "serial", Port: 0}}},
Channels: []domainChannelXML{{
Type: "unix",
Target: domainChannelTargetXML{Type: "virtio", Name: guestAgentChannelName},
}},
RNGs: []domainRNGXML{{Model: "virtio", Backend: domainRNGBackendXML{Model: "random", Value: "/dev/urandom"}}},
},
}
out, err := xml.MarshalIndent(d, "", " ")
if err != nil {
return "", permanentf("domain XML for %q: %v", p.Name, err)
}
return string(out), nil
}