server,kvm: detect boot options for vm import (#11218)

* server,kvm: detect boot options for vm import

Fixes #11184

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* tests and changes

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

---------

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2025-09-08 20:21:57 +05:30 committed by GitHub
parent 6868f052de
commit 9349b69b7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 132 additions and 2 deletions

View File

@ -48,6 +48,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogAction;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogModel;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestDef;
public class LibvirtDomainXMLParser {
protected Logger logger = LogManager.getLogger(getClass());
@ -63,6 +64,8 @@ public class LibvirtDomainXMLParser {
private LibvirtVMDef.CpuTuneDef cpuTuneDef;
private LibvirtVMDef.CpuModeDef cpuModeDef;
private String name;
private GuestDef.BootType bootType;
private GuestDef.BootMode bootMode;
public boolean parseDomainXML(String domXML) {
DocumentBuilder builder;
@ -388,6 +391,7 @@ public class LibvirtDomainXMLParser {
}
extractCpuTuneDef(rootElement);
extractCpuModeDef(rootElement);
extractBootDef(rootElement);
return true;
} catch (ParserConfigurationException e) {
logger.debug(e.toString());
@ -516,6 +520,14 @@ public class LibvirtDomainXMLParser {
return cpuModeDef;
}
public GuestDef.BootType getBootType() {
return bootType;
}
public GuestDef.BootMode getBootMode() {
return bootMode;
}
private void extractCpuTuneDef(final Element rootElement) {
NodeList cpuTunesList = rootElement.getElementsByTagName("cputune");
if (cpuTunesList.getLength() > 0) {
@ -569,4 +581,26 @@ public class LibvirtDomainXMLParser {
}
}
}
protected void extractBootDef(final Element rootElement) {
bootType = GuestDef.BootType.BIOS;
bootMode = GuestDef.BootMode.LEGACY;
Element osElement = (Element) rootElement.getElementsByTagName("os").item(0);
if (osElement == null) {
return;
}
NodeList loaderList = osElement.getElementsByTagName("loader");
if (loaderList.getLength() == 0) {
return;
}
Element loader = (Element) loaderList.item(0);
String type = loader.getAttribute("type");
String secure = loader.getAttribute("secure");
if ("pflash".equalsIgnoreCase(type) || loader.getTextContent().toLowerCase().contains("uefi")) {
bootType = GuestDef.BootType.UEFI;
}
if ("yes".equalsIgnoreCase(secure)) {
bootMode = GuestDef.BootMode.SECURE;
}
}
}

View File

@ -65,7 +65,7 @@ public class LibvirtVMDef {
}
}
enum BootType {
public enum BootType {
UEFI("UEFI"), BIOS("BIOS");
String _type;
@ -80,7 +80,7 @@ public class LibvirtVMDef {
}
}
enum BootMode {
public enum BootMode {
LEGACY("LEGACY"), SECURE("SECURE");
String _mode;

View File

@ -135,6 +135,12 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra
instance.setNics(getUnmanagedInstanceNics(parser.getInterfaces()));
instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource, conn, domain.getName()));
instance.setVncPassword(getFormattedVncPassword(parser.getVncPasswd()));
if (parser.getBootType() != null) {
instance.setBootType(parser.getBootType().toString());
}
if (parser.getBootMode() != null) {
instance.setBootMode(parser.getBootMode().toString());
}
return instance;
} catch (Exception e) {

View File

@ -20,8 +20,13 @@
package com.cloud.hypervisor.kvm.resource;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
@ -31,10 +36,15 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
import junit.framework.TestCase;
import org.apache.cloudstack.utils.qemu.QemuObject;
import org.apache.cloudstack.utils.security.ParserUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@RunWith(MockitoJUnitRunner.class)
public class LibvirtDomainXMLParserTest extends TestCase {
@ -386,4 +396,84 @@ public class LibvirtDomainXMLParserTest extends TestCase {
Assert.assertEquals("CPU cores count is parsed", 4, libvirtDomainXMLParser.getCpuModeDef().getCoresPerSocket());
Assert.assertEquals("CPU threads count is parsed", 2, libvirtDomainXMLParser.getCpuModeDef().getThreadsPerCore());
}
private LibvirtDomainXMLParser parseElementFromXML(String xml) {
LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
DocumentBuilder builder;
try {
builder = ParserUtils.getSaferDocumentBuilderFactory().newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xml));
Document doc = builder.parse(is);
Element element = doc.getDocumentElement();
parser.extractBootDef(element);
} catch (ParserConfigurationException | IOException | SAXException e) {
Assert.fail("Failed to parse XML: " + e.getMessage());
}
return parser;
}
@Test
public void extractBootDefParsesUEFISecureBootCorrectly() {
String xml = "<domain type='kvm'>" +
"<os>" +
"<loader type='pflash' secure='yes'>/path/to/uefi/loader</loader>" +
"</os>" +
"</domain>";
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
assertEquals(LibvirtVMDef.GuestDef.BootType.UEFI, parser.getBootType());
assertEquals(LibvirtVMDef.GuestDef.BootMode.SECURE, parser.getBootMode());
}
@Test
public void extractBootDefParsesUEFILegacyBootCorrectly() {
String xml = "<domain type='kvm'>" +
"<os>" +
"<loader type='pflash' secure='no'>/path/to/uefi/loader</loader>" +
"</os>" +
"</domain>";
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
assertEquals(LibvirtVMDef.GuestDef.BootType.UEFI, parser.getBootType());
assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode());
}
@Test
public void extractBootDefDefaultsToBIOSLegacyWhenNoLoaderPresent() {
String xml = "<domain type='kvm'>" +
"<os>" +
"<type arch='x86_64'>hvm</type>" +
"</os>" +
"</domain>";
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType());
assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode());
}
@Test
public void extractBootDefHandlesEmptyOSSection() {
String xml = "<domain type='kvm'>" +
"<os></os>" +
"</domain>";
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType());
assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode());
}
@Test
public void extractBootDefHandlesMissingOSSection() {
String xml = "<domain type='kvm'></domain>";
LibvirtDomainXMLParser parser = parseElementFromXML(xml);
assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType());
assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode());
}
}