Package lcmc.vm.domain

Source Code of lcmc.vm.domain.VmsXml

/*
* This file is part of DRBD Management Console by LINBIT HA-Solutions GmbH
* written by Rasto Levrinc.
*
* Copyright (C) 2009, Rastislav Levrinc
* Copyright (C) 2009, LINBIT HA-Solutions GmbH.
*
* DRBD Management Console is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* DRBD Management Console is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with drbd; see the file COPYING.  If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/


package lcmc.vm.domain;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import lcmc.host.domain.Host;
import lcmc.common.domain.StringValue;
import lcmc.common.domain.Value;
import lcmc.common.domain.XML;
import lcmc.common.domain.ConvertCmdCallback;
import lcmc.logger.Logger;
import lcmc.logger.LoggerFactory;
import lcmc.common.domain.util.Tools;
import lcmc.common.domain.Unit;
import lcmc.vm.service.VIRSH;
import lcmc.cluster.service.ssh.ExecCommandConfig;
import lcmc.cluster.service.ssh.SshOutput;

import org.apache.commons.collections15.map.MultiKeyMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* This class parses xml from drbdsetup and drbdadm, stores the
* information in the hashes and provides methods to get this
* information.
* The xml is obtained with drbdsetp xml command and drbdadm dump-xml.
*/
public final class VmsXml extends XML {
    private static final Logger LOG = LoggerFactory.getLogger(VmsXml.class);
    /** Pattern that maches display e.g. :4. */
    private static final Pattern DISPLAY_PATTERN = Pattern.compile(".*:(\\d+)$");
    public static final String VM_PARAM_NAME = "name";
    public static final String VM_PARAM_UUID = "uuid";
    public static final String VM_PARAM_DEFINED = "defined";
    public static final String VM_PARAM_STATUS = "status";
    public static final String VM_PARAM_DOMAIN_TYPE = "domain-type";
    public static final String VM_PARAM_VCPU = "vcpu";
    public static final String VM_PARAM_BOOTLOADER = "bootloader";
    public static final String VM_PARAM_CURRENTMEMORY = "currentMemory";
    public static final String VM_PARAM_MEMORY = "memory";
    public static final String OS_BOOT_NODE = "boot";
    public static final String OS_BOOT_NODE_DEV = "dev";
    public static final String VM_PARAM_BOOT = "boot";
    public static final String VM_PARAM_BOOT_2 = "boot2";
    public static final String VM_PARAM_LOADER = "loader";
    public static final String VM_PARAM_AUTOSTART = "autostart";
    public static final String VM_PARAM_VIRSH_OPTIONS = "virsh-options";
    public static final String VM_PARAM_TYPE = "type";
    public static final String VM_PARAM_INIT = "init";
    public static final String VM_PARAM_TYPE_ARCH = "arch";
    public static final String VM_PARAM_TYPE_MACHINE = "machine";
    public static final String VM_PARAM_ACPI = "acpi";
    public static final String VM_PARAM_APIC = "apic";
    public static final String VM_PARAM_PAE = "pae";
    public static final String VM_PARAM_HAP = "hap";
    public static final String VM_PARAM_CLOCK_OFFSET = "offset";
    public static final String VM_PARAM_CPU_MATCH = "match";
    public static final String VM_PARAM_CPUMATCH_MODEL = "model";
    public static final String VM_PARAM_CPUMATCH_VENDOR = "vendor";
    public static final String VM_PARAM_CPUMATCH_TOPOLOGY_SOCKETS = "sockets";
    public static final String VM_PARAM_CPUMATCH_TOPOLOGY_CORES = "cores";
    public static final String VM_PARAM_CPUMATCH_TOPOLOGY_THREADS = "threads";
    public static final String VM_PARAM_CPUMATCH_FEATURE_POLICY = "policy";
    public static final String VM_PARAM_CPUMATCH_FEATURES = "features";
    public static final String VM_PARAM_ON_POWEROFF = "on_poweroff";
    public static final String VM_PARAM_ON_REBOOT = "on_reboot";
    public static final String VM_PARAM_ON_CRASH = "on_crash";
    public static final String VM_PARAM_EMULATOR = "emulator";
    public static final String NET_PARAM_NAME = "name";
    public static final String NET_PARAM_UUID = "uuid";
    public static final String NET_PARAM_AUTOSTART = "autostart";
    public static final String HW_ADDRESS = "address";
    public static final Map<String, String> PARAM_INTERFACE_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_INTERFACE_ATTRIBUTE = new HashMap<String, String>();
    public static final Map<String, String> PARAM_DISK_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_DISK_ATTRIBUTE = new HashMap<String, String>();
    public static final Map<String, String> PARAM_FILESYSTEM_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_FILESYSTEM_ATTRIBUTE = new HashMap<String, String>();
    public static final Map<String, String> PARAM_INPUTDEV_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_INPUTDEV_ATTRIBUTE = new HashMap<String, String>();

    public static final Map<String, String> PARAM_GRAPHICS_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_GRAPHICS_ATTRIBUTE = new HashMap<String, String>();

    public static final Map<String, String> PARAM_SOUND_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_SOUND_ATTRIBUTE = new HashMap<String, String>();

    public static final Map<String, String> PARAM_SERIAL_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_SERIAL_ATTRIBUTE = new HashMap<String, String>();

    public static final Map<String, String> PARAM_PARALLEL_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_PARALLEL_ATTRIBUTE = new HashMap<String, String>();

    public static final Map<String, String> PARAM_VIDEO_TAG = new HashMap<String, String>();
    public static final Map<String, String> PARAM_VIDEO_ATTRIBUTE = new HashMap<String, String>();

    static {
        PARAM_INTERFACE_ATTRIBUTE.put(InterfaceData.TYPE, "type");
        PARAM_INTERFACE_TAG.put(InterfaceData.MAC_ADDRESS, "mac");
        PARAM_INTERFACE_ATTRIBUTE.put(InterfaceData.MAC_ADDRESS, "address");
        PARAM_INTERFACE_TAG.put(InterfaceData.SOURCE_NETWORK, "source");
        PARAM_INTERFACE_ATTRIBUTE.put(InterfaceData.SOURCE_NETWORK, "network");
        PARAM_INTERFACE_TAG.put(InterfaceData.SOURCE_BRIDGE, "source");
        PARAM_INTERFACE_ATTRIBUTE.put(InterfaceData.SOURCE_BRIDGE, "bridge");
        PARAM_INTERFACE_TAG.put(InterfaceData.TARGET_DEV, "target");
        PARAM_INTERFACE_ATTRIBUTE.put(InterfaceData.TARGET_DEV, "dev");
        PARAM_INTERFACE_TAG.put(InterfaceData.MODEL_TYPE, "model");
        PARAM_INTERFACE_ATTRIBUTE.put(InterfaceData.MODEL_TYPE, "type");
        PARAM_INTERFACE_TAG.put(InterfaceData.SCRIPT_PATH, "script");
        PARAM_INTERFACE_ATTRIBUTE.put(InterfaceData.SCRIPT_PATH, "path");

        PARAM_DISK_ATTRIBUTE.put(DiskData.TYPE, "type");
        PARAM_DISK_TAG.put(DiskData.TARGET_DEVICE, "target");
        PARAM_DISK_ATTRIBUTE.put(DiskData.TARGET_DEVICE, "dev");
        PARAM_DISK_TAG.put(DiskData.SOURCE_FILE, "source");
        PARAM_DISK_ATTRIBUTE.put(DiskData.SOURCE_FILE, "file");
        PARAM_DISK_TAG.put(DiskData.SOURCE_DEVICE, "source");
        PARAM_DISK_ATTRIBUTE.put(DiskData.SOURCE_DEVICE, "dev");

        PARAM_DISK_TAG.put(DiskData.SOURCE_PROTOCOL, "source");
        PARAM_DISK_ATTRIBUTE.put(DiskData.SOURCE_PROTOCOL, "protocol");
        PARAM_DISK_TAG.put(DiskData.SOURCE_NAME, "source");
        PARAM_DISK_ATTRIBUTE.put(DiskData.SOURCE_NAME, "name");

        PARAM_DISK_TAG.put(DiskData.SOURCE_HOST_NAME, "source:host");
        PARAM_DISK_ATTRIBUTE.put(DiskData.SOURCE_HOST_NAME, "name");
        PARAM_DISK_TAG.put(DiskData.SOURCE_HOST_PORT, "source:host");
        PARAM_DISK_ATTRIBUTE.put(DiskData.SOURCE_HOST_PORT, "port");

        PARAM_DISK_TAG.put(DiskData.AUTH_USERNAME, "auth");
        PARAM_DISK_ATTRIBUTE.put(DiskData.AUTH_USERNAME, "username");
        PARAM_DISK_TAG.put(DiskData.AUTH_SECRET_TYPE, "auth:secret");
        PARAM_DISK_ATTRIBUTE.put(DiskData.AUTH_SECRET_TYPE, "type");
        PARAM_DISK_TAG.put(DiskData.AUTH_SECRET_UUID, "auth:secret");
        PARAM_DISK_ATTRIBUTE.put(DiskData.AUTH_SECRET_UUID, "uuid");

        PARAM_DISK_TAG.put(DiskData.TARGET_BUS, "target");
        PARAM_DISK_ATTRIBUTE.put(DiskData.TARGET_BUS, "bus");
        PARAM_DISK_TAG.put(DiskData.DRIVER_NAME, "driver");
        PARAM_DISK_ATTRIBUTE.put(DiskData.DRIVER_NAME, "name");
        PARAM_DISK_TAG.put(DiskData.DRIVER_TYPE, "driver");
        PARAM_DISK_ATTRIBUTE.put(DiskData.DRIVER_TYPE, "type");
        PARAM_DISK_TAG.put(DiskData.DRIVER_CACHE, "driver");
        PARAM_DISK_ATTRIBUTE.put(DiskData.DRIVER_CACHE, "cache");
        PARAM_DISK_ATTRIBUTE.put(DiskData.TARGET_TYPE, "device");
        PARAM_DISK_TAG.put(DiskData.READONLY, "readonly");
        PARAM_DISK_TAG.put(DiskData.SHAREABLE, "shareable");

        PARAM_FILESYSTEM_ATTRIBUTE.put(InterfaceData.TYPE, "type");
        PARAM_FILESYSTEM_TAG.put(FilesystemData.SOURCE_DIR, "source");
        PARAM_FILESYSTEM_ATTRIBUTE.put(FilesystemData.SOURCE_DIR, "dir");
        PARAM_FILESYSTEM_TAG.put(FilesystemData.SOURCE_NAME, "source");
        PARAM_FILESYSTEM_ATTRIBUTE.put(FilesystemData.SOURCE_NAME, "name");
        PARAM_FILESYSTEM_TAG.put(FilesystemData.TARGET_DIR, "target");
        PARAM_FILESYSTEM_ATTRIBUTE.put(FilesystemData.TARGET_DIR, "dir");

        PARAM_INPUTDEV_ATTRIBUTE.put(InputDevData.TYPE, "type");
        PARAM_INPUTDEV_ATTRIBUTE.put(InputDevData.BUS, "bus");

        PARAM_GRAPHICS_ATTRIBUTE.put(GraphicsData.TYPE, "type");
        PARAM_GRAPHICS_ATTRIBUTE.put(GraphicsData.PORT, "port");
        PARAM_GRAPHICS_ATTRIBUTE.put(GraphicsData.AUTOPORT, "autoport");
        PARAM_GRAPHICS_ATTRIBUTE.put(GraphicsData.LISTEN, "listen");
        PARAM_GRAPHICS_ATTRIBUTE.put(GraphicsData.PASSWD, "passwd");
        PARAM_GRAPHICS_ATTRIBUTE.put(GraphicsData.KEYMAP, "keymap");
        PARAM_GRAPHICS_ATTRIBUTE.put(GraphicsData.DISPLAY, "display");
        PARAM_GRAPHICS_ATTRIBUTE.put(GraphicsData.XAUTH, "xauth");

        PARAM_SOUND_ATTRIBUTE.put(SoundData.MODEL, "model");

        PARAM_SERIAL_ATTRIBUTE.put(SerialData.TYPE, "type");
        PARAM_SERIAL_TAG.put(SerialData.SOURCE_PATH, "source");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.SOURCE_PATH, "path");
        PARAM_SERIAL_TAG.put(SerialData.BIND_SOURCE_MODE, "source");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.BIND_SOURCE_MODE, "mode");
        PARAM_SERIAL_TAG.put(SerialData.BIND_SOURCE_HOST, "source");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.BIND_SOURCE_HOST, "host");
        PARAM_SERIAL_TAG.put(SerialData.BIND_SOURCE_SERVICE, "source");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.BIND_SOURCE_SERVICE, "service");
        PARAM_SERIAL_TAG.put(SerialData.CONNECT_SOURCE_MODE, "source");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.CONNECT_SOURCE_MODE, "mode");
        PARAM_SERIAL_TAG.put(SerialData.CONNECT_SOURCE_HOST, "source");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.CONNECT_SOURCE_HOST, "host");
        PARAM_SERIAL_TAG.put(SerialData.CONNECT_SOURCE_SERVICE, "source");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.CONNECT_SOURCE_SERVICE, "service");
        PARAM_SERIAL_TAG.put(SerialData.PROTOCOL_TYPE, "protocol");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.PROTOCOL_TYPE, "type");
        PARAM_SERIAL_TAG.put(SerialData.TARGET_PORT, "target");
        PARAM_SERIAL_ATTRIBUTE.put(SerialData.TARGET_PORT, "port");

        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.TYPE, "type");
        PARAM_PARALLEL_TAG.put(ParallelData.SOURCE_PATH, "source");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.SOURCE_PATH, "path");
        PARAM_PARALLEL_TAG.put(ParallelData.BIND_SOURCE_MODE, "source");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.BIND_SOURCE_MODE, "mode");
        PARAM_PARALLEL_TAG.put(ParallelData.BIND_SOURCE_HOST, "source");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.BIND_SOURCE_HOST, "host");
        PARAM_PARALLEL_TAG.put(ParallelData.BIND_SOURCE_SERVICE, "source");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.BIND_SOURCE_SERVICE, "service");

        PARAM_PARALLEL_TAG.put(ParallelData.CONNECT_SOURCE_MODE, "source");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.CONNECT_SOURCE_MODE, "mode");
        PARAM_PARALLEL_TAG.put(ParallelData.CONNECT_SOURCE_HOST, "source");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.CONNECT_SOURCE_HOST, "host");
        PARAM_PARALLEL_TAG.put(ParallelData.CONNECT_SOURCE_SERVICE, "source");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.CONNECT_SOURCE_SERVICE,
                "service");

        PARAM_PARALLEL_TAG.put(ParallelData.PROTOCOL_TYPE, "protocol");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.PROTOCOL_TYPE, "type");
        PARAM_PARALLEL_TAG.put(ParallelData.TARGET_PORT, "target");
        PARAM_PARALLEL_ATTRIBUTE.put(ParallelData.TARGET_PORT, "port");

        PARAM_VIDEO_TAG.put(VideoData.MODEL_TYPE, "model");
        PARAM_VIDEO_ATTRIBUTE.put(VideoData.MODEL_TYPE, "type");
        PARAM_VIDEO_TAG.put(VideoData.MODEL_VRAM, "model");
        PARAM_VIDEO_ATTRIBUTE.put(VideoData.MODEL_VRAM, "vram");
        PARAM_VIDEO_TAG.put(VideoData.MODEL_HEADS, "model");
        PARAM_VIDEO_ATTRIBUTE.put(VideoData.MODEL_HEADS, "heads");
    }

    /** Returns string representation of the port; it can be autoport. */
    static String portString(final String port) {
        if ("-1".equals(port)) {
            return "auto";
        }
        return port;
    }

    /** Returns string representation graphic display SDL/VNC. */
    public static String graphicsDisplayName(final String type, final String port, final String display) {
        if ("vnc".equals(type)) {
            return type + " : " + portString(port);
        } else if ("sdl".equals(type)) {
            return type + " (" + display + ')';
        }
        return "unknown";
    }

    public static Unit getUnitKiBytes() {
        return new Unit("K", "K", "KiByte", "KiBytes");
    }

    public static Unit getUnitMiBytes() {
        return new Unit("M", "M", "MiByte", "MiBytes");
    }

    public static Unit getUnitGiBytes() {
        return new Unit("G", "G", "GiByte", "GiBytes");
    }

    public static Unit getUnitTiBytes() {
        return new Unit("T", "T", "TiByte", "TiBytes");
    }

    public static Unit getUnitPiBytes() {
        return new Unit("P", "P", "PiByte", "PiBytes");
    }

    public static Unit[] getUnits() {
        return new Unit[]{getUnitKiBytes(), getUnitMiBytes(), getUnitGiBytes(), getUnitTiBytes(), getUnitPiBytes()
        };
    }

    //TODO: move somewhere else
    public static Value convertKilobytes(final String kb) {
        if (!Tools.isNumber(kb)) {
            return new StringValue(kb, getUnitKiBytes());
        }
        final double k = Long.parseLong(kb);
        if (k == 0) {
            return new StringValue("0", getUnitKiBytes());
        }
        if (k / 1024 != (long) (k / 1024)) {
            return new StringValue(kb, getUnitKiBytes());
        }
        final double m = k / 1024;
        if (m / 1024 != (long) (m / 1024)) {
            return new StringValue(Long.toString((long) m), getUnitMiBytes());
        }
        final double g = m / 1024;
        if (g / 1024 != (long) (g / 1024)) {
            return new StringValue(Long.toString((long) g), getUnitGiBytes());
        }
        final double t = g / 1024;
        if (t / 1024 != (long) (t / 1024)) {
            return new StringValue(Long.toString((long) t), getUnitTiBytes());
        }
        final double p = t / 1024;
        return new StringValue(Long.toString((long) p), getUnitPiBytes());
    }

    /** Converts value with unit to kilobites. */
    //TODO: move somewhere else
    public static long convertToKilobytes(final Value value) {
        if (value == null) {
            return -1;
        }
        final String numS = value.getValueForConfig();
        if (Tools.isNumber(numS)) {
            long num = Long.parseLong(numS);
            final Unit unitObject = value.getUnit();
            if (unitObject == null) {
                return -1;
            }
            final String unit = unitObject.getShortName();
            if ("P".equalsIgnoreCase(unit)) {
                num = num * 1024 * 1024 * 1024 * 1024;
            } else if ("T".equalsIgnoreCase(unit)) {
                num = num * 1024 * 1024 * 1024;
            } else if ("G".equalsIgnoreCase(unit)) {
                num = num * 1024 * 1024;
            } else if ("M".equalsIgnoreCase(unit)) {
                num *= 1024;
            } else if ("K".equalsIgnoreCase(unit)) {
            } else {
                return -1;
            }
            return num;
        }
        return -1;
    }
    private final Collection<String> domainNames = new ArrayList<String>();
    private final Map<Value, String> configsToNames = new HashMap<Value, String>();
    private final Map<String, String> namesToConfigs = new HashMap<String, String>();
    private final Map<String, String> netToConfigs = new HashMap<String, String>();
    private final Map<String, String> netNamesConfigsMap = new HashMap<String, String>();
    private final MultiKeyMap<String, String> parameterValues = new MultiKeyMap<String, String>();
    private final Map<String, Integer> domainRemotePorts = new HashMap<String, Integer>();
    private final Map<String, Boolean> domainAutoports = new HashMap<String, Boolean>();
    private final Map<String, Boolean> domainRunningMap = new HashMap<String, Boolean>();
    private final Map<String, Boolean> domainSuspendedMap = new HashMap<String, Boolean>();
    /** Map from domain name and target device to the disk data. */
    private final Map<String, Map<String, DiskData>> disksMap = new LinkedHashMap<String, Map<String, DiskData>>();
    /** Map from domain name and target device to the fs data. */
    private final Map<String, Map<String, FilesystemData>> filesystemsMap =
                                                             new LinkedHashMap<String, Map<String, FilesystemData>>();
    /** Map from domain name and mac address to the interface data. */
    private final Map<String, Map<String, InterfaceData>> interfacesMap =
                                                             new LinkedHashMap<String, Map<String, InterfaceData>>();
    /** Map from domain name and index to the input device data. */
    private final Map<String, Map<String, InputDevData>> inputDevsMap =
                                                             new LinkedHashMap<String, Map<String, InputDevData>>();
    /** Map from domain name and type to the graphics device data. */
    private final Map<String, Map<String, GraphicsData>> graphicsDevsMap =
                                                             new LinkedHashMap<String, Map<String, GraphicsData>>();
    /** Map from domain name and model to the sound device data. */
    private final Map<String, Map<String, SoundData>> soundsMap = new LinkedHashMap<String, Map<String, SoundData>>();
    /** Map from domain name and type to the serial device data. */
    private final Map<String, Map<String, SerialData>> serialsMap =
                                                             new LinkedHashMap<String, Map<String, SerialData>>();
    /** Map from domain name and type to the parallel device data. */
    private final Map<String, Map<String, ParallelData>> parallelsMap =
                                                             new LinkedHashMap<String, Map<String, ParallelData>>();
    /** Map from domain name and model type to the video device data. */
    private final Map<String, Map<String, VideoData>> videosMap = new LinkedHashMap<String, Map<String, VideoData>>();
    /** Map from domain name and network name to the network data. */
    private final Map<Value, NetworkData> networkMap = new LinkedHashMap<Value, NetworkData>();
    private final Collection<String> sourceFileDirs = new TreeSet<String>();
    private final Collection<String> usedMacAddresses = new HashSet<String>();
    private final Host definedOnHost;

    private final ReadWriteLock mXMLDocumentLock = new ReentrantReadWriteLock();
    private final Lock mXMLDocumentReadLock = mXMLDocumentLock.readLock();
    private final Lock mXMLDocumentWriteLock = mXMLDocumentLock.writeLock();
    private Document xmlDocument = null;
    private String oldConfig = null;

    public VmsXml(final Host definedOnHost) {
        super();
        this.definedOnHost = definedOnHost;
    }

    public Node getDomainNode(final String domainName) {
        mXMLDocumentReadLock.lock();
        final Document document;
        try {
            document = xmlDocument;
        } finally {
            mXMLDocumentReadLock.unlock();
        }
        final XPath xpath = XPathFactory.newInstance().newXPath();
        final Node domainNode;
        try {
            final String path = "//vms/vm[@name='" + domainName + "']/config/domain";
            final NodeList domainNodes = (NodeList) xpath.evaluate(path, document, XPathConstants.NODESET);
            if (domainNodes.getLength() == 1) {
                domainNode = domainNodes.item(0);
            } else if (domainNodes.getLength() >= 1) {
                LOG.appError("getDomainNode: " + domainNodes.getLength() + " supposedly unique " + domainName + " configs.");
                return null;
            } else {
                LOG.appWarning("getDomainNode: could not find xml for " + domainName);
                return null;
            }
        } catch (final XPathExpressionException e) {
            LOG.appError("getDomainNode: could not evaluate: ", e);
            return null;
        }
        return domainNode;
    }

    private void saveDomainXML(final String configName, final Node node, final String defineCommand) {
        final String xml;
        try {
            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
            final StreamResult res = new StreamResult(new StringWriter());
            final Source src = new DOMSource(node);
            transformer.transform(src, res);
            xml = res.getWriter().toString();
        } catch (final TransformerException e) {
            LOG.appError("saveDomainXML: " + e.getMessageAndLocation(), e);
            return;
        }
        if (xml != null) {
            definedOnHost.getSSH().scp(xml, configName, "0600", true, defineCommand, null, null);
        }
    }

    private Node getDevicesNode(final XPath xpath, final Node domainNode) {
        try {
            final String devicesPath = "devices";
            final NodeList devicesNodes = (NodeList) xpath.evaluate(devicesPath, domainNode, XPathConstants.NODESET);
            if (devicesNodes.getLength() != 1) {
                LOG.appWarning("getDevicesNode: nodes: " + devicesNodes.getLength());
                return null;
            }
            return devicesNodes.item(0);
        } catch (final XPathExpressionException e) {
            LOG.appError("getDevicesNode: could not evaluate: ", e);
            return null;
        }
    }

    private void addCPUMatchNode(final Document doc, final Node root, final Map<String, String> parametersMap) {
        final String cpuMatch = parametersMap.get(VM_PARAM_CPU_MATCH);
        final Element cpuMatchNode = (Element) root.appendChild(doc.createElement("cpu"));
        if (!"".equals(cpuMatch)) {
            cpuMatchNode.setAttribute("match", cpuMatch);
        }
        final String model = parametersMap.get(VM_PARAM_CPUMATCH_MODEL);
        if (!"".equals(model)) {
            final Node modelNode = cpuMatchNode.appendChild(doc.createElement("model"));
            modelNode.appendChild(doc.createTextNode(model));
        }
        final String vendor = parametersMap.get(VM_PARAM_CPUMATCH_VENDOR);
        if (!"".equals(vendor)) {
            final Node vendorNode = cpuMatchNode.appendChild(doc.createElement("vendor"));
            vendorNode.appendChild(doc.createTextNode(vendor));
        }
        final String sockets = parametersMap.get(VM_PARAM_CPUMATCH_TOPOLOGY_SOCKETS);
        final String cores = parametersMap.get(VM_PARAM_CPUMATCH_TOPOLOGY_CORES);
        final String threads = parametersMap.get(VM_PARAM_CPUMATCH_TOPOLOGY_THREADS);
        final boolean isSockets = !"".equals(sockets);
        final boolean isCores =   !"".equals(cores);
        final boolean isThreads = !"".equals(threads);
        if (isSockets || isCores || isThreads) {
            final Element topologyNode = (Element) cpuMatchNode.appendChild(doc.createElement("topology"));
            if (isSockets) {
                topologyNode.setAttribute("sockets", sockets);
            }
            if (isCores) {
                topologyNode.setAttribute("cores", cores);
            }
            if (isThreads) {
                topologyNode.setAttribute("threads", threads);
            }
        }
        final String policy = parametersMap.get(VM_PARAM_CPUMATCH_FEATURE_POLICY);
        final String features = parametersMap.get(VM_PARAM_CPUMATCH_FEATURES);
        if (policy != null && features != null && !"".equals(policy) && !"".equals(features)) {
            for (final String feature : features.split("\\s+")) {
                final Element featureNode = (Element) cpuMatchNode.appendChild(doc.createElement("feature"));
                featureNode.setAttribute("policy", policy);
                featureNode.setAttribute("name", feature);
            }
        }
        if (!cpuMatchNode.hasChildNodes()) {
            root.removeChild(cpuMatchNode);
        }
    }

    private void addFeatures(final Document doc, final Node root, final Map<String, String> parametersMap) {
        final boolean acpi = "True".equals(parametersMap.get(VM_PARAM_ACPI));
        final boolean apic = "True".equals(parametersMap.get(VM_PARAM_APIC));
        final boolean pae = "True".equals(parametersMap.get(VM_PARAM_PAE));
        final boolean hap = "True".equals(parametersMap.get(VM_PARAM_HAP));
        if (acpi || apic || pae || hap) {
            final Node featuresNode = root.appendChild(doc.createElement("features"));
            if (acpi) {
                featuresNode.appendChild(doc.createElement("acpi"));
            }
            if (apic) {
                featuresNode.appendChild(doc.createElement("apic"));
            }
            if (pae) {
                featuresNode.appendChild(doc.createElement("pae"));
            }
            if (hap) {
                featuresNode.appendChild(doc.createElement("hap"));
            }
        }
    }

    private void addClockOffset(final Document doc, final Node root, final Map<String, String> parametersMap) {
        final Element clockNode = (Element) root.appendChild(doc.createElement("clock"));
        final String offset = parametersMap.get(VM_PARAM_CLOCK_OFFSET);
        clockNode.setAttribute("offset", offset);
        final Element timer1 = (Element) clockNode.appendChild(doc.createElement("timer"));
        timer1.setAttribute("name", "pit");
        timer1.setAttribute("tickpolicy", "delay");
        final Element timer2 = (Element) clockNode.appendChild(doc.createElement("timer"));
        timer2.setAttribute("name", "rtc");
        timer2.setAttribute("tickpolicy", "catchup");

        final Element timer3 = (Element) clockNode.appendChild(doc.createElement("timer"));
        timer3.setAttribute("name", "hpet");
        timer3.setAttribute("present", "no");
    }

    private String getConfigName(final String type, final String domainName) {
        if ("xen".equals(type)) {
            return "/etc/xen/vm/" + domainName + ".xml";
        } else if ("lxc".equals(type)) {
            return "/etc/libvirt/lxc/" + domainName + ".xml";
        } else {
            return "/etc/libvirt/qemu/" + domainName + ".xml";
        }
    }

    public Node createDomainXML(final String uuid,
                                final String domainName,
                                final Map<String, String> parametersMap,
                                final boolean needConsole) {
        //<domain type='kvm'>
        //  <memory>524288</memory>
        //  <name>fff</name>
        //  <os>
        //    <type arch='i686' machine='pc-0.12'>hvm</type>
        //  </os>
        //</domain>

        /* domain type: kvm/xen */
        final String type = parametersMap.get(VM_PARAM_DOMAIN_TYPE);
        final String configName = getConfigName(type, domainName);
        namesToConfigs.put(domainName, configName);
        /* build xml */
        final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        final DocumentBuilder db;
        try {
            db = dbf.newDocumentBuilder();
        } catch (final ParserConfigurationException pce) {
            throw new RuntimeException("cannot configure parser", pce);
        }
        final Document doc = db.newDocument();
        final Element root = (Element) doc.appendChild(doc.createElement("domain"));
        root.setAttribute("type", type); /* kvm/xen */
        final Node uuidNode = root.appendChild(doc.createElement("uuid"));
        uuidNode.appendChild(doc.createTextNode(uuid));
        final Node nameNode = root.appendChild(doc.createElement("name"));
        nameNode.appendChild(doc.createTextNode(domainName));
        final Node memoryNode = root.appendChild(doc.createElement("memory"));
        final long mem = Long.parseLong(parametersMap.get(VM_PARAM_MEMORY));
        memoryNode.appendChild(doc.createTextNode(Long.toString(mem)));
        final Node curMemoryNode = root.appendChild(doc.createElement("currentMemory"));
        final long curMem = Long.parseLong(parametersMap.get(VM_PARAM_CURRENTMEMORY));
        curMemoryNode.appendChild(doc.createTextNode(Long.toString(curMem)));
        final String vcpu = parametersMap.get(VM_PARAM_VCPU);
        if (vcpu != null) {
            final Node vcpuNode = root.appendChild(doc.createElement("vcpu"));
            vcpuNode.appendChild(doc.createTextNode(vcpu));
        }

        final String bootloader = parametersMap.get(VM_PARAM_BOOTLOADER);
        if (bootloader != null) {
            final Node bootloaderNode = root.appendChild(doc.createElement("bootloader"));
            bootloaderNode.appendChild(doc.createTextNode(bootloader));
        }

        final Node osNode = root.appendChild(doc.createElement("os"));
        final Element typeNode = (Element) osNode.appendChild(doc.createElement("type"));
        typeNode.appendChild(doc.createTextNode(parametersMap.get(VM_PARAM_TYPE)));
        typeNode.setAttribute("arch", parametersMap.get(VM_PARAM_TYPE_ARCH));
        typeNode.setAttribute("machine", parametersMap.get(VM_PARAM_TYPE_MACHINE));
        final String init = parametersMap.get(VM_PARAM_INIT);
        if (init != null && !"".equals(init)) {
            final Node initNode = osNode.appendChild(doc.createElement("init"));
            initNode.appendChild(doc.createTextNode(init));
        }
        final Element bootNode = (Element) osNode.appendChild(doc.createElement(OS_BOOT_NODE));
        bootNode.setAttribute(OS_BOOT_NODE_DEV, parametersMap.get(VM_PARAM_BOOT));
        final String bootDev2 = parametersMap.get(VM_PARAM_BOOT_2);
        if (bootDev2 != null && !"".equals(bootDev2)) {
            final Element bootNode2 = (Element) osNode.appendChild(doc.createElement(OS_BOOT_NODE));
            bootNode2.setAttribute(OS_BOOT_NODE_DEV, parametersMap.get(VM_PARAM_BOOT_2));
        }

        final Node loaderNode = osNode.appendChild(doc.createElement("loader"));
        loaderNode.appendChild(doc.createTextNode(parametersMap.get(VM_PARAM_LOADER)));

        addFeatures(doc, root, parametersMap);
        addClockOffset(doc, root, parametersMap);
        addCPUMatchNode(doc, root, parametersMap);

        final String onPoweroff = parametersMap.get(VM_PARAM_ON_POWEROFF);
        if (onPoweroff != null) {
            final Node onPoweroffNode = root.appendChild(doc.createElement("on_poweroff"));
            onPoweroffNode.appendChild(doc.createTextNode(onPoweroff));
        }
        final String onReboot = parametersMap.get(VM_PARAM_ON_REBOOT);
        if (onReboot != null) {
            final Node onRebootNode = root.appendChild(doc.createElement("on_reboot"));
            onRebootNode.appendChild(doc.createTextNode(onReboot));
        }
        final String onCrash = parametersMap.get(VM_PARAM_ON_CRASH);
        if (onCrash != null) {
            final Node onCrashNode = root.appendChild(doc.createElement("on_crash"));
            onCrashNode.appendChild(doc.createTextNode(onCrash));
        }
        final String emulator = parametersMap.get(VM_PARAM_EMULATOR);
        if (emulator != null || needConsole) {
            final Node devicesNode = root.appendChild(doc.createElement("devices"));
            if (needConsole) {
                final Element consoleNode = (Element) devicesNode.appendChild(doc.createElement("console"));
                consoleNode.setAttribute("type", "pty");
            }
            final Node emulatorNode = devicesNode.appendChild(doc.createElement("emulator"));
            emulatorNode.appendChild(doc.createTextNode(emulator));
        }
        return root;
    }

    public Node modifyDomainXML(final String domainName, final Map<String, String> parametersMap) {
        final String configName = namesToConfigs.get(domainName);
        if (configName == null) {
            return null;
        }
        final Node domainNode = getDomainNode(domainName);
        if (domainNode == null) {
            return null;
        }
        final XPath xpath = XPathFactory.newInstance().newXPath();
        final Map<String, String> paths = new HashMap<String, String>();
        paths.put(VM_PARAM_MEMORY, "memory");
        paths.put(VM_PARAM_CURRENTMEMORY, "currentMemory");
        paths.put(VM_PARAM_VCPU, "vcpu");
        paths.put(VM_PARAM_BOOTLOADER, "bootloader");
        paths.put(VM_PARAM_BOOT, "os/boot");
        paths.put(VM_PARAM_BOOT_2, "os/boot");
        paths.put(VM_PARAM_TYPE, "os/type");
        paths.put(VM_PARAM_TYPE_ARCH, "os/type");
        paths.put(VM_PARAM_TYPE_MACHINE, "os/type");
        paths.put(VM_PARAM_INIT, "os/init");
        paths.put(VM_PARAM_LOADER, "os/loader");
        paths.put(VM_PARAM_CPU_MATCH, "cpu");
        paths.put(VM_PARAM_ACPI, "features");
        paths.put(VM_PARAM_APIC, "features");
        paths.put(VM_PARAM_PAE, "features");
        paths.put(VM_PARAM_HAP, "features");
        paths.put(VM_PARAM_CLOCK_OFFSET, "clock");
        paths.put(VM_PARAM_ON_POWEROFF, "on_poweroff");
        paths.put(VM_PARAM_ON_REBOOT, "on_reboot");
        paths.put(VM_PARAM_ON_CRASH, "on_crash");
        paths.put(VM_PARAM_EMULATOR, "devices/emulator");
        final Document doc = domainNode.getOwnerDocument();
        try {
            for (final String param : parametersMap.keySet()) {
                final String path = paths.get(param);
                if (path == null) {
                    continue;
                }
                final NodeList nodes = (NodeList) xpath.evaluate(path, domainNode, XPathConstants.NODESET);
                Element node = (Element) nodes.item(0);
                if (node == null) {
                    continue;
                }
                if (VM_PARAM_BOOT_2.equals(param)) {
                    if (nodes.getLength() > 1) {
                        node = (Element) nodes.item(1);
                    } else {
                        node = (Element) node.getParentNode().appendChild(doc.createElement(OS_BOOT_NODE));
                    }
                }
                final String value = parametersMap.get(param);
                if (VM_PARAM_CPU_MATCH.equals(param)
                    || VM_PARAM_CLOCK_OFFSET.equals(param)
                    || VM_PARAM_ACPI.equals(param)
                    || VM_PARAM_APIC.equals(param)
                    || VM_PARAM_PAE.equals(param)
                    || VM_PARAM_HAP.equals(param)) {
                    domainNode.removeChild(node);
                } else if (VM_PARAM_BOOT.equals(param)) {
                    node.setAttribute(OS_BOOT_NODE_DEV, value);
                } else if (VM_PARAM_BOOT_2.equals(param)) {
                    if (value == null || "".equals(value)) {
                        node.getParentNode().removeChild(node);
                    } else {
                        node.setAttribute(OS_BOOT_NODE_DEV, value);
                    }
                } else if (VM_PARAM_TYPE_ARCH.equals(param)) {
                    node.setAttribute("arch", value);
                } else if (VM_PARAM_TYPE_MACHINE.equals(param)) {
                    node.setAttribute("machine", value);
                } else if (VM_PARAM_CPU_MATCH.equals(param)) {
                    if ("".equals(value)) {
                        node.getParentNode().removeChild(node);
                    } else {
                        node.setAttribute("match", value);
                    }
                } else if (VM_PARAM_CPUMATCH_TOPOLOGY_THREADS.equals(param)) {
                    node.setAttribute("threads", value);
                } else {
                    final Node n = getChildNode(node, "#text");
                    if (n == null) {
                        node.appendChild(doc.createTextNode(value));
                    } else {
                        n.setNodeValue(value);
                    }
                }
            }
            addCPUMatchNode(doc, domainNode, parametersMap);
            addFeatures(doc, domainNode, parametersMap);
            addClockOffset(doc, domainNode, parametersMap);
        } catch (final XPathExpressionException e) {
            LOG.appError("modifyDomainXML: could not evaluate: ", e);
            return null;
        }
        return domainNode;
    }

    private void modifyXMLOption(final Node domainNode,
                                 final Element hwNode,
                                 final String value,
                                 final int pos,
                                 final String tag0,
                                 final String attribute,
                                 final Map<String, Element> parentNodes) {
        if (tag0 == null && attribute != null) {
            /* attribute */
            final Node attributeNode = hwNode.getAttributes().getNamedItem(attribute);
            if (attributeNode == null) {
                if (value != null && !"".equals(value)) {
                    hwNode.setAttribute(attribute, value);
                }
            } else if (value == null || "".equals(value)) {
                hwNode.removeAttribute(attribute);
            } else {
                attributeNode.setNodeValue(value);
            }
            return;
        }

        if (tag0 == null) {
            return;
        }

        final String tag;
        final int i = tag0.indexOf(':');
        final Element pNode;
        if (i > 0) {
            if (value == null) {
                /* don't make empty parent */
                return;
            }
            /* with parent */
            final String parent = tag0.substring(0, i);
            tag = tag0.substring(i + 1);
            pNode = parentNodes.get(parent);
        } else {
            tag = tag0;
            pNode = hwNode;
        }

        Element node = (Element) getChildNode(pNode, tag, pos);
        if ((attribute != null || "True".equals(value))
            && node == null) {
            node = (Element) pNode.appendChild(domainNode.getOwnerDocument().createElement(tag));
        } else if (node != null && attribute == null && (value == null || "".equals(value))) {
            pNode.removeChild(node);
        }
        parentNodes.put(tag, node);

        if (attribute != null) {
            final Node attributeNode = node.getAttributes().getNamedItem(attribute);
            if (attributeNode == null) {
                if (value != null && !"".equals(value)) {
                    node.setAttribute(attribute, value);
                }
            } else {
                if (value == null || "".equals(value)) {
                    node.removeAttribute(attribute);
                } else {
                    attributeNode.setNodeValue(value);
                }
            }
        }
    }

    private void removeXMLOption(final Element hwNode,
                                 int pos,
                                 final String tag0,
                                 final Map<String, Element> parentNodes) {
        if (tag0 == null) {
            return;
        }

        final String tag;
        final int i = tag0.indexOf(':');
        final Element pNode;
        if (i > 0) {
            /* with parent */
            final String parent = tag0.substring(0, i);
            tag = tag0.substring(i + 1);
            pNode = parentNodes.get(parent);
        } else {
            tag = tag0;
            pNode = hwNode;
        }

        Element node;
        do {
            node = (Element) getChildNode(pNode, tag, pos);
            if (node != null) {
                pNode.removeChild(node);
                pos++;
            }
        } while (node != null);
    }

    /** Modify xml of some device element. */
    private void modifyXML(final Node domainNode,
                           final String domainName,
                           final Map<String, String> tagMap,
                           final Map<String, String> attributeMap,
                           final Map<String, String> parametersMap,
                           final String path,
                           final String elementName,
                           final VirtualHardwareComparator vhc) {
        final String configName = namesToConfigs.get(domainName);
        if (configName == null) {
            return;
        }
        if (domainNode == null) {
            return;
        }
        final XPath xpath = XPathFactory.newInstance().newXPath();
        final Node devicesNode = getDevicesNode(xpath, domainNode);
        if (devicesNode == null) {
            return;
        }
        try {
            final NodeList nodes = (NodeList) xpath.evaluate(path, domainNode, XPathConstants.NODESET);
            Element hwNode = vhc.getElement(nodes, parametersMap);
            if (hwNode == null) {
                hwNode = (Element) devicesNode.appendChild(domainNode.getOwnerDocument().createElement(elementName));
            }
            final Map<String, Element> parentNodes = new HashMap<String, Element>();
            for (final String param : parametersMap.keySet()) {
                final String value = parametersMap.get(param);
                final String tag = tagMap.get(param);
                final String attribute = attributeMap.get(param);
                if (value == null) {
                    modifyXMLOption(domainNode, hwNode, value, 0, tag, attribute, parentNodes);
                } else {
                    final String[] values = value.split("\\s*,\\s*");
                    int pos = 0;
                    for (final String v : values) {
                        modifyXMLOption(domainNode, hwNode, v, pos, tag, attribute, parentNodes);
                        pos++;
                    }
                    removeXMLOption(hwNode, pos, tag, parentNodes);
                }
            }
            final Node hwAddressNode = getChildNode(hwNode, HW_ADDRESS);
            if (hwAddressNode != null) {
                hwNode.removeChild(hwAddressNode);
            }
        } catch (final XPathExpressionException e) {
            LOG.appError("modifyXML: could not evaluate: ", e);
        }
    }

    private void removeXML(final String domainName,
                           final Map<String, String> parametersMap,
                           final String path,
                           final VirtualHardwareComparator vhc,
                           final String virshOptions) {
        final String configName = namesToConfigs.get(domainName);
        if (configName == null) {
            return;
        }
        final Node domainNode = getDomainNode(domainName);
        if (domainNode == null) {
            return;
        }
        final XPath xpath = XPathFactory.newInstance().newXPath();
        try {
            final NodeList nodes = (NodeList) xpath.evaluate(path, domainNode, XPathConstants.NODESET);
            final Element hwNode = vhc.getElement(nodes, parametersMap);
            if (hwNode != null) {
                hwNode.getParentNode().removeChild(hwNode);
            }
        } catch (final XPathExpressionException e) {
            LOG.appError("removeXML: could not evaluate: ", e);
            return;
        }
        saveAndDefine(domainNode, domainName, virshOptions);
    }

    public void modifyDiskXML(final Node domainNode,
                              final String domainName,
                              final Map<String, String> parametersMap) {
        modifyXML(domainNode, domainName,
                  PARAM_DISK_TAG,
                  PARAM_DISK_ATTRIBUTE,
                  parametersMap,
                  "devices/disk",
                  "disk",
                  getDiskDataComparator());
    }

    public void modifyFilesystemXML(final Node domainNode,
                                    final String domainName,
                                    final Map<String, String> parametersMap) {
        modifyXML(domainNode,
                  domainName,
                  PARAM_FILESYSTEM_TAG,
                  PARAM_FILESYSTEM_ATTRIBUTE,
                  parametersMap,
                  "devices/filesystem",
                  "filesystem",
                  getFilesystemDataComparator());
    }

    public void saveAndDefine(final Node domainNode, final String domainName, final String virshOptions) {
        final String configName = namesToConfigs.get(domainName);
        final String defineCommand = VIRSH.getDefineCommand(definedOnHost,
                                                            configName + ".new" + " && rm " + configName + ".new",
                                                            virshOptions);
        saveDomainXML(configName, domainNode, defineCommand);
        definedOnHost.setVMInfoMD5(null);
    }

    public void modifyInterfaceXML(final Node domainNode,
                                   final String domainName,
                                   final Map<String, String> parametersMap) {
        modifyXML(domainNode,
                  domainName,
                  PARAM_INTERFACE_TAG,
                  PARAM_INTERFACE_ATTRIBUTE,
                  parametersMap,
                  "devices/interface",
                  "interface",
                  getInterfaceDataComparator());
    }

    public void modifyInputDevXML(final Node domainNode,
                                  final String domainName,
                                  final Map<String, String> parametersMap) {
        modifyXML(domainNode, domainName,
                  PARAM_INPUTDEV_TAG,
                  PARAM_INPUTDEV_ATTRIBUTE,
                  parametersMap,
                  "devices/input",
                  "input",
                  getInputDevDataComparator());
    }

    public void modifyGraphicsXML(final Node domainNode,
                                  final String domainName,
                                  final Map<String, String> parametersMap) {
        modifyXML(domainNode,
                  domainName,
                  PARAM_GRAPHICS_TAG,
                  PARAM_GRAPHICS_ATTRIBUTE,
                  parametersMap,
                  "devices/graphics",
                  "graphics",
                  getGraphicsDataComparator());
    }

    public void modifySoundXML(final Node domainNode,
                               final String domainName,
                               final Map<String, String> parametersMap) {
        modifyXML(domainNode,
                  domainName,
                  PARAM_SOUND_TAG,
                  PARAM_SOUND_ATTRIBUTE,
                  parametersMap,
                  "devices/sound",
                  "sound",
                  getSoundDataComparator());
    }

    public void modifySerialXML(final Node domainNode,
                                final String domainName,
                                final Map<String, String> parametersMap) {
        modifyXML(domainNode,
                  domainName,
                  PARAM_SERIAL_TAG,
                  PARAM_SERIAL_ATTRIBUTE,
                  parametersMap,
                  "devices/serial",
                  "serial",
                  getSerialDataComparator());
    }

    public void modifyParallelXML(final Node domainNode,
                                  final String domainName,
                                  final Map<String, String> parametersMap) {
        modifyXML(domainNode,
                  domainName,
                  PARAM_PARALLEL_TAG,
                  PARAM_PARALLEL_ATTRIBUTE,
                  parametersMap,
                  "devices/parallel",
                  "parallel",
                  getParallelDataComparator());
    }

    /** Modify video device XML. */
    public void modifyVideoXML(final Node domainNode,
                               final String domainName,
                               final Map<String, String> parametersMap) {
        modifyXML(domainNode,
                  domainName,
                  PARAM_VIDEO_TAG,
                  PARAM_VIDEO_ATTRIBUTE,
                  parametersMap,
                  "devices/video",
                  "video",
                  getVideoDataComparator());
    }

    public void removeDiskXML(final String domainName,
                              final Map<String, String> parametersMap,
                              final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/disk", getDiskDataComparator(), virshOptions);
    }

    public void removeFilesystemXML(final String domainName,
                                    final Map<String, String> parametersMap,
                                    final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/filesystem", getFilesystemDataComparator(), virshOptions);
    }

    public void removeInterfaceXML(final String domainName,
                                   final Map<String, String> parametersMap,
                                   final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/interface", getInterfaceDataComparator(), virshOptions);
    }

    public void removeInputDevXML(final String domainName,
                                  final Map<String, String> parametersMap,
                                  final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/input", getInputDevDataComparator(), virshOptions);
    }

    public void removeGraphicsXML(final String domainName,
                                  final Map<String, String> parametersMap,
                                  final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/graphics", getGraphicsDataComparator(), virshOptions);
    }

    public void removeSoundXML(final String domainName,
                               final Map<String, String> parametersMap,
                               final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/sound", getSoundDataComparator(), virshOptions);
    }

    public void removeSerialXML(final String domainName,
                                final Map<String, String> parametersMap,
                                final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/serial", getSerialDataComparator(), virshOptions);
    }

    public void removeParallelXML(final String domainName,
                                  final Map<String, String> parametersMap,
                                  final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/parallel", getParallelDataComparator(), virshOptions);
    }

    public void removeVideoXML(final String domainName,
                               final Map<String, String> parametersMap,
                               final String virshOptions) {
        removeXML(domainName, parametersMap, "devices/video", getVideoDataComparator(), virshOptions);
    }

    public boolean update() {
        final String command = definedOnHost.getDistCommand("VMSXML.GetData", (ConvertCmdCallback) null);
        final SshOutput ret = definedOnHost.captureCommand(new ExecCommandConfig().command(command)
                                                                                  .silentCommand()
                                                                                  .silentOutput());
        if (ret.getExitCode() != 0) {
            return false;
        }
        final String output = ret.getOutput();
        if (output == null) {
            return false;
        }
        return update(output);
    }

    public boolean update(final String output) {
        oldConfig = output;
        final Document document = getXMLDocument(output);
        mXMLDocumentWriteLock.lock();
        try {
            xmlDocument = document;
        } finally {
            mXMLDocumentWriteLock.unlock();
        }
        if (document == null) {
            return false;
        }
        final Node vmsNode = getChildNode(document, "vms");
        final String md5 = getAttribute(vmsNode, "md5");
        if (md5 == null || md5.equals(definedOnHost.getVMInfoMD5())) {
            return false;
        }
        definedOnHost.setVMInfoMD5(md5);
        final NodeList vms = vmsNode.getChildNodes();
        for (int i = 0; i < vms.getLength(); i++) {
            final Node node = vms.item(i);
            if ("net".equals(node.getNodeName())) {
                updateNetworks(node);
            } else if ("vm".equals(node.getNodeName())) {
                updateVM(node);
            } else if ("version".equals(node.getNodeName())) {
                definedOnHost.setLibvirtVersion(getText(node));
            }
        }
        return true;
    }

    private void updateNetworks(final Node netNode) {
        /* one vm */
        if (netNode == null) {
            return;
        }
        final String name = getAttribute(netNode, VM_PARAM_NAME);
        final String config = getAttribute(netNode, "config");
        netToConfigs.put(config, name);
        netNamesConfigsMap.put(name, config);
        final String autostartString = getAttribute(netNode, NET_PARAM_AUTOSTART);
        parseNetConfig(getChildNode(netNode, "network"), name, autostartString);
    }

    /** Parses the libvirt network config file. */
    private void parseNetConfig(final Node networkNode, final String nameInFilename, final String autostartString) {
        if (networkNode == null) {
            return;
        }
        boolean autostart = false;
        if (autostartString != null && "true".equals(autostartString)) {
            autostart = true;
        }
        final NodeList options = networkNode.getChildNodes();
        String name = null;
        String uuid = null;
        String forwardMode = null;
        String bridgeName = null;
        String bridgeSTP = null;
        String bridgeDelay = null;
        String bridgeForwardDelay = null;
        for (int i = 0; i < options.getLength(); i++) {
            final Node optionNode = options.item(i);
            final String nodeName = optionNode.getNodeName();
            if (NET_PARAM_NAME.equals(nodeName)) {
                name = getText(optionNode);
                if (!name.equals(nameInFilename)) {
                    LOG.appWarning("parseNetConfig: unexpected name: " + name + " != " + nameInFilename);
                    return;
                }
            } else if (NET_PARAM_UUID.equals(nodeName)) {
                uuid = getText(optionNode);
            } else if ("forward".equals(nodeName)) {
                forwardMode = getAttribute(optionNode, "mode");
            } else if ("bridge".equals(nodeName)) {
                bridgeName = getAttribute(optionNode, "name");
                bridgeSTP = getAttribute(optionNode, "stp");
                bridgeDelay = getAttribute(optionNode, "delay");
                bridgeForwardDelay = getAttribute(optionNode, "forwardDelay");
            } else if ("mac".equals(nodeName)) {
                /* skip */
            } else if ("ip".equals(nodeName)) {
                /* skip */
            } else if (!"#text".equals(nodeName)) {
                LOG.appWarning("parseNetConfig: unknown network option: " + nodeName);
            }
        }
        if (name != null) {
            final NetworkData networkData = new NetworkData(name,
                                                            uuid,
                                                            autostart,
                                                            forwardMode,
                                                            bridgeName,
                                                            bridgeSTP,
                                                            bridgeDelay,
                                                            bridgeForwardDelay);

            networkMap.put(new StringValue(name), networkData);
        }
    }

    /**
     * Zero, one or more host nodes.
     */
    private void getHostNodes(final Node n, final Collection<String> names, final Collection<String> ports) {
        final NodeList nodes = n.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            final Node hostN = nodes.item(i);
            if ("host".equals(hostN.getNodeName())) {
                names.add(getAttribute(hostN, "name"));
                ports.add(getAttribute(hostN, "port"));
            }
        }
    }

    /** Parse disk xml and populate the devMap. */
    private void parseDiskNode(final Node diskNode, final Map<String, DiskData> devMap) {
        final String type = getAttribute(diskNode, "type");
        final String device = getAttribute(diskNode, "device");
        final NodeList opts = diskNode.getChildNodes();
        String sourceFile = null;
        String sourceDev = null;
        String sourceProtocol = null;
        String sourceName = null;
        final Collection<String> sourceHostNames = new ArrayList<String>();
        final Collection<String> sourceHostPorts = new ArrayList<String>();
        String authUsername = null;
        String authSecretType = null;
        String authSecretUuid = null;
        String targetDev = null;
        String targetBus = null;
        String driverName = null;
        String driverType = null;
        String driverCache = null;
        boolean readonly = false;
        boolean shareable = false;
        for (int k = 0; k < opts.getLength(); k++) {
            final Node optionNode = opts.item(k);
            final String nodeName = optionNode.getNodeName();
            if ("source".equals(nodeName)) {
                sourceFile = getAttribute(optionNode, "file");
                final String dir = Tools.getDirectoryPart(sourceFile);
                if (dir != null) {
                    sourceFileDirs.add(dir);
                }
                sourceDev = getAttribute(optionNode, "dev");
                sourceProtocol = getAttribute(optionNode, "protocol");
                sourceName = getAttribute(optionNode, "name");
                getHostNodes(optionNode, sourceHostNames, sourceHostPorts);
            } else if ("auth".equals(nodeName)) {
                authUsername = getAttribute(optionNode, "username");
                final Node secretN = getChildNode(optionNode, "secret");
                if (secretN != null) {
                    authSecretType = getAttribute(secretN, "type");
                    authSecretUuid = getAttribute(secretN, "uuid");
                }

            } else if ("target".equals(nodeName)) {
                targetDev = getAttribute(optionNode, "dev");
                targetBus = getAttribute(optionNode, "bus");
            } else if ("driver".equals(nodeName)) {
                driverName = getAttribute(optionNode, "name");
                driverType = getAttribute(optionNode, "type");
                driverCache = getAttribute(optionNode, "cache");
            } else if ("readonly".equals(nodeName)) {
                readonly = true;
            } else if ("shareable".equals(nodeName)) {
                shareable = true;
            } else if (HW_ADDRESS.equals(nodeName)) {
                /* it's generated, ignoring. */
            } else if (!"#text".equals(nodeName)) {
                LOG.appWarning("parseDiskNode: unknown disk option: " + nodeName);
            }
        }
        if (targetDev != null) {
            final DiskData diskData = new DiskData(type,
                                                   targetDev,
                                                   sourceFile,
                                                   sourceDev,
                                                   sourceProtocol,
                                                   sourceName,
                                                   Tools.join(", ", sourceHostNames),
                                                   Tools.join(", ", sourceHostPorts),
                                                   authUsername,
                                                   authSecretType,
                                                   authSecretUuid,
                                                   targetBus + '/' + device,
                                                   driverName,
                                                   driverType,
                                                   driverCache,
                                                   readonly,
                                                   shareable);
            devMap.put(targetDev, diskData);
        }
    }

    /** Parses the libvirt config file. */
    private String parseConfig(final Node configNode, final String nameInFilename) {
        if (configNode == null) {
            return null;
        }
        final Node domainNode = getChildNode(configNode, "domain");
        if (domainNode == null) {
            return null;
        }
        final String domainType = getAttribute(domainNode, "type");
        final NodeList options = domainNode.getChildNodes();
        boolean tabletOk = false;
        String name = null;
        for (int i = 0; i < options.getLength(); i++) {
            final Node option = options.item(i);
            if (VM_PARAM_NAME.equals(option.getNodeName())) {
                name = getText(option);
                if (!domainNames.contains(name)) {
                    domainNames.add(name);
                }
                parameterValues.put(name, VM_PARAM_NAME, name);
                parameterValues.put(name, VM_PARAM_DOMAIN_TYPE, domainType);
                if (!name.equals(nameInFilename)) {
                    LOG.appWarning("parseConfig: unexpected name: " + name + " != " + nameInFilename);
                    return domainType;
                }
            } else if (VM_PARAM_UUID.equals(option.getNodeName())) {
                parameterValues.put(name, VM_PARAM_UUID, getText(option));
            } else if (VM_PARAM_VCPU.equals(option.getNodeName())) {
                parameterValues.put(name, VM_PARAM_VCPU, getText(option));
            } else if (VM_PARAM_BOOTLOADER.equals(option.getNodeName())) {
                parameterValues.put(name, VM_PARAM_BOOTLOADER, getText(option));
            } else if (VM_PARAM_CURRENTMEMORY.equals(option.getNodeName())) {
                parameterValues.put(name, VM_PARAM_CURRENTMEMORY, getText(option));
            } else if (VM_PARAM_MEMORY.equals(option.getNodeName())) {
                parameterValues.put(name, VM_PARAM_MEMORY, getText(option));
            } else if ("os".equals(option.getNodeName())) {
                final NodeList osOptions = option.getChildNodes();
                int bootOption = 0;
                for (int j = 0; j < osOptions.getLength(); j++) {
                    final Node osOption = osOptions.item(j);
                    if (OS_BOOT_NODE.equals(osOption.getNodeName())) {
                        if (bootOption == 0) {
                            parameterValues.put(name, VM_PARAM_BOOT, getAttribute(osOption, OS_BOOT_NODE_DEV));
                        } else {
                            parameterValues.put(name, VM_PARAM_BOOT_2, getAttribute(osOption, OS_BOOT_NODE_DEV));
                        }
                        bootOption++;
                    } else if (VM_PARAM_LOADER.equals(osOption.getNodeName())) {
                        parameterValues.put(name, VM_PARAM_LOADER, getText(osOption));
                    } else if (VM_PARAM_TYPE.equals(osOption.getNodeName())) {
                        parameterValues.put(name, VM_PARAM_TYPE, getText(osOption));
                        parameterValues.put(name, VM_PARAM_TYPE_ARCH, getAttribute(osOption, "arch"));
                        parameterValues.put(name, VM_PARAM_TYPE_MACHINE, getAttribute(osOption, "machine"));
                    } else if (VM_PARAM_INIT.equals(osOption.getNodeName())) {
                        parameterValues.put(name, VM_PARAM_INIT, getText(osOption));
                    } else {
                        parameterValues.put(name, osOption.getNodeName(), getText(osOption));
                    }
                }
            } else if ("features".equals(option.getNodeName())) {
                final NodeList ftrOptions = option.getChildNodes();
                for (int j = 0; j < ftrOptions.getLength(); j++) {
                    final Node ftrOption = ftrOptions.item(j);
                    if (VM_PARAM_ACPI.equals(ftrOption.getNodeName())) {
                        parameterValues.put(name, VM_PARAM_ACPI, "True");
                    } else if (VM_PARAM_APIC.equals(ftrOption.getNodeName())) {
                        parameterValues.put(name, VM_PARAM_APIC, "True");
                    } else if (VM_PARAM_PAE.equals(ftrOption.getNodeName())) {
                        parameterValues.put(name, VM_PARAM_PAE, "True");
                    } else if (VM_PARAM_HAP.equals(ftrOption.getNodeName())) {
                        parameterValues.put(name, VM_PARAM_HAP, "True");
                    }
                }
            } else if ("clock".equals(option.getNodeName())) {
                final String offset = getAttribute(option, "offset");
                parameterValues.put(name, VM_PARAM_CLOCK_OFFSET, offset);
            } else if ("cpu".equals(option.getNodeName())) {
                final String match = getAttribute(option, "match");
                if (!"".equals(match)) {
                    parameterValues.put(name, VM_PARAM_CPU_MATCH, match);
                    final NodeList cpuMatchOptions = option.getChildNodes();
                    String policy = "";
                    final Collection<String> features = new ArrayList<String>();
                    for (int j = 0; j < cpuMatchOptions.getLength(); j++) {
                        final Node cpuMatchOption = cpuMatchOptions.item(j);
                        final String op = cpuMatchOption.getNodeName();
                        if ("topology".equals(op)) {
                            parameterValues.put(name,
                                                VM_PARAM_CPUMATCH_TOPOLOGY_SOCKETS,
                                                getAttribute(cpuMatchOption, VM_PARAM_CPUMATCH_TOPOLOGY_SOCKETS));
                            parameterValues.put(name,
                                                VM_PARAM_CPUMATCH_TOPOLOGY_CORES,
                                                getAttribute(cpuMatchOption, VM_PARAM_CPUMATCH_TOPOLOGY_CORES));
                            parameterValues.put(name,
                                                VM_PARAM_CPUMATCH_TOPOLOGY_THREADS,
                                                getAttribute(cpuMatchOption, VM_PARAM_CPUMATCH_TOPOLOGY_THREADS));
                        } else if ("feature".equals(op)) {
                            /* asuming the same policy for all */
                            policy = getAttribute(cpuMatchOption, VM_PARAM_CPUMATCH_FEATURE_POLICY);
                            features.add(getAttribute(cpuMatchOption, "name"));
                        } else {
                            parameterValues.put(name, op, getText(cpuMatchOption));
                        }
                    }
                    if (!"".equals(policy) && !features.isEmpty()) {
                        parameterValues.put(name, VM_PARAM_CPUMATCH_FEATURE_POLICY, policy);
                        parameterValues.put(name, VM_PARAM_CPUMATCH_FEATURES, Tools.join(" ", features));
                    }
                }
            } else if (VM_PARAM_ON_POWEROFF.equals(option.getNodeName())) {
                parameterValues.put(name, VM_PARAM_ON_POWEROFF, getText(option));
            } else if (VM_PARAM_ON_REBOOT.equals(option.getNodeName())) {
                parameterValues.put(name, VM_PARAM_ON_REBOOT, getText(option));
            } else if (VM_PARAM_ON_CRASH.equals(option.getNodeName())) {
                parameterValues.put(name, VM_PARAM_ON_CRASH, getText(option));
            } else if ("devices".equals(option.getNodeName())) {
                final Map<String, DiskData> devMap = new LinkedHashMap<String, DiskData>();
                final Map<String, FilesystemData> fsMap = new LinkedHashMap<String, FilesystemData>();
                final Map<String, InterfaceData> macMap = new LinkedHashMap<String, InterfaceData>();
                final Map<String, InputDevData> inputMap = new LinkedHashMap<String, InputDevData>();
                final Map<String, GraphicsData> graphicsMap = new LinkedHashMap<String, GraphicsData>();
                final Map<String, SoundData> soundMap = new LinkedHashMap<String, SoundData>();
                final Map<String, SerialData> serialMap = new LinkedHashMap<String, SerialData>();
                final Map<String, ParallelData> parallelMap = new LinkedHashMap<String, ParallelData>();
                final Map<String, VideoData> videoMap = new LinkedHashMap<String, VideoData>();
                final NodeList devices = option.getChildNodes();
                for (int j = 0; j < devices.getLength(); j++) {
                    final Node deviceNode = devices.item(j);
                    if ("emulator".equals(deviceNode.getNodeName())) {
                        parameterValues.put(name, VM_PARAM_EMULATOR, getText(deviceNode));
                    } else if ("input".equals(deviceNode.getNodeName())) {
                        final String type = getAttribute(deviceNode, "type");
                        final String bus = getAttribute(deviceNode, "bus");
                        if ("tablet".equals(type)) {
                            tabletOk = true;
                        }
                        final InputDevData inputDevData = new InputDevData(type, bus);
                        inputMap.put(type + " : " + bus, inputDevData);
                    } else if ("graphics".equals(deviceNode.getNodeName())) {
                        /** remotePort will be overwritten with virsh output */
                        final String type = getAttribute(deviceNode, "type");
                        final String port = getAttribute(deviceNode, "port");
                        final String autoport = getAttribute(deviceNode, "autoport");
                        final String listen = getAttribute(deviceNode, "listen");
                        final String passwd = getAttribute(deviceNode, "passwd");
                        final String keymap = getAttribute(deviceNode, "keymap");
                        final String display = getAttribute(deviceNode, "display");
                        final String xauth = getAttribute(deviceNode, "xauth");
                        LOG.debug2("parseConfig: type: " + type);
                        LOG.debug2("parseConfig: port: " + port);
                        LOG.debug2("parseConfig: autoport: " + autoport);
                        if ("vnc".equals(type)) {
                            if (port != null && Tools.isNumber(port)) {
                                domainRemotePorts.put(name, Integer.parseInt(port));
                            }
                            if ("yes".equals(autoport)) {
                                domainAutoports.put(name, true);
                            } else {
                                domainAutoports.put(name, false);
                            }
                        }
                        final GraphicsData graphicsData =
                                                new GraphicsData(type, port, listen, passwd, keymap, display, xauth);
                        graphicsMap.put(graphicsDisplayName(type, port, display), graphicsData);
                    } else if ("disk".equals(deviceNode.getNodeName())) {
                        parseDiskNode(deviceNode, devMap);
                    } else if ("filesystem".equals(deviceNode.getNodeName())) {
                        final String type = getAttribute(deviceNode, "type");
                        final NodeList opts = deviceNode.getChildNodes();
                        String sourceDir = null;
                        String sourceName = null;
                        String targetDir = null;
                        for (int k = 0; k < opts.getLength(); k++) {
                            final Node optionNode = opts.item(k);
                            final String nodeName = optionNode.getNodeName();
                            if ("source".equals(nodeName)) {
                                sourceDir = getAttribute(optionNode, "dir");
                                sourceName = getAttribute(optionNode, "name");
                            } else if ("target".equals(nodeName)) {
                                targetDir = getAttribute(optionNode, "dir");
                            } else if (!"#text".equals(nodeName)) {
                                LOG.appWarning("parseConfig: unknown fs option: " + nodeName);
                            }
                        }
                        if (targetDir != null) {
                            final FilesystemData filesystemData =
                                                         new FilesystemData(type, sourceDir, sourceName, targetDir);
                            fsMap.put(targetDir, filesystemData);
                        }
                    } else if ("interface".equals(deviceNode.getNodeName())) {
                        final String type = getAttribute(deviceNode, "type");
                        String macAddress = null;
                        String sourceNetwork = null;
                        String sourceBridge = null;
                        String targetDev = null;
                        String modelType = null;
                        String scriptPath = null;
                        final NodeList opts = deviceNode.getChildNodes();
                        for (int k = 0; k < opts.getLength(); k++) {
                            final Node optionNode = opts.item(k);
                            final String nodeName = optionNode.getNodeName();
                            if ("source".equals(nodeName)) {
                                sourceBridge = getAttribute(optionNode, "bridge");
                                sourceNetwork = getAttribute(optionNode, "network");
                            } else if ("target".equals(nodeName)) {
                                targetDev = getAttribute(optionNode, "dev");
                            } else if ("mac".equals(nodeName)) {
                                macAddress = getAttribute(optionNode, "address");
                            } else if ("model".equals(nodeName)) {
                                modelType = getAttribute(optionNode, "type");
                            } else if ("script".equals(nodeName)) {
                                scriptPath = getAttribute(optionNode, "path");
                            } else if (HW_ADDRESS.equals(nodeName)) {
                                /* it's generated, ignoring. */
                            } else if (!"#text".equals(nodeName)) {
                                LOG.appWarning("parseConfig: unknown interface option: " + nodeName);
                            }
                        }
                        if (macAddress != null) {
                            final InterfaceData interfaceData = new InterfaceData(type,
                                                                                  macAddress,
                                                                                  sourceNetwork,
                                                                                  sourceBridge,
                                                                                  targetDev,
                                                                                  modelType,
                                                                                  scriptPath);
                            macMap.put(macAddress, interfaceData);
                            usedMacAddresses.add(macAddress);
                        }
                    } else if ("sound".equals(deviceNode.getNodeName())) {
                        final String model = getAttribute(deviceNode, "model");
                        final NodeList opts = deviceNode.getChildNodes();
                        for (int k = 0; k < opts.getLength(); k++) {
                            final Node optionNode = opts.item(k);
                            final String nodeName = optionNode.getNodeName();
                            if (HW_ADDRESS.equals(nodeName)) {
                                /* it's generated, ignoring. */
                            } else if (!"#text".equals(nodeName)) {
                                LOG.appWarning("parseConfig: unknown sound option: " + nodeName);
                            }
                        }
                        if (model != null) {
                            final SoundData soundData = new SoundData(model);
                            soundMap.put(model, soundData);
                        }
                    } else if ("serial".equals(deviceNode.getNodeName())) {
                        final String type = getAttribute(deviceNode, "type");
                        final NodeList opts = deviceNode.getChildNodes();
                        String sourcePath = null;
                        String bindSourceMode = null;
                        String bindSourceHost = null;
                        String bindSourceService = null;
                        String connectSourceMode = null;
                        String connectSourceHost = null;
                        String connectSourceService = null;
                        String protocolType = null;
                        String targetPort = null;
                        for (int k = 0; k < opts.getLength(); k++) {
                            final Node optionNode = opts.item(k);
                            final String nodeName = optionNode.getNodeName();
                            if ("source".equals(nodeName)) {
                                sourcePath = getAttribute(optionNode, "path");
                                final String sourceMode = getAttribute(optionNode, "mode");
                                if ("bind".equals(sourceMode)) {
                                    bindSourceMode = sourceMode;
                                    bindSourceHost = getAttribute(optionNode, "host");
                                    bindSourceService = getAttribute(optionNode, "service");
                                } else if ("connect".equals(sourceMode)) {
                                    connectSourceMode = sourceMode;
                                    connectSourceHost = getAttribute(optionNode, "host");
                                    connectSourceService = getAttribute(optionNode, "service");
                                } else {
                                    LOG.appWarning("parseConfig: uknown source mode: " + sourceMode);
                                }
                            } else if ("protocol".equals(nodeName)) {
                                protocolType = getAttribute(optionNode, "type");
                            } else if ("target".equals(nodeName)) {
                                targetPort = getAttribute(optionNode, "port");
                            } else if (HW_ADDRESS.equals(nodeName)) {
                                /* it's generated, ignoring. */
                            } else if (!"#text".equals(nodeName)) {
                                LOG.appWarning("parseConfig: unknown serial option: " + nodeName);
                            }
                        }
                        if (type != null) {
                            final SerialData serialData = new SerialData(type,
                                                                         sourcePath,
                                                                         bindSourceMode,
                                                                         bindSourceHost,
                                                                         bindSourceService,
                                                                         connectSourceMode,
                                                                         connectSourceHost,
                                                                         connectSourceService,
                                                                         protocolType,
                                                                         targetPort);
                            serialMap.put("serial " + targetPort + " / " + type, serialData);
                        }
                    } else if ("parallel".equals(deviceNode.getNodeName())) {
                        final String type = getAttribute(deviceNode, "type");
                        final NodeList opts = deviceNode.getChildNodes();
                        String sourcePath = null;
                        String bindSourceMode = null;
                        String bindSourceHost = null;
                        String bindSourceService = null;
                        String connectSourceMode = null;
                        String connectSourceHost = null;
                        String connectSourceService = null;
                        String protocolType = null;
                        String targetPort = null;
                        for (int k = 0; k < opts.getLength(); k++) {
                            final Node optionNode = opts.item(k);
                            final String nodeName = optionNode.getNodeName();
                            if ("source".equals(nodeName)) {
                                sourcePath = getAttribute(optionNode, "path");
                                final String sourceMode = getAttribute(optionNode, "mode");
                                if ("bind".equals(sourceMode)) {
                                    bindSourceMode = sourceMode;
                                    bindSourceHost = getAttribute(optionNode, "host");
                                    bindSourceService = getAttribute(optionNode, "service");
                                } else if ("connect".equals(sourceMode)) {
                                    connectSourceMode = sourceMode;
                                    connectSourceHost = getAttribute(optionNode, "host");
                                    connectSourceService = getAttribute(optionNode, "service");
                                } else {
                                    LOG.appWarning("parseConfig: unknown source mode: " + sourceMode);
                                }
                            } else if ("protocol".equals(nodeName)) {
                                protocolType = getAttribute(optionNode, "type");
                            } else if ("target".equals(nodeName)) {
                                targetPort = getAttribute(optionNode, "port");
                            } else if (HW_ADDRESS.equals(nodeName)) {
                                /* it's generated, ignoring. */
                            } else if (!"#text".equals(nodeName)) {
                                LOG.appWarning("parseConfig: unknown parallel option: " + nodeName);
                            }
                        }
                        if (type != null) {
                            final ParallelData parallelData = new ParallelData(type,
                                                                               sourcePath,
                                                                               bindSourceMode,
                                                                               bindSourceHost,
                                                                               bindSourceService,
                                                                               connectSourceMode,
                                                                               connectSourceHost,
                                                                               connectSourceService,
                                                                               protocolType,
                                                                               targetPort);
                            parallelMap.put("parallel " + targetPort + " / " + type, parallelData);
                        }
                    } else if ("video".equals(deviceNode.getNodeName())) {
                        final NodeList opts = deviceNode.getChildNodes();
                        String modelType = null;
                        String modelVRAM = null;
                        String modelHeads = null;
                        for (int k = 0; k < opts.getLength(); k++) {
                            final Node optionNode = opts.item(k);
                            final String nodeName = optionNode.getNodeName();
                            if ("model".equals(nodeName)) {
                                modelType = getAttribute(optionNode, "type");
                                modelVRAM = getAttribute(optionNode, "vram");
                                modelHeads = getAttribute(optionNode, "heads");
                            } else if (HW_ADDRESS.equals(nodeName)) {
                                /* it's generated, ignoring. */
                            } else if (!"#text".equals(nodeName)) {
                                LOG.appWarning("parseConfig: unknown video option: " + nodeName);
                            }
                        }
                        if (modelType != null) {
                            final VideoData videoData = new VideoData(modelType, modelVRAM, modelHeads);
                            videoMap.put(modelType, videoData);
                        }
                    } else if ("controller".equals(deviceNode.getNodeName())) {
                        /* it's generated, ignore */
                    } else if ("memballoon".equals(deviceNode.getNodeName())) {
                        /* it's generated, ignore */
                    } else if (!"#text".equals(deviceNode.getNodeName())) {
                        LOG.appWarning("parseConfig: unknown device: " + deviceNode.getNodeName());

                    }
                }
                disksMap.put(name, devMap);
                filesystemsMap.put(name, fsMap);
                interfacesMap.put(name, macMap);
                inputDevsMap.put(name, inputMap);
                graphicsDevsMap.put(name, graphicsMap);
                soundsMap.put(name, soundMap);
                serialsMap.put(name, serialMap);
                parallelsMap.put(name, parallelMap);
                videosMap.put(name, videoMap);
            }
        }
        if (!tabletOk) {
            LOG.appWarning("parseConfig: you should enable input type tablet for " + name);
        }
        return domainType;
    }

    private void updateVM(final Node vmNode) {
        /* one vm */
        if (vmNode == null) {
            return;
        }
        final Node infoNode = getChildNode(vmNode, "info");
        final String domainName = getAttribute(vmNode, VM_PARAM_NAME);
        final String autostart = getAttribute(vmNode, VM_PARAM_AUTOSTART);
        final String virshOptions = getAttribute(vmNode, VM_PARAM_VIRSH_OPTIONS);
        parameterValues.put(domainName, VM_PARAM_VIRSH_OPTIONS, virshOptions);
        if (autostart != null && "True".equals(autostart)) {
            parameterValues.put(domainName, VM_PARAM_AUTOSTART, definedOnHost.getName());
        } else {
            parameterValues.put(domainName, VM_PARAM_AUTOSTART, null);
        }
        if (infoNode != null) {
            parseInfo(domainName, getText(infoNode));
        }
        final Node vncdisplayNode = getChildNode(vmNode, "vncdisplay");
        domainRemotePorts.put(domainName, -1);
        if (vncdisplayNode != null) {
            final String vncdisplay = getText(vncdisplayNode).trim();
            final Matcher m = DISPLAY_PATTERN.matcher(vncdisplay);
            if (m.matches()) {
                domainRemotePorts.put(domainName, Integer.parseInt(m.group(1)) + 5900);
            }
        }
        final Node configNode = getChildNode(vmNode, "config");
        final String type = parseConfig(configNode, domainName);
        final String configName = getConfigName(type, domainName);
        configsToNames.put(new StringValue(configName), domainName);
        namesToConfigs.put(domainName, configName);
    }

    /** Updates all data for this domain. */
    void parseInfo(final String name, final String info) {
        if (info != null) {
            boolean running = false;
            boolean suspended = false;
            for (final String line : info.split("\n")) {
                final String[] optionValue = line.split(":");
                if (optionValue.length == 2) {
                    final String option = optionValue[0].trim();
                    final String value = optionValue[1].trim();
                    if ("State".equals(option)) {
                        if ("running".equals(value) || "idle".equals(value)) {
                            running = true;
                            suspended = false;
                        } else if ("paused".equals(value)) {
                            running = true;
                            suspended = true;
                        }
                    }
                }
            }
            domainRunningMap.put(name, running);
            domainSuspendedMap.put(name, suspended);
        }
    }

    public Collection<String> getDomainNames() {
        return domainNames;
    }

    public boolean isRunning(final String domainName) {
        final Boolean running = domainRunningMap.get(domainName);
        if (running != null) {
            return running;
        }
        return false;
    }

    public boolean isSuspended(final String domainName) {
        final Boolean suspended = domainSuspendedMap.get(domainName);
        if (suspended != null) {
            return suspended;
        }
        return false;
    }


    public int getRemotePort(final String domainName) {
        final Integer port = domainRemotePorts.get(domainName);
        if (port == null) {
            return -1;
        } else {
            return port;
        }
    }

    public Host getDefinedOnHost() {
        return definedOnHost;
    }

    public Collection<Value> getConfigs() {
        return configsToNames.keySet();
    }

    public String getNameFromConfig(final String config) {
        return configsToNames.get(new StringValue(config));
    }

    public String getValue(final String name, final String param) {
        return parameterValues.get(name, param);
    }

    public Map<String, DiskData> getDisks(final String name) {
        return disksMap.get(name);
    }

    public Map<String, FilesystemData> getFilesystems(final String name) {
        return filesystemsMap.get(name);
    }

    public Map<String, InterfaceData> getInterfaces(final String name) {
        return interfacesMap.get(name);
    }

    public List<Value> getNetworks() {
        return new ArrayList<Value>(networkMap.keySet());
    }

    public Map<String, InputDevData> getInputDevs(final String name) {
        return inputDevsMap.get(name);
    }

    public Map<String, GraphicsData> getGraphicDisplays(final String name) {
        return graphicsDevsMap.get(name);
    }

    public Map<String, SoundData> getSounds(final String name) {
        return soundsMap.get(name);
    }

    public Map<String, SerialData> getSerials(final String name) {
        return serialsMap.get(name);
    }

    public Map<String, ParallelData> getParallels(final String name) {
        return parallelsMap.get(name);
    }

    public Map<String, VideoData> getVideos(final String name) {
        return videosMap.get(name);
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getDiskDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                Element el = null;
                final String targetDev = parameters.get(DiskData.SAVED_TARGET_DEVICE);
                if (targetDev != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = getChildNode(nodes.item(i), "target");
                        if (targetDev.equals(getAttribute(mn, "dev"))) {
                            el = (Element) nodes.item(i);
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getFilesystemDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                Element el = null;
                final String targetDev = parameters.get(FilesystemData.SAVED_TARGET_DIR);
                if (targetDev != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = getChildNode(nodes.item(i), "target");
                        if (targetDev.equals(getAttribute(mn, "dir"))) {
                            el = (Element) nodes.item(i);
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getInterfaceDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                final String macAddress = parameters.get(InterfaceData.SAVED_MAC_ADDRESS);
                Element el = null;
                if (macAddress != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = getChildNode(nodes.item(i), "mac");
                        if (macAddress.equals(getAttribute(mn, "address"))) {
                            el = (Element) nodes.item(i);
                            break;
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getInputDevDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                final String type = parameters.get(InputDevData.SAVED_TYPE);
                final String bus = parameters.get(InputDevData.SAVED_BUS);
                Element el = null;
                if (type != null && bus != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = nodes.item(i);
                        if (type.equals(getAttribute(mn, "type")) && bus.equals(getAttribute(mn, "bus"))) {
                            el = (Element) nodes.item(i);
                            break;
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getGraphicsDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                final String type = parameters.get(GraphicsData.SAVED_TYPE);
                Element el = null;
                if (type != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = nodes.item(i);
                        if (type.equals(getAttribute(mn, "type"))) {
                            el = (Element) nodes.item(i);
                            break;
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getSoundDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                final String model = parameters.get(SoundData.SAVED_MODEL);
                Element el = null;
                if (model != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = nodes.item(i);
                        if (model.equals(getAttribute(mn, "model"))) {
                            el = (Element) nodes.item(i);
                            break;
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getSerialDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                final String type = parameters.get(SerialData.SAVED_TYPE);
                Element el = null;
                if (type != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = nodes.item(i);
                        if (type.equals(getAttribute(mn, "type"))) {
                            el = (Element) nodes.item(i);
                            break;
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getParallelDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                final String type = parameters.get(ParallelData.SAVED_TYPE);
                Element el = null;
                if (type != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = nodes.item(i);
                        if (type.equals(getAttribute(mn, "type"))) {
                            el = (Element) nodes.item(i);
                            break;
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns function that gets the node that belongs to the paremeters. */
    protected VirtualHardwareComparator getVideoDataComparator() {
        return new VirtualHardwareComparator() {
            @Override
            public Element getElement(final NodeList nodes, final Map<String, String> parameters) {
                Element el = null;
                final String modelType = parameters.get(VideoData.SAVED_MODEL_TYPE);
                if (modelType != null) {
                    for (int i = 0; i < nodes.getLength(); i++) {
                        final Node mn = getChildNode(nodes.item(i), "model");
                        if (modelType.equals(getAttribute(mn, "type"))) {
                            el = (Element) nodes.item(i);
                        }
                    }
                }
                return el;
            }
        };
    }

    /** Returns source file directories. */
    public Iterable<String> getSourceFileDirs() {
        return sourceFileDirs;
    }

    /** Return set of mac addresses. */
    public Collection<String> getUsedMacAddresses() {
        return usedMacAddresses;
    }

    public String getConfig() {
        return oldConfig;
    }
}
TOP

Related Classes of lcmc.vm.domain.VmsXml

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.