Skip to content

Commit 53a7f2e

Browse files
committed
baremetal: use q35 machine type with EFI firmware for bootstrap VM
The bootstrap VM domain definition did not set a machine type, causing libvirt to default to the deprecated pc-i440fx-rhel7.6.0 chipset on CentOS Stream 9 hosts. This has been correlated with intermittent bootstrap VM startup failures in CI where the VM becomes unreachable after QEMU starts. Explicitly set the machine type to q35 with EFI firmware for x86_64, matching the pattern already used for aarch64 and aligning with how dev-scripts provisions master/worker VMs. The arch-specific domain configuration is extracted into a separate configureDomainArch() function with unit tests. Assisted-By: Claude 4.6 Opus High
1 parent 4100a5b commit 53a7f2e

2 files changed

Lines changed: 116 additions & 11 deletions

File tree

pkg/infrastructure/baremetal/bootstrap.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -290,28 +290,36 @@ func getHostCapabilities(virConn *libvirt.Libvirt) (libvirtxml.Caps, error) {
290290
return caps, nil
291291
}
292292

293-
func createBootstrapDomain(virConn *libvirt.Libvirt, config baremetalConfig, pool libvirt.StoragePool, volume libvirt.StorageVol) error {
294-
bootstrapDom := newDomain(fmt.Sprintf("%s-bootstrap", config.ClusterID))
293+
func configureDomainArch(dom *libvirtxml.Domain, arch string) {
294+
dom.OS.Type.Arch = arch
295295

296-
capabilities, err := getHostCapabilities(virConn)
297-
if err != nil {
298-
return fmt.Errorf("failed to get libvirt capabilities: %w", err)
296+
if arch == "x86_64" {
297+
dom.OS.Type.Machine = "q35"
298+
dom.OS.Firmware = "efi"
299299
}
300300

301-
arch := capabilities.Host.CPU.Arch
302-
bootstrapDom.OS.Type.Arch = arch
303-
304301
if arch == "aarch64" {
305-
// for aarch64 speciffying this will automatically select the firmware and NVRAM file
306302
// reference: https://libvirt.org/formatdomain.html#bios-bootloader
307-
bootstrapDom.OS.Firmware = "efi"
303+
dom.OS.Firmware = "efi"
308304
}
309305

310306
// For aarch64, s390x, ppc64 and ppc64le spice is not supported
311307
if arch == "aarch64" || arch == "s390x" || strings.HasPrefix(arch, "ppc64") {
312-
bootstrapDom.Devices.Graphics = nil
308+
dom.Devices.Graphics = nil
309+
}
310+
}
311+
312+
func createBootstrapDomain(virConn *libvirt.Libvirt, config baremetalConfig, pool libvirt.StoragePool, volume libvirt.StorageVol) error {
313+
bootstrapDom := newDomain(fmt.Sprintf("%s-bootstrap", config.ClusterID))
314+
315+
capabilities, err := getHostCapabilities(virConn)
316+
if err != nil {
317+
return fmt.Errorf("failed to get libvirt capabilities: %w", err)
313318
}
314319

320+
arch := capabilities.Host.CPU.Arch
321+
configureDomainArch(&bootstrapDom, arch)
322+
315323
for _, bridge := range config.Bridges {
316324
netIface := libvirtxml.DomainInterface{
317325
Model: &libvirtxml.DomainInterfaceModel{
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package baremetal
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"libvirt.org/go/libvirtxml"
8+
)
9+
10+
func TestNewDomain(t *testing.T) {
11+
dom := newDomain("test-bootstrap")
12+
13+
assert.Equal(t, "test-bootstrap", dom.Name)
14+
assert.Equal(t, "kvm", dom.Type)
15+
assert.Equal(t, "hvm", dom.OS.Type.Type)
16+
assert.Empty(t, dom.OS.Type.Arch, "arch should not be set by newDomain")
17+
assert.Empty(t, dom.OS.Type.Machine, "machine should not be set by newDomain")
18+
assert.Equal(t, uint(6144), dom.Memory.Value)
19+
assert.Equal(t, "MiB", dom.Memory.Unit)
20+
assert.Equal(t, uint(4), dom.VCPU.Value)
21+
assert.Equal(t, "host-passthrough", dom.CPU.Mode)
22+
assert.NotEmpty(t, dom.Devices.RNGs, "RNG device should be configured")
23+
assert.NotEmpty(t, dom.Devices.Graphics, "graphics should be configured")
24+
assert.NotEmpty(t, dom.Devices.Consoles, "console should be configured")
25+
}
26+
27+
func TestConfigureDomainArch(t *testing.T) {
28+
tests := []struct {
29+
name string
30+
arch string
31+
expectMachine string
32+
expectFirmware string
33+
expectGraphics bool
34+
}{
35+
{
36+
name: "x86_64 sets q35 and efi",
37+
arch: "x86_64",
38+
expectMachine: "q35",
39+
expectFirmware: "efi",
40+
expectGraphics: true,
41+
},
42+
{
43+
name: "aarch64 sets efi and removes graphics",
44+
arch: "aarch64",
45+
expectMachine: "",
46+
expectFirmware: "efi",
47+
expectGraphics: false,
48+
},
49+
{
50+
name: "s390x removes graphics",
51+
arch: "s390x",
52+
expectMachine: "",
53+
expectFirmware: "",
54+
expectGraphics: false,
55+
},
56+
{
57+
name: "ppc64le removes graphics",
58+
arch: "ppc64le",
59+
expectMachine: "",
60+
expectFirmware: "",
61+
expectGraphics: false,
62+
},
63+
}
64+
65+
for _, tc := range tests {
66+
t.Run(tc.name, func(t *testing.T) {
67+
dom := newDomain("test-bootstrap")
68+
configureDomainArch(&dom, tc.arch)
69+
70+
assert.Equal(t, tc.arch, dom.OS.Type.Arch)
71+
assert.Equal(t, tc.expectMachine, dom.OS.Type.Machine)
72+
assert.Equal(t, tc.expectFirmware, dom.OS.Firmware)
73+
74+
if tc.expectGraphics {
75+
assert.NotNil(t, dom.Devices.Graphics, "graphics should be present for %s", tc.arch)
76+
} else {
77+
assert.Nil(t, dom.Devices.Graphics, "graphics should be removed for %s", tc.arch)
78+
}
79+
})
80+
}
81+
}
82+
83+
func TestConfigureDomainArchPreservesOtherFields(t *testing.T) {
84+
dom := newDomain("test-bootstrap")
85+
86+
dom.Devices.Interfaces = []libvirtxml.DomainInterface{
87+
{
88+
Model: &libvirtxml.DomainInterfaceModel{Type: "virtio"},
89+
},
90+
}
91+
92+
configureDomainArch(&dom, "x86_64")
93+
94+
assert.Len(t, dom.Devices.Interfaces, 1, "interfaces should not be modified")
95+
assert.Equal(t, "hvm", dom.OS.Type.Type, "OS type should remain hvm")
96+
assert.Equal(t, "kvm", dom.Type, "domain type should remain kvm")
97+
}

0 commit comments

Comments
 (0)