257 lines
7.4 KiB
Go
257 lines
7.4 KiB
Go
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
|
|
}
|