Skip to content

Commit bd35fb6

Browse files
KVM: default x86 guests to vga video model when none is configured (#13807)
Without an explicit video model (vm.video.hardware agent property or video.hardware VM detail), CloudStack emits no <video> element and libvirt defaults x86 guests to cirrus, which is deprecated in QEMU and renders a blank console on Windows Server 2025 Core guests. Default unconfigured x86 guests to vga with 32 MiB of VRAM instead. Explicit VM details and agent properties keep precedence; non-x86 guests (aarch64, s390x) are unchanged. Fixes #13806
1 parent d87c804 commit bd35fb6

2 files changed

Lines changed: 72 additions & 0 deletions

File tree

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
318318
private static final String KVMCLOCK = "kvmclock";
319319
private static final String HYPERVCLOCK = "hypervclock";
320320
private static final String WINDOWS = "Windows";
321+
private static final String X86_DEFAULT_VIDEO_MODEL = "vga";
322+
private static final int X86_DEFAULT_VIDEO_RAM_KIB = 32768;
321323
private static final String Q35 = "q35";
322324
private static final String PTY = "pty";
323325
private static final String VNC = "vnc";
@@ -3330,6 +3332,14 @@ protected VideoDef createVideoDef(VirtualMachineTO vmTO) {
33303332
videoRam = NumbersUtil.parseInt(value, videoRam);
33313333
}
33323334
}
3335+
if (StringUtils.isBlank(videoHw) && isGuestX86(vmTO)) {
3336+
// With no <video> element libvirt defaults x86 guests to cirrus, which is deprecated
3337+
// in QEMU and renders a blank console on recent Windows guests (e.g. Windows Server 2025 Core)
3338+
videoHw = X86_DEFAULT_VIDEO_MODEL;
3339+
if (videoRam == 0) {
3340+
videoRam = X86_DEFAULT_VIDEO_RAM_KIB;
3341+
}
3342+
}
33333343
return new VideoDef(videoHw, videoRam);
33343344
}
33353345

@@ -3482,6 +3492,11 @@ public boolean isGuestAarch64() {
34823492
return AARCH64.equals(guestCpuArch);
34833493
}
34843494

3495+
protected boolean isGuestX86(VirtualMachineTO vmTO) {
3496+
String arch = guestCpuArch != null ? guestCpuArch : vmTO.getArch();
3497+
return arch == null || arch.equals("x86_64") || arch.equals("i686");
3498+
}
3499+
34853500
private boolean isGuestS390x() {
34863501
return S390X.equals(guestCpuArch);
34873502
}

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,63 @@ public void testCreateVideoDef() {
713713
assertXpath(domainDoc, "/video/model/@vram", "200");
714714
}
715715

716+
@Test
717+
public void testCreateVideoDefX86DefaultsToVga() {
718+
VirtualMachineTO to = createDefaultVM(false);
719+
720+
VideoDef videoDef = libvirtComputingResourceSpy.createVideoDef(to);
721+
Document domainDoc = parse(videoDef.toString());
722+
assertXpath(domainDoc, "/video/model/@type", "vga");
723+
assertXpath(domainDoc, "/video/model/@vram", "32768");
724+
}
725+
726+
@Test
727+
public void testCreateVideoDefWindowsDefaultsToVga() {
728+
VirtualMachineTO to = createDefaultVM(false);
729+
to.setOs("Windows Server 2025 (64-bit)");
730+
731+
VideoDef videoDef = libvirtComputingResourceSpy.createVideoDef(to);
732+
Document domainDoc = parse(videoDef.toString());
733+
assertXpath(domainDoc, "/video/model/@type", "vga");
734+
assertXpath(domainDoc, "/video/model/@vram", "32768");
735+
}
736+
737+
@Test
738+
public void testCreateVideoDefExplicitDetailWins() {
739+
VirtualMachineTO to = createDefaultVM(false);
740+
Map<String, String> details = new HashMap<>();
741+
details.put(VmDetailConstants.VIDEO_HARDWARE, "virtio");
742+
details.put(VmDetailConstants.VIDEO_RAM, "65536");
743+
to.setDetails(details);
744+
745+
VideoDef videoDef = libvirtComputingResourceSpy.createVideoDef(to);
746+
Document domainDoc = parse(videoDef.toString());
747+
assertXpath(domainDoc, "/video/model/@type", "virtio");
748+
assertXpath(domainDoc, "/video/model/@vram", "65536");
749+
}
750+
751+
@Test
752+
public void testCreateVideoDefAgentPropertyWins() {
753+
VirtualMachineTO to = createDefaultVM(false);
754+
libvirtComputingResourceSpy.videoHw = "qxl";
755+
libvirtComputingResourceSpy.videoRam = 65536;
756+
757+
VideoDef videoDef = libvirtComputingResourceSpy.createVideoDef(to);
758+
Document domainDoc = parse(videoDef.toString());
759+
assertXpath(domainDoc, "/video/model/@type", "qxl");
760+
assertXpath(domainDoc, "/video/model/@vram", "65536");
761+
}
762+
763+
@Test
764+
public void testCreateVideoDefAarch64UnconfiguredStaysEmpty() {
765+
VirtualMachineTO to = createDefaultVM(false);
766+
to.setArch("aarch64");
767+
libvirtComputingResourceSpy.guestCpuArch = "aarch64";
768+
769+
VideoDef videoDef = libvirtComputingResourceSpy.createVideoDef(to);
770+
assertEquals("", videoDef.toString());
771+
}
772+
716773
@Test
717774
public void testCreateRngDef() {
718775
VirtualMachineTO to = createDefaultVM(false);

0 commit comments

Comments
 (0)