// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.hypervisor.kvm.resource;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.cloudstack.utils.security.ParserUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.cloudstack.utils.qemu.QemuObject;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.NicModel;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MemBalloonDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
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());
    private final List<InterfaceDef> interfaces = new ArrayList<InterfaceDef>();
    private MemBalloonDef memBalloonDef = new MemBalloonDef();
    private final List<DiskDef> diskDefs = new ArrayList<DiskDef>();
    private final List<RngDef> rngDefs = new ArrayList<RngDef>();
    private final List<ChannelDef> channels = new ArrayList<ChannelDef>();
    private final List<WatchDogDef> watchDogDefs = new ArrayList<WatchDogDef>();
    private Integer vncPort;
    private  String vncPasswd;
    private String desc;
    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;
        try {
            builder = ParserUtils.getSaferDocumentBuilderFactory().newDocumentBuilder();

            InputSource is = new InputSource();
            is.setCharacterStream(new StringReader(domXML));
            Document doc = builder.parse(is);

            Element rootElement = doc.getDocumentElement();

            desc = getTagValue("description", rootElement);
            name = getTagValue("name", rootElement);

            Element devices = (Element)rootElement.getElementsByTagName("devices").item(0);
            NodeList disks = devices.getElementsByTagName("disk");
            for (int i = 0; i < disks.getLength(); i++) {
                Element disk = (Element)disks.item(i);
                String type = disk.getAttribute("type");
                DiskDef def = new DiskDef();
                if (type.equalsIgnoreCase("network")) {
                    String diskFmtType = getAttrValue("driver", "type", disk);
                    String diskCacheMode = getAttrValue("driver", "cache", disk);
                    String diskPath = getAttrValue("source", "name", disk);
                    String protocol = getAttrValue("source", "protocol", disk);
                    String authUserName = getAttrValue("auth", "username", disk);
                    String poolUuid = getAttrValue("secret", "uuid", disk);
                    String host = LibvirtStoragePoolXMLParser.getStorageHosts(disk);
                    int port = 0;
                    String xmlPort = getAttrValue("host", "port", disk);
                    if (StringUtils.isNotBlank(xmlPort)) {
                        port = Integer.parseInt(xmlPort);
                    }
                    String diskLabel = getAttrValue("target", "dev", disk);
                    String bus = getAttrValue("target", "bus", disk);

                    DiskDef.DiskFmtType fmt = null;
                    if (diskFmtType != null) {
                        fmt = DiskDef.DiskFmtType.valueOf(diskFmtType.toUpperCase());
                    }

                    def.defNetworkBasedDisk(diskPath, host, port, authUserName, poolUuid, diskLabel,
                        DiskDef.DiskBus.valueOf(bus.toUpperCase()),
                        DiskDef.DiskProtocol.valueOf(protocol.toUpperCase()), fmt);
                    def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
                } else {
                    String diskFmtType = getAttrValue("driver", "type", disk);
                    String diskCacheMode = getAttrValue("driver", "cache", disk);
                    String diskFile = getAttrValue("source", "file", disk);
                    String diskDev = getAttrValue("source", "dev", disk);

                    String diskLabel = getAttrValue("target", "dev", disk);
                    String bus = getAttrValue("target", "bus", disk);
                    String device = disk.getAttribute("device");

                    if (type.equalsIgnoreCase("file")) {
                        if (device.equalsIgnoreCase("disk")) {
                            DiskDef.DiskFmtType fmt = null;
                            if (diskFmtType != null) {
                                fmt = DiskDef.DiskFmtType.valueOf(diskFmtType.toUpperCase());
                            }
                            def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt);
                        } else if (device.equalsIgnoreCase("cdrom")) {
                            def.defISODisk(diskFile, i+1, diskLabel, DiskDef.DiskType.FILE);
                        }
                    } else if (type.equalsIgnoreCase("block")) {
                        parseDiskBlock(def, device, diskDev, diskLabel, bus, diskFile, i);
                    }
                    if (StringUtils.isNotBlank(diskCacheMode)) {
                        def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
                    }
                }

                NodeList iotune = disk.getElementsByTagName("iotune");
                if ((iotune != null) && (iotune.getLength() != 0)) {
                    String bytesReadRateStr = getTagValue("read_bytes_sec", (Element)iotune.item(0));
                    if (bytesReadRateStr != null) {
                        Long bytesReadRate = Long.parseLong(bytesReadRateStr);
                        def.setBytesReadRate(bytesReadRate);
                    }
                    String bytesReadRateMaxStr = getTagValue("read_bytes_sec_max", (Element)iotune.item(0));
                    if (bytesReadRateMaxStr != null) {
                        Long bytesReadRateMax = Long.parseLong(bytesReadRateMaxStr);
                        def.setBytesReadRateMax(bytesReadRateMax);
                    }
                    String bytesReadRateMaxLengthStr = getTagValue("read_bytes_sec_max_length", (Element)iotune.item(0));
                    if (bytesReadRateMaxLengthStr != null) {
                        Long bytesReadRateMaxLength = Long.parseLong(bytesReadRateMaxLengthStr);
                        def.setBytesReadRateMaxLength(bytesReadRateMaxLength);
                    }
                    String bytesWriteRateStr = getTagValue("write_bytes_sec", (Element)iotune.item(0));
                    if (bytesWriteRateStr != null) {
                        Long bytesWriteRate = Long.parseLong(bytesWriteRateStr);
                        def.setBytesWriteRate(bytesWriteRate);
                    }
                    String bytesWriteRateMaxStr = getTagValue("write_bytes_sec_max", (Element)iotune.item(0));
                    if (bytesWriteRateMaxStr != null) {
                        Long bytesWriteRateMax = Long.parseLong(bytesWriteRateMaxStr);
                        def.setBytesWriteRateMax(bytesWriteRateMax);
                    }
                    String bytesWriteRateMaxLengthStr = getTagValue("write_bytes_sec_max_length", (Element)iotune.item(0));
                    if (bytesWriteRateMaxLengthStr != null) {
                        Long bytesWriteRateMaxLength = Long.parseLong(bytesWriteRateMaxLengthStr);
                        def.setBytesWriteRateMaxLength(bytesWriteRateMaxLength);
                    }
                    String iopsReadRateStr = getTagValue("read_iops_sec", (Element)iotune.item(0));
                    if (iopsReadRateStr != null) {
                        Long iopsReadRate = Long.parseLong(iopsReadRateStr);
                        def.setIopsReadRate(iopsReadRate);
                    }
                    String iopsReadRateMaxStr = getTagValue("read_iops_sec_max", (Element)iotune.item(0));
                    if (iopsReadRateMaxStr != null) {
                        Long iopsReadRateMax = Long.parseLong(iopsReadRateMaxStr);
                        def.setIopsReadRateMax(iopsReadRateMax);
                    }
                    String iopsReadRateMaxLengthStr = getTagValue("read_iops_sec_max_length", (Element)iotune.item(0));
                    if (iopsReadRateMaxLengthStr != null) {
                        Long iopsReadRateMaxLength = Long.parseLong(iopsReadRateMaxLengthStr);
                        def.setIopsReadRateMaxLength(iopsReadRateMaxLength);
                    }
                    String iopsWriteRateStr = getTagValue("write_iops_sec", (Element)iotune.item(0));
                    if (iopsWriteRateStr != null) {
                        Long iopsWriteRate = Long.parseLong(iopsWriteRateStr);
                        def.setIopsWriteRate(iopsWriteRate);
                    }
                    String iopsWriteRateMaxStr = getTagValue("write_iops_sec_max", (Element)iotune.item(0));
                    if (iopsWriteRateMaxStr != null) {
                        Long iopsWriteRateMax = Long.parseLong(iopsWriteRateMaxStr);
                        def.setIopsWriteRateMax(iopsWriteRateMax);
                    }
                    String iopsWriteRateMaxLengthStr = getTagValue("write_iops_sec_max_length", (Element)iotune.item(0));
                    if (iopsWriteRateMaxLengthStr != null) {
                        Long iopsWriteRateMaxLength = Long.parseLong(iopsWriteRateMaxLengthStr);
                        def.setIopsWriteRateMaxLength(iopsWriteRateMaxLength);
                    }
                }

                NodeList encryption = disk.getElementsByTagName("encryption");
                if (encryption.getLength() != 0) {
                    Element encryptionElement = (Element) encryption.item(0);
                    String passphraseUuid = getAttrValue("secret", "uuid", encryptionElement);
                    QemuObject.EncryptFormat encryptFormat = QemuObject.EncryptFormat.enumValue(encryptionElement.getAttribute("format"));
                    DiskDef.LibvirtDiskEncryptDetails encryptDetails = new DiskDef.LibvirtDiskEncryptDetails(passphraseUuid, encryptFormat);
                    def.setLibvirtDiskEncryptDetails(encryptDetails);
                }

                diskDefs.add(def);
            }

            memBalloonDef = parseMemBalloonTag(devices);

            NodeList nics = devices.getElementsByTagName("interface");
            for (int i = 0; i < nics.getLength(); i++) {
                Element nic = (Element)nics.item(i);

                String type = nic.getAttribute("type");
                String mac = getAttrValue("mac", "address", nic);
                String dev = getAttrValue("target", "dev", nic);
                String model = getAttrValue("model", "type", nic);
                String slot = StringUtils.removeStart(getAttrValue("address", "slot", nic), "0x");

                InterfaceDef def = new InterfaceDef();
                NodeList bandwidth = nic.getElementsByTagName("bandwidth");
                Integer networkRateKBps = 0;
                if ((bandwidth != null) && (bandwidth.getLength() != 0)) {
                    Integer inbound = Integer.valueOf(getAttrValue("inbound", "average", (Element)bandwidth.item(0)));
                    Integer outbound = Integer.valueOf(getAttrValue("outbound", "average", (Element)bandwidth.item(0)));
                    if (inbound.equals(outbound)) {
                        networkRateKBps = inbound;
                    }
                }
                if (type.equalsIgnoreCase("network")) {
                    String network = getAttrValue("source", "network", nic);
                    def.defPrivateNet(network, dev, mac, NicModel.valueOf(model.toUpperCase()), networkRateKBps);
                } else if (type.equalsIgnoreCase("bridge")) {
                    String bridge = getAttrValue("source", "bridge", nic);
                    def.defBridgeNet(bridge, dev, mac, NicModel.valueOf(model.toUpperCase()), networkRateKBps);
                } else if (type.equalsIgnoreCase("ethernet")) {
                    String scriptPath = getAttrValue("script", "path", nic);
                    def.defEthernet(dev, mac, NicModel.valueOf(model.toUpperCase()), scriptPath, networkRateKBps);
                } else if (type.equals("vhostuser")) {
                    String sourcePort = getAttrValue("source", "path", nic);
                    String mode = getAttrValue("source", "mode", nic);
                    int lastSlashIndex = sourcePort.lastIndexOf("/");
                    String ovsPath = sourcePort.substring(0,lastSlashIndex);
                    String port = sourcePort.substring(lastSlashIndex + 1);
                    def.setDpdkSourcePort(port);
                    def.setDpdkOvsPath(ovsPath);
                    def.setInterfaceMode(mode);
                }
                String multiQueueNumber = getAttrValue("driver", "queues", nic);
                if (StringUtils.isNotBlank(multiQueueNumber)) {
                    def.setMultiQueueNumber(Integer.valueOf(multiQueueNumber));
                }

                String packedOn = getAttrValue("driver", "packed", nic);
                if (StringUtils.isNotBlank(packedOn)) {
                    def.setPackedVirtQueues("on".equalsIgnoreCase(packedOn));
                }

                if (StringUtils.isNotBlank(slot)) {
                    def.setSlot(Integer.parseInt(slot, 16));
                }

                interfaces.add(def);
            }

            NodeList ports = devices.getElementsByTagName("channel");
            for (int i = 0; i < ports.getLength(); i++) {
                Element channel = (Element)ports.item(i);

                String type = channel.getAttribute("type");
                String path = getAttrValue("source", "path", channel);
                String name = getAttrValue("target", "name", channel);
                String state = getAttrValue("target", "state", channel);

                if (ChannelDef.ChannelType.valueOf(type.toUpperCase()).equals(ChannelDef.ChannelType.SPICEVMC)) {
                    continue;
                }

                if (path == null) {
                    path = "";
                }

                ChannelDef def = null;
                if (StringUtils.isBlank(state)) {
                    def = new ChannelDef(name, ChannelDef.ChannelType.valueOf(type.toUpperCase()), new File(path));
                } else {
                    def = new ChannelDef(name, ChannelDef.ChannelType.valueOf(type.toUpperCase()),
                            ChannelDef.ChannelState.valueOf(state.toUpperCase()), new File(path));
                }

                channels.add(def);
            }

            Element graphic = (Element)devices.getElementsByTagName("graphics").item(0);

            if (graphic != null) {
                String port = graphic.getAttribute("port");
                if (port != null) {
                    try {
                        vncPort = Integer.parseInt(port);
                        if (vncPort != -1) {
                            vncPort = vncPort - 5900;
                        } else {
                            vncPort = null;
                        }
                    } catch (NumberFormatException nfe) {
                        vncPort = null;
                    }
                }

                String passwd = graphic.getAttribute("passwd");
                if (passwd != null) {
                    vncPasswd = passwd;
                }

            }

            NodeList rngs = devices.getElementsByTagName("rng");
            for (int i = 0; i < rngs.getLength(); i++) {
                RngDef def = null;
                Element rng = (Element)rngs.item(i);
                String backendModel = getAttrValue("backend", "model", rng);
                String path = getTagValue("backend", rng);
                String bytes = getAttrValue("rate", "bytes", rng);
                String period = getAttrValue("rate", "period", rng);
                if (StringUtils.isAnyEmpty(bytes, period)) {
                    logger.debug(String.format("Bytes and period in the rng section should not be null, please check the VM %s", name));
                }

                if (bytes == null) {
                    bytes = "0";
                }

                if (period == null) {
                    period = "0";
                }

                if (bytes == null) {
                    bytes = "0";
                }

                if (period == null) {
                    period = "0";
                }

                if (StringUtils.isEmpty(backendModel)) {
                    def = new RngDef(path, Integer.parseInt(bytes), Integer.parseInt(period));
                } else {
                    if (StringUtils.isEmpty(backendModel)) {
                        def = new RngDef(path, Integer.parseInt(bytes), Integer.parseInt(period));
                    } else {
                        def = new RngDef(path, RngBackendModel.valueOf(backendModel.toUpperCase()),
                                Integer.parseInt(bytes), Integer.parseInt(period));
                    }
                }
                if (def != null) {
                    rngDefs.add(def);
                }
            }

            NodeList watchDogs = devices.getElementsByTagName("watchdog");
            for (int i = 0; i < watchDogs.getLength(); i++) {
                WatchDogDef def = null;
                Element watchDog = (Element)watchDogs.item(i);
                String action = watchDog.getAttribute("action");
                String model = watchDog.getAttribute("model");

                if (StringUtils.isEmpty(model)) {
                   continue;
                }

                if (StringUtils.isEmpty(action)) {
                    def = new WatchDogDef(WatchDogModel.valueOf(model.toUpperCase()));
                } else {
                    def = new WatchDogDef(WatchDogAction.valueOf(action.toUpperCase()),
                                          WatchDogModel.valueOf(model.toUpperCase()));
                }

                watchDogDefs.add(def);
            }
            extractCpuTuneDef(rootElement);
            extractCpuModeDef(rootElement);
            extractBootDef(rootElement);
            return true;
        } catch (ParserConfigurationException e) {
            logger.debug(e.toString());
        } catch (SAXException e) {
            logger.debug(e.toString());
        } catch (IOException e) {
            logger.debug(e.toString());
        }
        return false;
    }

    /**
     * Parse the memballoon tag.
     * @param devices the devices tag.
     * @return the MemBalloonDef.
     */
    private MemBalloonDef parseMemBalloonTag(Element devices) {
        MemBalloonDef def = new MemBalloonDef();
        NodeList memBalloons = devices.getElementsByTagName("memballoon");
        if (memBalloons != null && memBalloons.getLength() != 0) {
            Element memBalloon = (Element)memBalloons.item(0);
            String model = memBalloon.getAttribute("model");
            if (model.equalsIgnoreCase("virtio")) {
                String statsPeriod = getAttrValue("stats", "period", memBalloon);
                def.defVirtioMemBalloon(statsPeriod);
            }
        }
        return def;
    }

    private static String getTagValue(String tag, Element eElement) {
        if (eElement == null) {
            return null;
        }
        NodeList tagNodeList = eElement.getElementsByTagName(tag);
        if (tagNodeList == null || tagNodeList.getLength() == 0) {
            return null;
        }

        NodeList nlList = tagNodeList.item(0).getChildNodes();

        if (nlList == null || nlList.getLength() == 0) {
            return null;
        }
        Node nValue = nlList.item(0);

        return nValue.getNodeValue();
    }

    private static String getAttrValue(String tag, String attr, Element eElement) {
        if (eElement == null) {
            return null;
        }
        NodeList tagNode = eElement.getElementsByTagName(tag);
        if (tag == null || tagNode.getLength() == 0) {
            return null;
        }
        Element node = (Element)tagNode.item(0);
        return node.getAttribute(attr);
    }

    /**
     * Parse the disk block part of the libvirt XML.
     * @param def
     * @param device
     * @param diskDev
     * @param diskLabel
     * @param bus
     * @param diskFile
     * @param curDiskIndex
     */
    private void parseDiskBlock(DiskDef def, String device, String diskDev, String diskLabel, String bus,
                                String diskFile, int curDiskIndex) {
        if (device.equalsIgnoreCase("disk")) {
            def.defBlockBasedDisk(diskDev, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()));
        } else if (device.equalsIgnoreCase("cdrom")) {
            def.defISODisk(diskFile, curDiskIndex+1, diskLabel, DiskDef.DiskType.BLOCK);
        }
    }

    public Integer getVncPort() {
        return vncPort;
    }

    public List<InterfaceDef> getInterfaces() {
        return interfaces;
    }

    public String getVncPasswd() {
        return vncPasswd;
    }

    public MemBalloonDef getMemBalloon() {
        return memBalloonDef;
    }

    public List<DiskDef> getDisks() {
        return diskDefs;
    }

    public List<RngDef> getRngs() {
        return rngDefs;
    }

    public List<ChannelDef> getChannels() {
        return Collections.unmodifiableList(channels);
    }

    public List<WatchDogDef> getWatchDogs() {
        return watchDogDefs;
    }

    public String getDescription() {
        return desc;
    }

    public String getName() {
        return name;
    }

    public LibvirtVMDef.CpuTuneDef getCpuTuneDef() {
        return cpuTuneDef;
    }

    public LibvirtVMDef.CpuModeDef getCpuModeDef() {
        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) {
            cpuTuneDef = new LibvirtVMDef.CpuTuneDef();
            final Element cpuTuneDefElement = (Element) cpuTunesList.item(0);
            final String cpuShares = getTagValue("shares", cpuTuneDefElement);
            if (StringUtils.isNotBlank(cpuShares)) {
                cpuTuneDef.setShares((Integer.parseInt(cpuShares)));
            }

            final String quota = getTagValue("quota", cpuTuneDefElement);
            if (StringUtils.isNotBlank(quota)) {
                cpuTuneDef.setQuota((Integer.parseInt(quota)));
            }

            final String period = getTagValue("period", cpuTuneDefElement);
            if (StringUtils.isNotBlank(period)) {
                cpuTuneDef.setPeriod((Integer.parseInt(period)));
            }
        }
    }

    private void extractCpuModeDef(final Element rootElement){
        NodeList cpuModeList = rootElement.getElementsByTagName("cpu");
        if (cpuModeList.getLength() > 0){
            cpuModeDef = new LibvirtVMDef.CpuModeDef();
            final Element cpuModeDefElement = (Element) cpuModeList.item(0);
            final String cpuModel = getTagValue("model", cpuModeDefElement);
            if (StringUtils.isNotBlank(cpuModel)){
                cpuModeDef.setModel(cpuModel);
            }
            NodeList cpuFeatures = cpuModeDefElement.getElementsByTagName("features");
            if (cpuFeatures.getLength() > 0) {
                final ArrayList<String> features = new ArrayList<>(cpuFeatures.getLength());
                for (int i = 0; i < cpuFeatures.getLength(); i++) {
                    final Element feature = (Element)cpuFeatures.item(i);
                    final String policy = feature.getAttribute("policy");
                    String featureName = feature.getAttribute("name");
                    if ("disable".equals(policy)) {
                        featureName = "-" + featureName;
                    }
                    features.add(featureName);
                }
                cpuModeDef.setFeatures(features);
            }
            final String sockets = getAttrValue("topology", "sockets", cpuModeDefElement);
            final String cores = getAttrValue("topology", "cores", cpuModeDefElement);
            final String threads = getAttrValue("topology", "threads", cpuModeDefElement);
            if (StringUtils.isNotBlank(sockets) && StringUtils.isNotBlank(cores) && StringUtils.isNotBlank(threads)) {
                cpuModeDef.setTopology(Integer.parseInt(cores), Integer.parseInt(threads), Integer.parseInt(sockets));
            }
        }
    }

    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;
        }
    }
}
