From 10567e0145ce651c327267d8f4ea31d82bc7e239 Mon Sep 17 00:00:00 2001
From: Manuel Bentele
Date: Fri, 29 Jan 2021 12:22:05 +0100
Subject: Add base classes and utilites to represent Libvirt XML documents
---
.../openslx/libvirt/xml/LibvirtXmlCreatable.java | 26 ++
.../openslx/libvirt/xml/LibvirtXmlDocument.java | 374 +++++++++++++++++++++
.../libvirt/xml/LibvirtXmlDocumentException.java | 25 ++
.../openslx/libvirt/xml/LibvirtXmlEditable.java | 241 +++++++++++++
.../org/openslx/libvirt/xml/LibvirtXmlNode.java | 356 ++++++++++++++++++++
.../openslx/libvirt/xml/LibvirtXmlResources.java | 52 +++
.../libvirt/xml/LibvirtXmlSchemaValidator.java | 283 ++++++++++++++++
.../libvirt/xml/LibvirtXmlSerializable.java | 57 ++++
.../xml/LibvirtXmlSerializationException.java | 25 ++
.../openslx/libvirt/xml/LibvirtXmlValidatable.java | 18 +
.../libvirt/xml/LibvirtXmlValidationException.java | 25 ++
.../libvirt/xml/LibvirtXmlDocumentTest.java | 262 +++++++++++++++
.../libvirt/xml/LibvirtXmlTestResources.java | 29 ++
.../xml/qemu-kvm_default-archlinux-vm-cdrom.xml | 144 ++++++++
.../xml/qemu-kvm_default-archlinux-vm-floppy.xml | 145 ++++++++
.../xml/qemu-kvm_default-archlinux-vm-no-hdd.xml | 134 ++++++++
.../xml/qemu-kvm_default-archlinux-vm-no-nic.xml | 134 ++++++++
.../xml/qemu-kvm_default-archlinux-vm-no-sound.xml | 136 ++++++++
.../xml/qemu-kvm_default-archlinux-vm-no-usb.xml | 127 +++++++
.../libvirt/xml/qemu-kvm_default-archlinux-vm.xml | 140 ++++++++
.../qemu-kvm_default-ubuntu-20-04-vm-invalid.xml | 164 +++++++++
.../xml/qemu-kvm_default-ubuntu-20-04-vm.xml | 164 +++++++++
22 files changed, 3061 insertions(+)
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlCreatable.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocument.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocumentException.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlResources.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlSchemaValidator.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializable.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializationException.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidatable.java
create mode 100644 src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidationException.java
create mode 100644 src/test/java/org/openslx/libvirt/xml/LibvirtXmlDocumentTest.java
create mode 100644 src/test/java/org/openslx/libvirt/xml/LibvirtXmlTestResources.java
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-cdrom.xml
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-floppy.xml
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-hdd.xml
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-nic.xml
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-sound.xml
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-usb.xml
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm.xml
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm-invalid.xml
create mode 100644 src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm.xml
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlCreatable.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlCreatable.java
new file mode 100644
index 0000000..e799ace
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlCreatable.java
@@ -0,0 +1,26 @@
+package org.openslx.libvirt.xml;
+
+import org.w3c.dom.Node;
+
+/**
+ * Serializability of a Libvirt XML object from/to its XML representation.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public interface LibvirtXmlCreatable
+{
+ /**
+ * Serializing an object from its XML representation.
+ *
+ * @param xmlNode The object's XML representation.
+ */
+ void fromXmlNode( Node xmlNode );
+
+ /**
+ * Serializing the object to its XML representation.
+ *
+ * @return XML representation of the object.
+ */
+ Node toXmlNode();
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocument.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocument.java
new file mode 100644
index 0000000..abab162
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocument.java
@@ -0,0 +1,374 @@
+package org.openslx.libvirt.xml;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+
+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.TransformerConfigurationException;
+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.transform.stream.StreamSource;
+
+import org.apache.commons.io.IOUtils;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * A generic representation of a Libvirt XML file.
+ *
+ * @implNote Base class to derive the representation of specific Libvirt XML files.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public abstract class LibvirtXmlDocument implements LibvirtXmlSerializable, LibvirtXmlValidatable
+{
+ /**
+ * Document builder to parse Libvirt XML document from file.
+ */
+ private DocumentBuilder domBuilder = null;
+
+ /**
+ * Representation of a Libvirt XML document.
+ */
+ private Document xmlDocument = null;
+
+ /**
+ * XML transformer to transform Libvirt XML document to a file.
+ */
+ private Transformer xmlTransformer = null;
+
+ /**
+ * XML root node of the Libvirt XML document.
+ */
+ private LibvirtXmlNode rootXmlNode = null;
+
+ /**
+ * RNG schema validator to validate the Libvirt XML document content.
+ */
+ private LibvirtXmlSchemaValidator rngValidator = null;
+
+ /**
+ * Creates and initializes XML context to create and transform a Libvirt XML file from/to a file.
+ *
+ * @param rngSchema RNG schema to validate the Libvirt XML document content.
+ *
+ * @throws LibvirtXmlDocumentException error occured during setup of the XML context to read and
+ * write from/to a Libvirt XML file.
+ */
+ private void createXmlContext( Source rngSchema ) throws LibvirtXmlDocumentException
+ {
+ // used for XML input
+ try {
+ DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
+ domFactory.setIgnoringElementContentWhitespace( true );
+ domFactory.setNamespaceAware( true );
+ this.domBuilder = domFactory.newDocumentBuilder();
+ } catch ( ParserConfigurationException e ) {
+ String errorMsg = new String( "Setting up XML context for reading from the Libvirt XML document failed." );
+ throw new LibvirtXmlDocumentException( errorMsg );
+ }
+
+ // used for XML output
+ try {
+ // use hack to load specific transformer factory implementation for XSLT
+ System.setProperty( TransformerFactory.class.getName(),
+ "org.apache.xalan.processor.TransformerFactoryImpl" );
+
+ // create XML transformer factory to create XML transformer with specific indentation
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+
+ // create XML transformer and apply settings for output XML transformation
+ InputStream xslOutputSchemaStream = LibvirtXmlResources.getLibvirtXsl( "xml-output-transformation.xsl" );
+ StreamSource xslOutputSchema = new StreamSource( xslOutputSchemaStream );
+ this.xmlTransformer = transformerFactory.newTransformer( xslOutputSchema );
+ } catch ( TransformerConfigurationException e ) {
+ String errorMsg = new String( "Setting up XML context for writing to the Libvirt XML document failed." );
+ throw new LibvirtXmlDocumentException( errorMsg );
+ }
+
+ // used for XML validation with RNG schema files
+ if ( rngSchema != null ) {
+ try {
+ this.rngValidator = new LibvirtXmlSchemaValidator( rngSchema );
+ } catch ( SAXException e ) {
+ String errorMsg = new String( "Setting up XML context for validating to the Libvirt XML document failed." );
+ e.printStackTrace();
+ throw new LibvirtXmlDocumentException( errorMsg );
+ }
+ }
+ }
+
+ /**
+ * Creates a Libvirt XML document from a given XML content.
+ *
+ * @param xml XML content as {@link String}.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public LibvirtXmlDocument( String xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ this( xml, null );
+ }
+
+ /**
+ * Creates a Libvirt XML document from a given XML content.
+ *
+ * @param xml XML content as {@link String}.
+ * @param rngSchema RNG schema to validate XML content.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public LibvirtXmlDocument( String xml, Source rngSchema )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ this.createXmlContext( rngSchema );
+ this.fromXml( xml );
+ this.validateXml();
+ }
+
+ /**
+ * Creates a Libvirt XML document from a given XML content.
+ *
+ * @param xml XML content as {@link File}.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public LibvirtXmlDocument( File xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ this( xml, null );
+ }
+
+ /**
+ * Creates a Libvirt XML document from a given XML content.
+ *
+ * @param xml XML content as {@link File}.
+ * @param rngSchema RNG schema to validate XML content.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public LibvirtXmlDocument( File xml, Source rngSchema )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ this.createXmlContext( rngSchema );
+ this.fromXml( xml );
+ this.validateXml();
+ }
+
+ /**
+ * Creates a Libvirt XML document from a given XML content.
+ *
+ * @param xml XML content as {@link InputStream}.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public LibvirtXmlDocument( InputStream xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ this( xml, null );
+ }
+
+ /**
+ * Creates a Libvirt XML document from a given XML content.
+ *
+ * @param xml XML content as {@link InputStream}.
+ * @param rngSchema RNG schema to validate XML content.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public LibvirtXmlDocument( InputStream xml, Source rngSchema )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ this.createXmlContext( rngSchema );
+ this.fromXml( xml );
+ this.validateXml();
+ }
+
+ /**
+ * Creates a Libvirt XML document from a given XML content.
+ *
+ * @param xml XML content as {@link InputSource}.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public LibvirtXmlDocument( InputSource xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ this( xml, null );
+ }
+
+ /**
+ * Creates a Libvirt XML document from a given XML content.
+ *
+ * @param xml XML content as {@link InputSource}.
+ * @param rngSchema RNG schema to validate XML content.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public LibvirtXmlDocument( InputSource xml, Source rngSchema )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ this.createXmlContext( rngSchema );
+ this.fromXml( xml );
+ this.validateXml();
+ }
+
+ /**
+ * Returns the XML root node of the Libvirt XML document.
+ *
+ * @return root node of the Libvirt XML document.
+ */
+ public LibvirtXmlNode getRootXmlNode()
+ {
+ return this.rootXmlNode;
+ }
+
+ @Override
+ public void fromXml( String xml ) throws LibvirtXmlSerializationException
+ {
+ try {
+ this.xmlDocument = this.domBuilder.parse( xml );
+ this.xmlDocument.getDocumentElement().normalize();
+ } catch ( SAXException e ) {
+ e.printStackTrace();
+ } catch ( IOException e ) {
+ e.printStackTrace();
+ }
+
+ this.rootXmlNode = new LibvirtXmlNode( this.xmlDocument, this.xmlDocument.getDocumentElement() );
+ }
+
+ @Override
+ public void fromXml( File xml ) throws LibvirtXmlSerializationException
+ {
+ try {
+ this.xmlDocument = this.domBuilder.parse( xml );
+ this.xmlDocument.getDocumentElement().normalize();
+ } catch ( SAXException e ) {
+ throw new LibvirtXmlSerializationException( e.getLocalizedMessage() );
+ } catch ( IOException e ) {
+ throw new LibvirtXmlSerializationException( e.getLocalizedMessage() );
+ }
+
+ this.rootXmlNode = new LibvirtXmlNode( this.xmlDocument, this.xmlDocument.getDocumentElement() );
+ }
+
+ @Override
+ public void fromXml( InputStream xml ) throws LibvirtXmlSerializationException
+ {
+ try {
+ this.xmlDocument = this.domBuilder.parse( xml );
+ this.xmlDocument.getDocumentElement().normalize();
+ } catch ( SAXException e ) {
+ throw new LibvirtXmlSerializationException( e.getLocalizedMessage() );
+ } catch ( IOException e ) {
+ throw new LibvirtXmlSerializationException( e.getLocalizedMessage() );
+ }
+
+ this.rootXmlNode = new LibvirtXmlNode( this.xmlDocument, this.xmlDocument.getDocumentElement() );
+ }
+
+ @Override
+ public void fromXml( InputSource xml ) throws LibvirtXmlSerializationException
+ {
+ try {
+ this.xmlDocument = this.domBuilder.parse( xml );
+ this.xmlDocument.getDocumentElement().normalize();
+ } catch ( SAXException e ) {
+ throw new LibvirtXmlSerializationException( e.getLocalizedMessage() );
+ } catch ( IOException e ) {
+ throw new LibvirtXmlSerializationException( e.getLocalizedMessage() );
+ }
+
+ this.rootXmlNode = new LibvirtXmlNode( this.xmlDocument, this.xmlDocument.getDocumentElement() );
+ }
+
+ @Override
+ @SuppressWarnings( "deprecation" )
+ public String toXml() throws LibvirtXmlSerializationException
+ {
+ StringWriter xmlWriter = null;
+ String xml = null;
+
+ try {
+ xmlWriter = new StringWriter();
+ DOMSource source = new DOMSource( this.xmlDocument );
+ StreamResult xmlString = new StreamResult( xmlWriter );
+ this.xmlTransformer.transform( source, xmlString );
+ xml = xmlWriter.toString() + System.lineSeparator();
+ xmlWriter.close();
+ } catch ( TransformerException | IOException e ) {
+ throw new LibvirtXmlSerializationException( e.getLocalizedMessage() );
+ } finally {
+ IOUtils.closeQuietly( xmlWriter );
+ }
+
+ return xml;
+ }
+
+ @Override
+ @SuppressWarnings( "deprecation" )
+ public void toXml( File xml ) throws LibvirtXmlSerializationException
+ {
+ FileWriter xmlWriter = null;
+
+ try {
+ xmlWriter = new FileWriter( xml );
+ DOMSource source = new DOMSource( this.xmlDocument );
+ StreamResult xmlStream = new StreamResult( xmlWriter );
+ this.xmlTransformer.transform( source, xmlStream );
+ xmlWriter.append( System.lineSeparator() );
+ xmlWriter.close();
+ } catch ( TransformerException | IOException e ) {
+ throw new LibvirtXmlSerializationException( e.getLocalizedMessage() );
+ } finally {
+ IOUtils.closeQuietly( xmlWriter );
+ }
+ }
+
+ @Override
+ public void validateXml() throws LibvirtXmlValidationException
+ {
+ if ( this.rngValidator != null ) {
+ this.rngValidator.validate( this.xmlDocument );
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ try {
+ return this.toXml();
+ } catch ( LibvirtXmlSerializationException e ) {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocumentException.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocumentException.java
new file mode 100644
index 0000000..f8605ed
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocumentException.java
@@ -0,0 +1,25 @@
+package org.openslx.libvirt.xml;
+
+/**
+ * An exception of a Libvirt XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class LibvirtXmlDocumentException extends Exception
+{
+ /**
+ * Version number for serialization.
+ */
+ private static final long serialVersionUID = -7423926322035713576L;
+
+ /**
+ * Creates an document exception including an error message.
+ *
+ * @param errorMsg message to describe a specific document error.
+ */
+ public LibvirtXmlDocumentException( String errorMsg )
+ {
+ super( errorMsg );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java
new file mode 100644
index 0000000..1ddddce
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java
@@ -0,0 +1,241 @@
+package org.openslx.libvirt.xml;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Editability of XML nodes based on {@link XPath} expressions.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public interface LibvirtXmlEditable
+{
+ /**
+ * Returns XML node selected by a {@link XPath} expression
+ *
+ * @param expression {@link XPath} expression to select XML node.
+ * @return selected XML node.
+ */
+ public Node getXmlNode( String expression );
+
+ /**
+ * Returns XML nodes selected by a {@link XPath} expression
+ *
+ * @param expression {@link XPath} expression to select XML nodes.
+ * @return selected XML nodes.
+ */
+ public NodeList getXmlNodes( String expression );
+
+ /**
+ * Return current XML root element.
+ *
+ * @return current XML root element.
+ */
+ public default Node getXmlElement()
+ {
+ return this.getXmlElement( null );
+ }
+
+ /**
+ * Returns XML element from selection by a {@link XPath} expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @return selected XML element.
+ */
+ public Node getXmlElement( String expression );
+
+ /**
+ * Sets an XML element selected by a {@link XPath} expression.
+ *
+ * If the XML element selected by the given {@link XPath} expression does not exists, the XML
+ * element will be created.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ */
+ public default void setXmlElement( String expression )
+ {
+ this.setXmlElement( expression, null );
+ }
+
+ /**
+ * Sets a XML element selected by a {@link XPath} expression and appends child XML node.
+ *
+ * If the XML element selected by the given {@link XPath} expression does not exists, the XML
+ * element will be created and the given XML child node is appended.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @param child XML node that will be appended to the selected XML element.
+ */
+ public void setXmlElement( String expression, Node child );
+
+ /**
+ * Returns the text value of a XML element selected by a {@link XPath} expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @return Text value of the selected XML element.
+ */
+ public String getXmlElementValue( String expression );
+
+ /**
+ * Sets the text value of a XML element selected by a {@link XPath} expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @param value text value to set selected XML element's text.
+ */
+ public void setXmlElementValue( String expression, String value );
+
+ /**
+ * Removes a XML element and all its childs selected by a {@link XPath} expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ */
+ public void removeXmlElement( String expression );
+
+ /**
+ * Removes all child elements of a XML element selected by a {@link XPath} expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ */
+ public void removeXmlElementChilds( String expression );
+
+ /**
+ * Returns the text value of a XML attribute from the current XML root element.
+ *
+ * @param attributeName name to select XML attribute of the current XML root element.
+ * @return attribute text of the XML attribute from the current XML root element as
+ * {@link String}.
+ */
+ public default String getXmlElementAttributeValue( String attributeName )
+ {
+ return this.getXmlElementAttributeValue( null, attributeName );
+ }
+
+ /**
+ * Returns the binary choice of a XML attribute from the current XML root element.
+ *
+ * If the text value of the XML attribute equals to yes, the returned {@link boolean}
+ * value is set to true. Otherwise, if the text value of the XML attribute equals to
+ * no, the returned {@link boolean} value is set to false.
+ *
+ * @param attributeName name to select XML attribute of the current XML root element.
+ * @return attribute value of the XML attribute from the current XML root element as
+ * {@link boolean}.
+ */
+ public default boolean getXmlElementAttributeValueAsBool( String attributeName )
+ {
+ return "yes".equals( this.getXmlElementAttributeValue( attributeName ) );
+ }
+
+ /**
+ * Returns the binary choice of a XML attribute from a XML element selected by a
+ * {@link XPath}expression.
+ *
+ * If the text value of the XML attribute equals to yes, the returned {@link boolean}
+ * value is set to true. Otherwise, if the text value of the XML attribute equals to
+ * no, the returned {@link boolean} value is set to false.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @param attributeName name to select XML attribute of the current XML root element.
+ * @return attribute value of the XML attribute from the current XML root element as
+ * {@link boolean}.
+ */
+ public default boolean getXmlElementAttributeValueAsBool( String expression, String attributeName )
+ {
+ return "yes".equals( this.getXmlElementAttributeValue( expression, attributeName ) );
+ }
+
+ /**
+ * Returns the text value of a XML attribute from a XML element selected by a
+ * {@link XPath}expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @param attributeName name to select XML attribute of the selected XML element.
+ * @return attribute text of the XML attribute from the selected XML element.
+ */
+ public String getXmlElementAttributeValue( String expression, String attributeName );
+
+ /**
+ * Sets the text value of a XML attribute from the current XML root element.
+ *
+ * @param attributeName name to select XML attribute of the current XML root element.
+ * @param value XML attribute value for the selected XML attribute from the current XML root
+ * element.
+ */
+ public default void setXmlElementAttributeValue( String attributeName, String value )
+ {
+ this.setXmlElementAttributeValue( null, attributeName, value );
+ }
+
+ /**
+ * Sets the binary choice value of a XML attribute from the current XML root element.
+ *
+ * If the binary choice value for the XML attribute equals to true, the text value of the
+ * selected XML attribute is set to yes. Otherwise, if the binary choice value for the
+ * selected XML attribute equals to false, the text value of the selected XML attribute is
+ * set to no.
+ *
+ * @param attributeName name to select XML attribute of the selected XML element.
+ * @param value binary choice value for the selected XML attribute from the selected XML element.
+ */
+ public default void setXmlElementAttributeValue( String attributeName, boolean value )
+ {
+ final String valueYesNo = value ? "yes" : "no";
+ this.setXmlElementAttributeValue( attributeName, valueYesNo );
+ }
+
+ /**
+ * Sets the binary choice value of a XML attribute from a XML element selected by a
+ * {@link XPath} expression.
+ *
+ * If the binary choice value for the XML attribute equals to true, the text value of the
+ * selected XML attribute is set to yes. Otherwise, if the binary choice value for the
+ * selected XML attribute equals to false, the text value of the selected XML attribute is
+ * set to no.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @param attributeName name to select XML attribute of the selected XML element.
+ * @param value binary choice value for the selected XML attribute from the selected XML element.
+ */
+ public default void setXmlElementAttributeValue( String expression, String attributeName, boolean value )
+ {
+ final String valueYesNo = value ? "yes" : "no";
+ this.setXmlElementAttributeValue( expression, attributeName, valueYesNo );
+ }
+
+ /**
+ * Sets the text value of a XML attribute from a XML element selected by a
+ * {@link XPath} expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @param attributeName name to select XML attribute of the selected XML element.
+ * @param value XML attribute value for the selected XML attribute from the selected XML element.
+ */
+ public void setXmlElementAttributeValue( String expression, String attributeName, String value );
+
+ /**
+ * Removes an XML attribute from the current XML root element.
+ *
+ * @param attributeName name of the attribute which should be deleted.
+ */
+ public default void removeXmlElementAttribute( String attributeName )
+ {
+ this.removeXmlElementAttribute( null, attributeName );
+ }
+
+ /**
+ * Removes an XML attribute from a XML element selected by a {@link XPath} expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ * @param attributeName name of the attribute which should be deleted.
+ */
+ public void removeXmlElementAttribute( String expression, String attributeName );
+
+ /**
+ * Removes all XML attributes from a XML element selected by a {@link XPath} expression.
+ *
+ * @param expression {@link XPath} expression to select XML element.
+ */
+ public void removeXmlElementAttributes( String expression );
+
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java
new file mode 100644
index 0000000..93e28de
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java
@@ -0,0 +1,356 @@
+package org.openslx.libvirt.xml;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A representation of a XML node as part of a {@link LibvirtXMLDocument}.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class LibvirtXmlNode implements LibvirtXmlCreatable, LibvirtXmlEditable
+{
+ /**
+ * Separation character for internal {@link XPath} expressions.
+ */
+ private static final String XPATH_EXPRESSION_SEPARATOR = "/";
+
+ /**
+ * Current XML node selection character for internal {@link XPath} expressions.
+ */
+ private static final String XPATH_EXPRESSION_CURRENT_NODE = ".";
+
+ /**
+ * Factory to create {@link XPath} objects.
+ */
+ private XPathFactory xPathFactory = null;
+
+ /**
+ * Representation of the XML document, in which this {@link LibvirtXmlNode} is part of.
+ */
+ private Document xmlDocument = null;
+
+ /**
+ * Current XML base node as XML root anchor for relative internal {@link XPath} expressions.
+ */
+ private Node xmlBaseNode = null;
+
+ /**
+ * Create and initialize {@link XPath} context to define and compile custom {@link XPath}
+ * expressions.
+ */
+ private void createXPathContext()
+ {
+ this.xPathFactory = XPathFactory.newInstance();
+ }
+
+ /**
+ * Creates empty Libvirt XML node, which does not belong to any XML document and does not specify
+ * any XML base node.
+ *
+ * @implNote Please call {@link LibvirtXmlNode#setXmlDocument(Document)} and
+ * {@link LibvirtXmlNode#setXmlBaseNode(Node)} manually to obtain a functional Libvirt
+ * XML node.
+ */
+ public LibvirtXmlNode()
+ {
+ this( null, null );
+ }
+
+ /**
+ * Creates Libvirt XML node from a existing Libvirt XML node by reference.
+ *
+ * @param xmlNode existing Libvirt XML node.
+ */
+ public LibvirtXmlNode( LibvirtXmlNode xmlNode )
+ {
+ this( xmlNode.getXmlDocument(), xmlNode.getXmlBaseNode() );
+ }
+
+ /**
+ * Creates Libvirt XML node as part of a existing XML document.
+ *
+ * @param xmlDocument existing XML document.
+ *
+ * @implNote Please call {@link LibvirtXmlNode#setXmlBaseNode(Node)} manually to obtain a
+ * functional Libvirt XML node.
+ */
+ public LibvirtXmlNode( Document xmlDocument )
+ {
+ this( xmlDocument, null );
+ }
+
+ /**
+ * Creates Libvirt XML node with a specific XML base node.
+ *
+ * @param xmlBaseNode existing XML base node.
+ *
+ * @implNote Please call {@link LibvirtXmlNode#setXmlDocument(Document)} manually to obtain a
+ * functional Libvirt XML node.
+ */
+ public LibvirtXmlNode( Node xmlBaseNode )
+ {
+ this( null, xmlBaseNode );
+ }
+
+ /**
+ * Creates Libvirt XML node with a specific XML base node as part of a XML document.
+ *
+ * @param xmlDocument existing XML document.
+ * @param xmlBaseNode existing XML base node.
+ */
+ public LibvirtXmlNode( Document xmlDocument, Node xmlBaseNode )
+ {
+ this.createXPathContext();
+
+ this.setXmlDocument( xmlDocument );
+ this.setXmlBaseNode( xmlBaseNode );
+ }
+
+ /**
+ * Returns referenced XML document.
+ *
+ * @return referenced XML document.
+ */
+ public Document getXmlDocument()
+ {
+ return this.xmlDocument;
+ }
+
+ /**
+ * Sets existing XML document for Libvirt XML node.
+ *
+ * @param xmlDocument existing XML document.
+ */
+ public void setXmlDocument( Document xmlDocument )
+ {
+ this.xmlDocument = xmlDocument;
+ }
+
+ /**
+ * Returns current XML base node.
+ *
+ * @return current XML base node as XML root anchor of relative internal {@link XPath}
+ * expressions.
+ */
+ public Node getXmlBaseNode()
+ {
+ return this.xmlBaseNode;
+ }
+
+ /**
+ * Sets existing XML base node for Libvirt XML node.
+ *
+ * @param xmlBaseNode existing XML base node as XML root anchor for relative internal
+ * {@link XPath} expressions.
+ */
+ public void setXmlBaseNode( Node xmlBaseNode )
+ {
+ this.xmlBaseNode = xmlBaseNode;
+ }
+
+ @Override
+ public Node getXmlNode( String expression )
+ {
+ NodeList nodes = this.getXmlNodes( expression );
+ return nodes.item( 0 );
+ }
+
+ @Override
+ public NodeList getXmlNodes( String expression )
+ {
+ Object nodes = null;
+
+ try {
+ XPath xPath = this.xPathFactory.newXPath();
+ XPathExpression xPathExpr = xPath.compile( expression );
+ nodes = xPathExpr.evaluate( this.xmlBaseNode, XPathConstants.NODESET );
+ } catch ( XPathExpressionException e ) {
+ e.printStackTrace();
+ }
+
+ return NodeList.class.cast( nodes );
+ }
+
+ @Override
+ public Node getXmlElement( String expression )
+ {
+ String completeExpression = null;
+
+ if ( expression == null ) {
+ completeExpression = XPATH_EXPRESSION_CURRENT_NODE;
+ } else if ( expression.isEmpty() ) {
+ completeExpression = XPATH_EXPRESSION_CURRENT_NODE;
+ } else {
+ completeExpression = XPATH_EXPRESSION_CURRENT_NODE + XPATH_EXPRESSION_SEPARATOR + expression;
+ }
+
+ Node node = this.getXmlNode( completeExpression );
+
+ if ( node != null && node.getNodeType() == Node.ELEMENT_NODE ) {
+ return node;
+ } else {
+ return null;
+ }
+ }
+
+ private Node createXmlElement( String expression )
+ {
+ Node parentNode = this.xmlBaseNode;
+ Node currentNode = parentNode;
+
+ if ( expression != null && !expression.isEmpty() ) {
+ String[] nodeNames = expression.split( XPATH_EXPRESSION_SEPARATOR );
+ String partialExpression = XPATH_EXPRESSION_CURRENT_NODE;
+
+ for ( int i = 0; i < nodeNames.length; i++ ) {
+ partialExpression += XPATH_EXPRESSION_SEPARATOR + nodeNames[i];
+ currentNode = this.getXmlNode( partialExpression );
+
+ if ( currentNode == null ) {
+ parentNode.appendChild( this.xmlDocument.createElement( nodeNames[i] ) );
+ currentNode = parentNode.getLastChild();
+ }
+
+ parentNode = currentNode;
+ }
+ }
+
+ return currentNode;
+ }
+
+ @Override
+ public void setXmlElement( String expression, Node child )
+ {
+ Node node = this.createXmlElement( expression );
+
+ if ( child != null ) {
+ node.appendChild( child );
+ }
+ }
+
+ @Override
+ public String getXmlElementValue( String expression )
+ {
+ Node node = this.getXmlElement( expression );
+
+ if ( node != null ) {
+ return node.getTextContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void setXmlElementValue( String expression, String value )
+ {
+ Node node = this.createXmlElement( expression );
+ node.setTextContent( value );
+ }
+
+ @Override
+ public void removeXmlElement( String expression )
+ {
+ Node node = this.getXmlElement( expression );
+
+ if ( node != null ) {
+ node.getParentNode().removeChild( node );
+ }
+ }
+
+ @Override
+ public void removeXmlElementChilds( String expression )
+ {
+ Node node = this.getXmlElement( expression );
+
+ if ( node != null ) {
+ for ( int i = 0; i < node.getChildNodes().getLength(); i++ ) {
+ Node child = node.getChildNodes().item( 0 );
+ node.removeChild( child );
+ }
+ }
+ }
+
+ @Override
+ public String getXmlElementAttributeValue( String expression, String attributeName )
+ {
+ Node node = null;
+
+ if ( expression != null && !expression.isEmpty() ) {
+ node = this.getXmlElement( expression );
+ } else {
+ node = this.xmlBaseNode;
+ }
+
+ if ( node == null ) {
+ return null;
+ } else {
+ Node attribute = node.getAttributes().getNamedItem( attributeName );
+
+ if ( attribute == null ) {
+ return null;
+ } else {
+ return attribute.getNodeValue();
+ }
+ }
+ }
+
+ @Override
+ public void setXmlElementAttributeValue( String expression, String attributeName, String value )
+ {
+ Node node = this.createXmlElement( expression );
+ Node attribute = node.getAttributes().getNamedItem( attributeName );
+
+ if ( attribute == null ) {
+ Element element = Element.class.cast( node );
+ element.setAttribute( attributeName, value );
+ } else {
+ attribute.setNodeValue( value );
+ }
+ }
+
+ @Override
+ public void removeXmlElementAttribute( String expression, String attributeName )
+ {
+ Node node = this.getXmlElement( expression );
+
+ if ( node != null ) {
+ Node attribute = node.getAttributes().getNamedItem( attributeName );
+ node.getAttributes().removeNamedItem( attribute.getNodeName() );
+ }
+ }
+
+ @Override
+ public void removeXmlElementAttributes( String expression )
+ {
+ Node node = this.getXmlElement( expression );
+
+ if ( node != null ) {
+ for ( int i = 0; i < node.getAttributes().getLength(); i++ ) {
+ Node attribute = node.getAttributes().item( 0 );
+ node.getAttributes().removeNamedItem( attribute.getNodeName() );
+ }
+ }
+ }
+
+ @Override
+ public void fromXmlNode( Node xmlNode )
+ {
+ this.setXmlBaseNode( xmlNode );
+ }
+
+ @Override
+ public Node toXmlNode()
+ {
+ return this.getXmlBaseNode();
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlResources.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlResources.java
new file mode 100644
index 0000000..38c818b
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlResources.java
@@ -0,0 +1,52 @@
+package org.openslx.libvirt.xml;
+
+import java.io.File;
+import java.io.InputStream;
+
+/**
+ * Collection of resource utils for a Libvirt XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public final class LibvirtXmlResources
+{
+ /**
+ * File path prefix of the absolute path to the libvirt resource folder in a *.jar file.
+ */
+ private static final String LIBVIRT_PREFIX_PATH = File.separator + "libvirt";
+
+ /**
+ * File path prefix of the absolute path to the libvirt XSL resource folder in a *.jar file.
+ */
+ private static final String LIBVIRT_PREFIX_PATH_XSL = LIBVIRT_PREFIX_PATH + File.separator + "xsl";
+
+ /**
+ * File path prefix of the absolute path to the libvirt RNG resource folder in a *.jar file.
+ */
+ private static final String LIBVIRT_PREFIX_PATH_RNG = LIBVIRT_PREFIX_PATH + File.separator + "rng";
+
+ /**
+ * Returns a Libvirt XSL resource as stream.
+ *
+ * @param libvirtXslFileName file name of the XSL resource in the resources *.jar folder.
+ * @return Libvirt XSL resource as stream.
+ */
+ public static InputStream getLibvirtXsl( String libvirtXslFileName )
+ {
+ String libvirtXslPath = LibvirtXmlResources.LIBVIRT_PREFIX_PATH_XSL + File.separator + libvirtXslFileName;
+ return LibvirtXmlResources.class.getResourceAsStream( libvirtXslPath );
+ }
+
+ /**
+ * Returns a Libvirt RNG schema resource as stream.
+ *
+ * @param libvirtRngFileName file name of the RNG schema resource in the resources *.jar folder.
+ * @return Libvirt RNG schema resource as stream.
+ */
+ public static InputStream getLibvirtRng( String libvirtRngFileName )
+ {
+ String libvirtRngPath = LibvirtXmlResources.LIBVIRT_PREFIX_PATH_RNG + File.separator + libvirtRngFileName;
+ return LibvirtXmlResources.class.getResourceAsStream( libvirtRngPath );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSchemaValidator.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSchemaValidator.java
new file mode 100644
index 0000000..01e8adb
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSchemaValidator.java
@@ -0,0 +1,283 @@
+package org.openslx.libvirt.xml;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import javax.xml.XMLConstants;
+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.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSResourceResolver;
+import org.xml.sax.SAXException;
+
+/**
+ * Resource resolver input for RelaxNG schemas.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+class LibvirtXmlSchemaResourceInput implements LSInput
+{
+ /**
+ * Stores the public identification of the schema resource.
+ */
+ private String publicId;
+
+ /**
+ * Stores the system identification of the schema resource.
+ */
+ private String systemId;
+
+ /**
+ * Stream to process and read the schema resource.
+ */
+ private BufferedInputStream inputStream;
+
+ /**
+ * Creates a resource resolver input for a RelaxNG schema.
+ *
+ * @param publicId public identification of the schema resource.
+ * @param sysId system identification of the schema resource.
+ * @param input stream of the schema resource.
+ */
+ public LibvirtXmlSchemaResourceInput( String publicId, String sysId, InputStream input )
+ {
+ this.publicId = publicId;
+ this.systemId = sysId;
+ this.inputStream = new BufferedInputStream( input );
+ }
+
+ @Override
+ public String getBaseURI()
+ {
+ return null;
+ }
+
+ @Override
+ public InputStream getByteStream()
+ {
+ return null;
+ }
+
+ @Override
+ public boolean getCertifiedText()
+ {
+ return false;
+ }
+
+ @Override
+ public Reader getCharacterStream()
+ {
+ return null;
+ }
+
+ @Override
+ public String getEncoding()
+ {
+ return null;
+ }
+
+ @Override
+ public String getPublicId()
+ {
+ return this.publicId;
+ }
+
+ @Override
+ public String getStringData()
+ {
+ String data = null;
+
+ synchronized ( this.inputStream ) {
+ try {
+ int inputLength = this.inputStream.available();
+ byte[] input = new byte[ inputLength ];
+ this.inputStream.read( input );
+ data = new String( input );
+ } catch ( IOException e ) {
+ e.printStackTrace();
+ }
+ }
+
+ return data;
+ }
+
+ @Override
+ public String getSystemId()
+ {
+ return this.systemId;
+ }
+
+ @Override
+ public void setBaseURI( String arg0 )
+ {
+ }
+
+ @Override
+ public void setByteStream( InputStream arg0 )
+ {
+ }
+
+ @Override
+ public void setCertifiedText( boolean arg0 )
+ {
+ }
+
+ @Override
+ public void setCharacterStream( Reader arg0 )
+ {
+ }
+
+ @Override
+ public void setEncoding( String arg0 )
+ {
+ }
+
+ @Override
+ public void setPublicId( String arg0 )
+ {
+ this.publicId = arg0;
+ }
+
+ @Override
+ public void setStringData( String arg0 )
+ {
+ }
+
+ @Override
+ public void setSystemId( String arg0 )
+ {
+ this.systemId = arg0;
+ }
+}
+
+/**
+ * Resource resolver for RelaxNG schemas.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+class LibvirtXmlSchemaResourceResolver implements LSResourceResolver
+{
+ @Override
+ public LSInput resolveResource( String type, String namespaceURI, String publicId, String systemId, String baseURI )
+ {
+ InputStream rngResourceStream = LibvirtXmlResources.getLibvirtRng( systemId );
+ return new LibvirtXmlSchemaResourceInput( publicId, systemId, rngResourceStream );
+ }
+}
+
+/**
+ * Validator for validation of Libvirt XML documents with RelaxNG schemas.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class LibvirtXmlSchemaValidator
+{
+ /**
+ * RelaxNG based validator for validation of Libvirt XML documents.
+ */
+ private Validator rngSchemaValidator;
+
+ /**
+ * Creates a validator for validation of Libvirt XML documents with RelaxNG schemas.
+ *
+ * @param rngSchema
+ * @throws SAXException
+ */
+ public LibvirtXmlSchemaValidator( Source rngSchema ) throws SAXException
+ {
+ this.createValidationContext( rngSchema );
+ }
+
+ /**
+ * Creates context for validation of Libvirt XML documents with a RelaxNG schema.
+ *
+ * @param rngSchema RelaxNG schema used for validation with {@link #validate(Document)}.
+ *
+ * @throws SAXException Loading, creation and processing of rngSchema has failed.
+ */
+ private void createValidationContext( Source rngSchema ) throws SAXException
+ {
+ // use hack to load specific schema factory implementation for RelaxNG schemas
+ System.setProperty( SchemaFactory.class.getName() + ":" + XMLConstants.RELAXNG_NS_URI,
+ "com.thaiopensource.relaxng.jaxp.XMLSyntaxSchemaFactory" );
+
+ // create schema resource resolver to resolve schema resources during parsing and validation
+ LibvirtXmlSchemaResourceResolver schemaResolver = new LibvirtXmlSchemaResourceResolver();
+
+ // create schema factory to be able to create a RelaxNG schema validator
+ SchemaFactory factory = SchemaFactory.newInstance( XMLConstants.RELAXNG_NS_URI );
+ factory.setResourceResolver( schemaResolver );
+ Schema schema = factory.newSchema( rngSchema );
+
+ // create the RelaxNG schema validator
+ this.rngSchemaValidator = schema.newValidator();
+ this.rngSchemaValidator.setResourceResolver( schemaResolver );
+ }
+
+ /**
+ * Transforms a DOM source to a Stream source.
+ *
+ * @param domSource DOM source of a Libvirt XML document.
+ * @return Stream source of a Libvirt XML document.
+ *
+ * @throws TransformerException Transformation of DOM source to a Stream source has failed.
+ *
+ * @implNote This utility method is necessary in {@link #validate(Document)} to be able to
+ * validate a DOM Libvirt XML document with the schema and validator implementation for
+ * RelaxNG schema files from
+ * com.thaiopensource.relaxng.jaxp.XMLSyntaxSchemaFactory.
+ */
+ private static StreamSource toStreamSource( DOMSource domSource ) throws TransformerException
+ {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ StreamResult result = new StreamResult( outputStream );
+
+ // create identity transformer
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.transform( domSource, result );
+
+ ByteArrayInputStream inputStream = new ByteArrayInputStream( outputStream.toByteArray() );
+ return new StreamSource( inputStream );
+ }
+
+ /**
+ * Validates a given (and parsed) DOM Libvirt XML document.
+ *
+ * Validation takes place if the specified xmlDocument is non-null, otherwise the
+ * validation succeeds immediately. If the validation of the xmlDocument fails, a
+ * validation exception is thrown.
+ *
+ * @param xmlDocument Libvirt XML document.
+ *
+ * @throws LibvirtXmlValidationException Validation of Libvirt XML document failed.
+ */
+ public void validate( Document xmlDocument ) throws LibvirtXmlValidationException
+ {
+ if ( xmlDocument != null ) {
+ try {
+ DOMSource domSource = new DOMSource( xmlDocument );
+ StreamSource source = LibvirtXmlSchemaValidator.toStreamSource( domSource );
+ this.rngSchemaValidator.validate( source );
+ } catch ( SAXException | TransformerException | IOException e ) {
+ throw new LibvirtXmlValidationException( e.getLocalizedMessage() );
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializable.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializable.java
new file mode 100644
index 0000000..6f11ce5
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializable.java
@@ -0,0 +1,57 @@
+package org.openslx.libvirt.xml;
+
+import java.io.File;
+import java.io.InputStream;
+
+import org.xml.sax.InputSource;
+
+/**
+ * Serializability of a Libvirt XML document from/to a XML file.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public abstract interface LibvirtXmlSerializable
+{
+ /**
+ * Serialize Libvirt XML document from {@link String}.
+ *
+ * @param xml {@link String} containing XML content.
+ */
+ public void fromXml( String xml ) throws LibvirtXmlSerializationException;
+
+ /**
+ * Serialize Libvirt XML document from {@link File}.
+ *
+ * @param xml {@link File} containing XML content.
+ */
+ public void fromXml( File xml ) throws LibvirtXmlSerializationException;
+
+ /**
+ * Serialize Libvirt XML document from {@link InputStream}.
+ *
+ * @param xml {@link InputStream} providing XML content.
+ */
+ void fromXml( InputStream xml ) throws LibvirtXmlSerializationException;
+
+ /**
+ * Serialize Libvirt XML document from {@link InputSource}.
+ *
+ * @param xml {@link InputSource} providing XML content.
+ */
+ public void fromXml( InputSource xml ) throws LibvirtXmlSerializationException;
+
+ /**
+ * Serialize Libvirt XML document to {@link String}.
+ *
+ * @return XML {@link String} containing Libvirt XML document content.
+ */
+ public String toXml() throws LibvirtXmlSerializationException;
+
+ /**
+ * Serialize Libvirt XML document to {@link File}.
+ *
+ * @param xml XML {@link File} containing Libvirt XML document content.
+ */
+ public void toXml( File xml ) throws LibvirtXmlSerializationException;
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializationException.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializationException.java
new file mode 100644
index 0000000..d522107
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializationException.java
@@ -0,0 +1,25 @@
+package org.openslx.libvirt.xml;
+
+/**
+ * An exception of an serialization error during Libvirt XML serialization.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class LibvirtXmlSerializationException extends Exception
+{
+ /**
+ * Version number for serialization.
+ */
+ private static final long serialVersionUID = 7995955592221349949L;
+
+ /**
+ * Creates a XML serialization exception including an error message.
+ *
+ * @param errorMsg message to describe a specific XML serialization error.
+ */
+ public LibvirtXmlSerializationException( String errorMsg )
+ {
+ super( errorMsg );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidatable.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidatable.java
new file mode 100644
index 0000000..8574cc4
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidatable.java
@@ -0,0 +1,18 @@
+package org.openslx.libvirt.xml;
+
+/**
+ * Validatability of Libvirt XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public abstract interface LibvirtXmlValidatable
+{
+ /**
+ * Validates the XML document's content and report error if document is not a valid Libvirt XML
+ * document.
+ *
+ * @throws LibvirtXmlValidationException XML content is not a valid Libvirt XML.
+ */
+ public void validateXml() throws LibvirtXmlValidationException;
+}
diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidationException.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidationException.java
new file mode 100644
index 0000000..24e9db7
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidationException.java
@@ -0,0 +1,25 @@
+package org.openslx.libvirt.xml;
+
+/**
+ * An exception of an unsuccessful Libvirt XML validation.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class LibvirtXmlValidationException extends Exception
+{
+ /**
+ * Version number for serialization.
+ */
+ private static final long serialVersionUID = 2299967599483742777L;
+
+ /**
+ * Creates a validation exception including an error message.
+ *
+ * @param errorMsg message to describe a specific Libvirt XML error.
+ */
+ public LibvirtXmlValidationException( String errorMsg )
+ {
+ super( errorMsg );
+ }
+}
diff --git a/src/test/java/org/openslx/libvirt/xml/LibvirtXmlDocumentTest.java b/src/test/java/org/openslx/libvirt/xml/LibvirtXmlDocumentTest.java
new file mode 100644
index 0000000..1b6e5a5
--- /dev/null
+++ b/src/test/java/org/openslx/libvirt/xml/LibvirtXmlDocumentTest.java
@@ -0,0 +1,262 @@
+package org.openslx.libvirt.xml;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+class LibvirtXmlDocumentStub extends LibvirtXmlDocument
+{
+ public LibvirtXmlDocumentStub( File xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ super( xml );
+ }
+
+ public LibvirtXmlDocumentStub( File xml, Source rngSchema )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ super( xml, rngSchema );
+ }
+}
+
+public class LibvirtXmlDocumentTest
+{
+ private static final String EMPTY = new String();
+
+ @BeforeAll
+ public static void setUp()
+ {
+ // disable logging with log4j
+ LogManager.getRootLogger().setLevel( Level.OFF );
+ }
+
+ private LibvirtXmlDocument newLibvirtXmlDocumentInstance( String xmlFileName )
+ {
+ LibvirtXmlDocument document = null;
+
+ try {
+ File xmlFile = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ document = new LibvirtXmlDocumentStub( xmlFile );
+ } catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) {
+ String errorMsg = new String( "Cannot prepare requested Libvirt XML file from the resources folder" );
+ fail( errorMsg );
+ }
+
+ return document;
+ }
+
+ private LibvirtXmlDocument newLibvirtXmlDocumentValidationInstance( String xmlFileName, String rngSchemaFileName )
+ throws LibvirtXmlValidationException
+ {
+ LibvirtXmlDocument document = null;
+
+ try {
+ File xmlFile = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ Source rngSchemaSource = new StreamSource( LibvirtXmlResources.getLibvirtRng( rngSchemaFileName ) );
+ document = new LibvirtXmlDocumentStub( xmlFile, rngSchemaSource );
+ } catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException e ) {
+ String errorMsg = new String( "Cannot prepare requested Libvirt XML file from the resources folder" );
+ fail( errorMsg );
+ }
+
+ return document;
+ }
+
+ @Test
+ @DisplayName( "Read libvirt XML file to String" )
+ public void testReadXmlFileToString() throws LibvirtXmlSerializationException, IOException
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ File originalXmlFile = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+
+ final String readXmlContent = vm.toXml();
+ final String originalXmlContent = FileUtils.readFileToString( originalXmlFile, StandardCharsets.UTF_8 );
+
+ assertNotNull( readXmlContent );
+
+ final int lengthReadXmlContent = readXmlContent.split( System.lineSeparator() ).length;
+ final int lengthOriginalXmlContent = originalXmlContent.split( System.lineSeparator() ).length;
+
+ assertEquals( lengthOriginalXmlContent, lengthReadXmlContent );
+ }
+
+ @Test
+ @DisplayName( "Read libvirt XML file to file" )
+ public void testReadXmlFileToFile() throws LibvirtXmlSerializationException, IOException
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ File originalXmlFile = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ File readXmlFile = LibvirtXmlTestResources.createLibvirtXmlTempFile();
+
+ vm.toXml( readXmlFile );
+
+ final String readXmlContent = FileUtils.readFileToString( readXmlFile, StandardCharsets.UTF_8 );
+ final String originalXmlContent = FileUtils.readFileToString( originalXmlFile, StandardCharsets.UTF_8 );
+
+ assertNotNull( readXmlContent );
+
+ final int lengthReadXmlContent = readXmlContent.split( System.lineSeparator() ).length;
+ final int lengthOriginalXmlContent = originalXmlContent.split( System.lineSeparator() ).length;
+
+ assertEquals( lengthOriginalXmlContent, lengthReadXmlContent );
+ }
+
+ @Test
+ @DisplayName( "Validate correct libvirt XML file" )
+ public void testValidateCorrectXmlFile()
+ {
+ Executable validateXmlDocument = () -> {
+ this.newLibvirtXmlDocumentValidationInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml", "domain.rng" );
+ };
+
+ assertDoesNotThrow( validateXmlDocument );
+ }
+
+ @Test
+ @DisplayName( "Validate incorrect libvirt XML file" )
+ public void testValidateIncorrectXmlFile()
+ {
+ Executable validateXmlDocument = () -> {
+ this.newLibvirtXmlDocumentValidationInstance( "qemu-kvm_default-ubuntu-20-04-vm-invalid.xml", "domain.rng" );
+ };
+
+ assertThrows( LibvirtXmlValidationException.class, validateXmlDocument );
+ }
+
+ @Test
+ @DisplayName( "Get non-existent node from libvirt XML file" )
+ public void testGetNonExistentElement()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertNull( vm.getRootXmlNode().getXmlElement( "info" ) );
+ }
+
+ @Test
+ @DisplayName( "Set non-existent node in libvirt XML file" )
+ public void testSetNonExistentElement()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.getRootXmlNode().setXmlElement( "info" );
+ assertNotNull( vm.getRootXmlNode().getXmlElement( "info" ) );
+ }
+
+ @Test
+ @DisplayName( "Get non-existent element's value in libvirt XML file" )
+ public void testGetNonExistentElementValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertNull( vm.getRootXmlNode().getXmlElementValue( "info" ) );
+ }
+
+ @Test
+ @DisplayName( "Set non-existent element's value in libvirt XML file" )
+ public void testSetNonExistentElementValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.getRootXmlNode().setXmlElementValue( "info", "content" );
+ assertEquals( "content", vm.getRootXmlNode().getXmlElementValue( "info" ) );
+ }
+
+ @Test
+ @DisplayName( "Get empty element from libvirt XML file" )
+ public void testGetEmptyElement()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertNotNull( vm.getRootXmlNode().getXmlElement( "features/acpi" ) );
+ }
+
+ @Test
+ @DisplayName( "Set empty element in libvirt XML file" )
+ public void testSetEmptyElement()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.getRootXmlNode().setXmlElement( "features/acpi" );
+ assertNotNull( vm.getRootXmlNode().getXmlElement( "features/acpi" ) );
+ }
+
+ @Test
+ @DisplayName( "Get empty element's value from libvirt XML file" )
+ public void testGetEmptyElementValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( EMPTY, vm.getRootXmlNode().getXmlElementValue( "features/acpi" ) );
+ }
+
+ @Test
+ @DisplayName( "Set empty element's value in libvirt XML file" )
+ public void testSetEmptyElementValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.getRootXmlNode().setXmlElementValue( "features/acpi", "content" );
+ assertEquals( "content", vm.getRootXmlNode().getXmlElementValue( "features/acpi" ) );
+ }
+
+ @Test
+ @DisplayName( "Get non-existent element's attribute value from libvirt XML file" )
+ public void testGetNonExistentElementAttributeValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertNull( vm.getRootXmlNode().getXmlElementAttributeValue( "info", "test" ) );
+ }
+
+ @Test
+ @DisplayName( "Set non-existent element's attribute value from libvirt XML file" )
+ public void testSetNonExistentElementAttributeValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.getRootXmlNode().setXmlElementAttributeValue( "info", "test", "info" );
+ assertEquals( "info", vm.getRootXmlNode().getXmlElementAttributeValue( "info", "test" ) );
+ }
+
+ @Test
+ @DisplayName( "Get element's non-existent attribute value from libvirt XML file" )
+ public void testGetElementNonExistentAttributeValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertNull( vm.getRootXmlNode().getXmlElementAttributeValue( "features/acpi", "test" ) );
+ }
+
+ @Test
+ @DisplayName( "Set element's non-existent attribute value from libvirt XML file" )
+ public void testSetElementNonExistentAttributeValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.getRootXmlNode().setXmlElementAttributeValue( "features/acpi", "test", "info" );
+ assertEquals( "info", vm.getRootXmlNode().getXmlElementAttributeValue( "features/acpi", "test" ) );
+ }
+
+ @Test
+ @DisplayName( "Get element's attribute value from libvirt XML file" )
+ public void testGetElementAttributeValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( "partial", vm.getRootXmlNode().getXmlElementAttributeValue( "cpu", "check" ) );
+ }
+
+ @Test
+ @DisplayName( "Set element's attribute value from libvirt XML file" )
+ public void testSetElementAttributeValue()
+ {
+ LibvirtXmlDocument vm = this.newLibvirtXmlDocumentInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.getRootXmlNode().setXmlElementAttributeValue( "cpu", "check", "full" );
+ assertEquals( "full", vm.getRootXmlNode().getXmlElementAttributeValue( "cpu", "check" ) );
+ }
+}
diff --git a/src/test/java/org/openslx/libvirt/xml/LibvirtXmlTestResources.java b/src/test/java/org/openslx/libvirt/xml/LibvirtXmlTestResources.java
new file mode 100644
index 0000000..6cc0360
--- /dev/null
+++ b/src/test/java/org/openslx/libvirt/xml/LibvirtXmlTestResources.java
@@ -0,0 +1,29 @@
+package org.openslx.libvirt.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+public final class LibvirtXmlTestResources
+{
+ private static final String LIBVIRT_PREFIX_PATH = File.separator + "libvirt";
+ private static final String LIBVIRT_PREFIX_PATH_XML = LIBVIRT_PREFIX_PATH + File.separator + "xml";
+
+ private static final String LIBVIRT_TEMP_PREFIX = "libvirt-";
+ private static final String LIBVIRT_TEMP_SUFFIX = ".xml";
+
+ public static File getLibvirtXmlFile( String libvirtXmlFileName )
+ {
+ String libvirtXmlPath = LibvirtXmlTestResources.LIBVIRT_PREFIX_PATH_XML + File.separator + libvirtXmlFileName;
+ URL libvirtXml = LibvirtXmlTestResources.class.getResource( libvirtXmlPath );
+ return new File( libvirtXml.getFile() );
+ }
+
+ public static File createLibvirtXmlTempFile() throws IOException
+ {
+ File tempFile = File.createTempFile( LibvirtXmlTestResources.LIBVIRT_TEMP_PREFIX,
+ LibvirtXmlTestResources.LIBVIRT_TEMP_SUFFIX );
+ tempFile.deleteOnExit();
+ return tempFile;
+ }
+}
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-cdrom.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-cdrom.xml
new file mode 100644
index 0000000..617e20b
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-cdrom.xml
@@ -0,0 +1,144 @@
+
+ archlinux
+ 22bbd81f-b31b-4242-9907-8840844944bf
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-floppy.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-floppy.xml
new file mode 100644
index 0000000..8a2e316
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-floppy.xml
@@ -0,0 +1,145 @@
+
+ archlinux
+ 22bbd81f-b31b-4242-9907-8840844944bf
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
+
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-hdd.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-hdd.xml
new file mode 100644
index 0000000..9fe998e
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-hdd.xml
@@ -0,0 +1,134 @@
+
+ archlinux
+ 22bbd81f-b31b-4242-9907-8840844944bf
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
+
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-nic.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-nic.xml
new file mode 100644
index 0000000..8c08471
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-nic.xml
@@ -0,0 +1,134 @@
+
+ archlinux
+ 22bbd81f-b31b-4242-9907-8840844944bf
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
+
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-sound.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-sound.xml
new file mode 100644
index 0000000..9e3d612
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-sound.xml
@@ -0,0 +1,136 @@
+
+ archlinux
+ 22bbd81f-b31b-4242-9907-8840844944bf
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-usb.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-usb.xml
new file mode 100644
index 0000000..2d50c59
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-usb.xml
@@ -0,0 +1,127 @@
+
+ archlinux
+ 22bbd81f-b31b-4242-9907-8840844944bf
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm.xml
new file mode 100644
index 0000000..32c4ae8
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm.xml
@@ -0,0 +1,140 @@
+
+ archlinux
+ 22bbd81f-b31b-4242-9907-8840844944bf
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
+
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm-invalid.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm-invalid.xml
new file mode 100644
index 0000000..15f6cff
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm-invalid.xml
@@ -0,0 +1,164 @@
+
+ 8dc5433c-0228-49e4-b019-fa2b606aa544
+ Ubuntu 20.04
+ Ubuntu 20.04 desktop installation
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+ Hello World!
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
+
diff --git a/src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm.xml b/src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm.xml
new file mode 100644
index 0000000..241a680
--- /dev/null
+++ b/src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm.xml
@@ -0,0 +1,164 @@
+
+ ubuntu-20-04
+ 8dc5433c-0228-49e4-b019-fa2b606aa544
+ Ubuntu 20.04
+ Ubuntu 20.04 desktop installation
+
+
+
+
+
+ 4194304
+ 4194304
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /dev/urandom
+
+
+
+
+
--
cgit v1.2.3-55-g7522