/*
* This file is part of LCMC written by Rasto Levrinc.
*
* Copyright (C) 2015, Rastislav Levrinc.
*
* The LCMC 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.
*
* The LCMC 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 LCMC; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package lcmc.vm.domain;
import lcmc.common.domain.XMLTools;
import lcmc.logger.Logger;
import lcmc.logger.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.inject.Named;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.util.HashMap;
import java.util.Map;
@Named
class VMCreator {
private static final Logger LOG = LoggerFactory.getLogger(VMCreator.class);
private Document doc;
private Map<String, String> parametersMap;
public void init(final Document doc, final Map<String, String> parametersMap) {
this.doc = doc;
this.parametersMap = parametersMap;
}
//<domain type='kvm'>
// <memory>524288</memory>
// <name>fff</name>
// <os>
// <type arch='i686' machine='pc-0.12'>hvm</type>
// </os>
//</domain>
public Element createDomain(final String uuid,
final String domainName,
final boolean needConsole,
final String type) {
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(VMParams.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(VMParams.VM_PARAM_CURRENTMEMORY));
curMemoryNode.appendChild(doc.createTextNode(Long.toString(curMem)));
final String vcpu = parametersMap.get(VMParams.VM_PARAM_VCPU);
if (vcpu != null) {
final Node vcpuNode = root.appendChild(doc.createElement("vcpu"));
vcpuNode.appendChild(doc.createTextNode(vcpu));
}
final String bootloader = parametersMap.get(VMParams.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(VMParams.VM_PARAM_TYPE)));
typeNode.setAttribute("arch", parametersMap.get(VMParams.VM_PARAM_TYPE_ARCH));
typeNode.setAttribute("machine", parametersMap.get(VMParams.VM_PARAM_TYPE_MACHINE));
final String init = parametersMap.get(VMParams.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(VMParams.OS_BOOT_NODE));
bootNode.setAttribute(VMParams.OS_BOOT_NODE_DEV, parametersMap.get(VMParams.VM_PARAM_BOOT));
final String bootDev2 = parametersMap.get(VMParams.VM_PARAM_BOOT_2);
if (bootDev2 != null && !"".equals(bootDev2)) {
final Element bootNode2 = (Element) osNode.appendChild(doc.createElement(VMParams.OS_BOOT_NODE));
bootNode2.setAttribute(VMParams.OS_BOOT_NODE_DEV, parametersMap.get(VMParams.VM_PARAM_BOOT_2));
}
final Node loaderNode = osNode.appendChild(doc.createElement("loader"));
loaderNode.appendChild(doc.createTextNode(parametersMap.get(VMParams.VM_PARAM_LOADER)));
addFeatures(root);
addClockOffset(root);
addCPUMatchNode(root);
final String onPoweroff = parametersMap.get(VMParams.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(VMParams.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(VMParams.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(VMParams.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 void addCPUMatchNode(final Node root) {
final String cpuMatch = parametersMap.get(VMParams.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(VMParams.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(VMParams.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(VMParams.VM_PARAM_CPUMATCH_TOPOLOGY_SOCKETS);
final String cores = parametersMap.get(VMParams.VM_PARAM_CPUMATCH_TOPOLOGY_CORES);
final String threads = parametersMap.get(VMParams.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(VMParams.VM_PARAM_CPUMATCH_FEATURE_POLICY);
final String features = parametersMap.get(VMParams.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);
}
}
public void addFeatures(final Node root) {
final boolean acpi = "True".equals(parametersMap.get(VMParams.VM_PARAM_ACPI));
final boolean apic = "True".equals(parametersMap.get(VMParams.VM_PARAM_APIC));
final boolean pae = "True".equals(parametersMap.get(VMParams.VM_PARAM_PAE));
final boolean hap = "True".equals(parametersMap.get(VMParams.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"));
}
}
}
public void addClockOffset(final Node root) {
final Element clockNode = (Element) root.appendChild(doc.createElement("clock"));
final String offset = parametersMap.get(VMParams.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");
}
public void modifyDomain(final Node domainNode) {
final XPath xpath = XPathFactory.newInstance().newXPath();
final Map<String, String> paths = new HashMap<String, String>();
paths.put(VMParams.VM_PARAM_MEMORY, "memory");
paths.put(VMParams.VM_PARAM_CURRENTMEMORY, "currentMemory");
paths.put(VMParams.VM_PARAM_VCPU, "vcpu");
paths.put(VMParams.VM_PARAM_BOOTLOADER, "bootloader");
paths.put(VMParams.VM_PARAM_BOOT, "os/boot");
paths.put(VMParams.VM_PARAM_BOOT_2, "os/boot");
paths.put(VMParams.VM_PARAM_TYPE, "os/type");
paths.put(VMParams.VM_PARAM_TYPE_ARCH, "os/type");
paths.put(VMParams.VM_PARAM_TYPE_MACHINE, "os/type");
paths.put(VMParams.VM_PARAM_INIT, "os/init");
paths.put(VMParams.VM_PARAM_LOADER, "os/loader");
paths.put(VMParams.VM_PARAM_CPU_MATCH, "cpu");
paths.put(VMParams.VM_PARAM_ACPI, "features");
paths.put(VMParams.VM_PARAM_APIC, "features");
paths.put(VMParams.VM_PARAM_PAE, "features");
paths.put(VMParams.VM_PARAM_HAP, "features");
paths.put(VMParams.VM_PARAM_CLOCK_OFFSET, "clock");
paths.put(VMParams.VM_PARAM_ON_POWEROFF, "on_poweroff");
paths.put(VMParams.VM_PARAM_ON_REBOOT, "on_reboot");
paths.put(VMParams.VM_PARAM_ON_CRASH, "on_crash");
paths.put(VMParams.VM_PARAM_EMULATOR, "devices/emulator");
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 (VMParams.VM_PARAM_BOOT_2.equals(param)) {
if (nodes.getLength() > 1) {
node = (Element) nodes.item(1);
} else {
node = (Element) node.getParentNode().appendChild(doc.createElement(VMParams.OS_BOOT_NODE));
}
}
final String value = parametersMap.get(param);
if (VMParams.VM_PARAM_CPU_MATCH.equals(param)
|| VMParams.VM_PARAM_CLOCK_OFFSET.equals(param)
|| VMParams.VM_PARAM_ACPI.equals(param)
|| VMParams.VM_PARAM_APIC.equals(param)
|| VMParams.VM_PARAM_PAE.equals(param)
|| VMParams.VM_PARAM_HAP.equals(param)) {
domainNode.removeChild(node);
} else if (VMParams.VM_PARAM_BOOT.equals(param)) {
node.setAttribute(VMParams.OS_BOOT_NODE_DEV, value);
} else if (VMParams.VM_PARAM_BOOT_2.equals(param)) {
if (value == null || "".equals(value)) {
node.getParentNode().removeChild(node);
} else {
node.setAttribute(VMParams.OS_BOOT_NODE_DEV, value);
}
} else if (VMParams.VM_PARAM_TYPE_ARCH.equals(param)) {
node.setAttribute("arch", value);
} else if (VMParams.VM_PARAM_TYPE_MACHINE.equals(param)) {
node.setAttribute("machine", value);
} else if (VMParams.VM_PARAM_CPU_MATCH.equals(param)) {
if ("".equals(value)) {
node.getParentNode().removeChild(node);
} else {
node.setAttribute("match", value);
}
} else if (VMParams.VM_PARAM_CPUMATCH_TOPOLOGY_THREADS.equals(param)) {
node.setAttribute("threads", value);
} else {
final Node n = XMLTools.getChildNode(node, "#text");
if (n == null) {
node.appendChild(doc.createTextNode(value));
} else {
n.setNodeValue(value);
}
}
}
addCPUMatchNode(domainNode);
addFeatures(domainNode);
addClockOffset(domainNode);
} catch (final XPathExpressionException e) {
LOG.appError("modifyDomainXML: could not evaluate: ", e);
}
}
}