summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Schwär2021-02-19 00:09:54 +0100
committerStephan Schwär2021-02-19 00:09:54 +0100
commit62dce8c2b4e519f689e89038ff2afe4496f60628 (patch)
tree7c6802a436e2beb943065b5a377b61eb77d93170
parent[docker] Check recieved content. (diff)
parentAdd automatic RelaxNG schema validation for Libvirt domain XML documents (diff)
downloadmaster-sync-shared-62dce8c2b4e519f689e89038ff2afe4496f60628.tar.gz
master-sync-shared-62dce8c2b4e519f689e89038ff2afe4496f60628.tar.xz
master-sync-shared-62dce8c2b4e519f689e89038ff2afe4496f60628.zip
Merge remote-tracking branch 'origin/feature/qemu-integration'
-rw-r--r--pom.xml54
-rw-r--r--src/main/java/org/openslx/libvirt/domain/Domain.java1018
-rw-r--r--src/main/java/org/openslx/libvirt/domain/DomainUtils.java118
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Controller.java181
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/ControllerFloppy.java54
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/ControllerIde.java128
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/ControllerPci.java145
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/ControllerSata.java54
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/ControllerScsi.java138
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java139
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Device.java205
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Disk.java427
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/DiskCdrom.java55
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/DiskFloppy.java52
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/DiskStorage.java54
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Graphics.java138
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/GraphicsSdl.java54
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/GraphicsSpice.java74
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/GraphicsVnc.java54
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Hostdev.java139
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/HostdevPci.java79
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/HostdevUsb.java52
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Interface.java316
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/InterfaceBridge.java54
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/InterfaceNetwork.java54
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Sound.java128
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Video.java188
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlCreatable.java26
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocument.java373
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocumentException.java25
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java241
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java356
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlResources.java52
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlSchemaValidator.java282
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializable.java57
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlSerializationException.java25
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidatable.java18
-rw-r--r--src/main/java/org/openslx/libvirt/xml/LibvirtXmlValidationException.java25
-rw-r--r--src/main/java/org/openslx/util/vm/DiskImage.java191
-rw-r--r--src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java17
-rw-r--r--src/main/java/org/openslx/util/vm/QemuMetaData.java797
-rw-r--r--src/main/java/org/openslx/util/vm/QemuMetaDataUtils.java188
-rw-r--r--src/main/java/org/openslx/util/vm/VboxMetaData.java14
-rw-r--r--src/main/java/org/openslx/util/vm/VmMetaData.java9
-rw-r--r--src/main/java/org/openslx/util/vm/VmwareMetaData.java16
-rw-r--r--src/main/resources/libvirt/rng/basictypes.rng637
-rw-r--r--src/main/resources/libvirt/rng/capability.rng519
-rw-r--r--src/main/resources/libvirt/rng/cpu.rng12
-rw-r--r--src/main/resources/libvirt/rng/cputypes.rng420
-rw-r--r--src/main/resources/libvirt/rng/domain.rng21
-rw-r--r--src/main/resources/libvirt/rng/domainbackup.rng300
-rw-r--r--src/main/resources/libvirt/rng/domaincaps.rng294
-rw-r--r--src/main/resources/libvirt/rng/domaincheckpoint.rng94
-rw-r--r--src/main/resources/libvirt/rng/domaincommon.rng7270
-rw-r--r--src/main/resources/libvirt/rng/domainsnapshot.rng228
-rw-r--r--src/main/resources/libvirt/rng/interface.rng434
-rw-r--r--src/main/resources/libvirt/rng/network.rng450
-rw-r--r--src/main/resources/libvirt/rng/networkcommon.rng294
-rw-r--r--src/main/resources/libvirt/rng/networkport.rng160
-rw-r--r--src/main/resources/libvirt/rng/nodedev.rng765
-rw-r--r--src/main/resources/libvirt/rng/nwfilter.rng986
-rw-r--r--src/main/resources/libvirt/rng/nwfilter_params.rng32
-rw-r--r--src/main/resources/libvirt/rng/nwfilterbinding.rng49
-rw-r--r--src/main/resources/libvirt/rng/secret.rng94
-rw-r--r--src/main/resources/libvirt/rng/storagecommon.rng238
-rw-r--r--src/main/resources/libvirt/rng/storagepool.rng788
-rw-r--r--src/main/resources/libvirt/rng/storagepoolcaps.rng88
-rw-r--r--src/main/resources/libvirt/rng/storagevol.rng235
-rw-r--r--src/main/resources/libvirt/xsl/xml-output-transformation.xsl12
-rw-r--r--src/test/java/org/openslx/libvirt/domain/DomainTest.java294
-rw-r--r--src/test/java/org/openslx/libvirt/xml/LibvirtXmlDocumentTest.java260
-rw-r--r--src/test/java/org/openslx/libvirt/xml/LibvirtXmlTestResources.java29
-rw-r--r--src/test/java/org/openslx/util/vm/DiskImageTest.java260
-rw-r--r--src/test/java/org/openslx/util/vm/DiskImageTestResources.java16
-rw-r--r--src/test/java/org/openslx/util/vm/QemuMetaDataTest.java466
-rw-r--r--src/test/resources/disk/image-default.invalid1
-rw-r--r--src/test/resources/disk/image-default.qcow2bin0 -> 196624 bytes
-rw-r--r--src/test/resources/disk/image-default.vdibin0 -> 4608 bytes
-rw-r--r--src/test/resources/disk/image-default.vmdkbin0 -> 196608 bytes
-rw-r--r--src/test/resources/disk/image_cs-16384_cp-off_l2-off.qcow2bin0 -> 131072 bytes
-rw-r--r--src/test/resources/disk/image_cs-16384_cp-off_l2-on.qcow2bin0 -> 115712 bytes
-rw-r--r--src/test/resources/disk/image_cs-16384_cp-on_l2-off.qcow2bin0 -> 82432 bytes
-rw-r--r--src/test/resources/disk/image_cs-16384_cp-on_l2-on.qcow2bin0 -> 82432 bytes
-rw-r--r--src/test/resources/disk/image_cs-2097152_cp-off_l2-off.qcow2bin0 -> 16777216 bytes
-rw-r--r--src/test/resources/disk/image_cs-2097152_cp-off_l2-on.qcow2bin0 -> 14745600 bytes
-rw-r--r--src/test/resources/disk/image_cs-2097152_cp-on_l2-off.qcow2bin0 -> 10492416 bytes
-rw-r--r--src/test/resources/disk/image_cs-2097152_cp-on_l2-on.qcow2bin0 -> 10492416 bytes
-rw-r--r--src/test/resources/disk/image_cs-65536_cp-off_l2-off.qcow2bin0 -> 524288 bytes
-rw-r--r--src/test/resources/disk/image_cs-65536_cp-off_l2-on.qcow2bin0 -> 460800 bytes
-rw-r--r--src/test/resources/disk/image_cs-65536_cp-on_l2-off.qcow2bin0 -> 328192 bytes
-rw-r--r--src/test/resources/disk/image_cs-65536_cp-on_l2-on.qcow2bin0 -> 328192 bytes
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-cdrom.xml144
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-floppy.xml145
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-hdd.xml134
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-nic.xml134
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-sound.xml136
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm-no-usb.xml127
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-archlinux-vm.xml140
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm-invalid.xml164
-rw-r--r--src/test/resources/libvirt/xml/qemu-kvm_default-ubuntu-20-04-vm.xml164
100 files changed, 23792 insertions, 106 deletions
diff --git a/pom.xml b/pom.xml
index 32b6675..4b8b3dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,8 +47,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
- <source>1.7</source>
- <target>1.7</target>
+ <source>1.8</source>
+ <target>1.8</target>
</configuration>
</plugin>
<plugin>
@@ -76,7 +76,30 @@
<workspaceCodeStylesURL>file://${basedir}/extras/eclipse-code-style.xml</workspaceCodeStylesURL>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>3.0.0-M5</version>
+ </plugin>
</plugins>
+ <resources>
+ <resource>
+ <directory>${basedir}/src/main/resources</directory>
+ <includes>
+ <include>libvirt/rng/*</include>
+ <include>libvirt/xsl/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <testResources>
+ <testResource>
+ <directory>${basedir}/src/test/resources</directory>
+ <includes>
+ <include>disk/*</include>
+ <include>libvirt/xml/*</include>
+ </includes>
+ </testResource>
+ </testResources>
</build>
<dependencies>
@@ -93,9 +116,15 @@
<scope>compile</scope>
</dependency>
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>5.5.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ <version>5.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -121,5 +150,20 @@
<artifactId>lz4-java</artifactId>
<version>1.4.1</version>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.8.0</version>
+ </dependency>
+ <dependency>
+ <groupId>xalan</groupId>
+ <artifactId>xalan</artifactId>
+ <version>2.7.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.relaxng</groupId>
+ <artifactId>jing</artifactId>
+ <version>20181222</version>
+ </dependency>
</dependencies>
</project>
diff --git a/src/main/java/org/openslx/libvirt/domain/Domain.java b/src/main/java/org/openslx/libvirt/domain/Domain.java
new file mode 100644
index 0000000..4e15ec1
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/Domain.java
@@ -0,0 +1,1018 @@
+package org.openslx.libvirt.domain;
+
+import java.io.File;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.openslx.libvirt.domain.device.Device;
+import org.openslx.libvirt.domain.device.Controller;
+import org.openslx.libvirt.domain.device.ControllerFloppy;
+import org.openslx.libvirt.domain.device.ControllerIde;
+import org.openslx.libvirt.domain.device.ControllerPci;
+import org.openslx.libvirt.domain.device.ControllerSata;
+import org.openslx.libvirt.domain.device.ControllerScsi;
+import org.openslx.libvirt.domain.device.ControllerUsb;
+import org.openslx.libvirt.domain.device.Disk;
+import org.openslx.libvirt.domain.device.DiskCdrom;
+import org.openslx.libvirt.domain.device.DiskFloppy;
+import org.openslx.libvirt.domain.device.DiskStorage;
+import org.openslx.libvirt.domain.device.Graphics;
+import org.openslx.libvirt.domain.device.GraphicsSdl;
+import org.openslx.libvirt.domain.device.GraphicsSpice;
+import org.openslx.libvirt.domain.device.GraphicsVnc;
+import org.openslx.libvirt.domain.device.Hostdev;
+import org.openslx.libvirt.domain.device.Interface;
+import org.openslx.libvirt.domain.device.InterfaceBridge;
+import org.openslx.libvirt.domain.device.InterfaceNetwork;
+import org.openslx.libvirt.domain.device.Sound;
+import org.openslx.libvirt.domain.device.Video;
+import org.openslx.libvirt.xml.LibvirtXmlDocument;
+import org.openslx.libvirt.xml.LibvirtXmlDocumentException;
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+import org.openslx.libvirt.xml.LibvirtXmlResources;
+import org.openslx.libvirt.xml.LibvirtXmlSerializationException;
+import org.openslx.libvirt.xml.LibvirtXmlValidationException;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+/**
+ * Implementation of the Libvirt domain XML document.
+ *
+ * The Libvirt domain XML document is used to describe virtual machines and their configurations.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Domain extends LibvirtXmlDocument
+{
+ /**
+ * Creates Libvirt domain XML document from {@link String} providing Libvirt domain XML content.
+ *
+ * @param xml {@link String} with Libvirt domain XML content.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the domain XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid domain XML document.
+ */
+ public Domain( String xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ super( xml, LibvirtXmlResources.getLibvirtRng( "domain.rng" ) );
+ }
+
+ /**
+ * Creates Libvirt domain XML document from {@link File} containing Libvirt domain XML content.
+ *
+ * @param xml existing {@link File} containing Libvirt domain XML content.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the domain XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid domain XML document.
+ */
+ public Domain( File xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ super( xml, LibvirtXmlResources.getLibvirtRng( "domain.rng" ) );
+ }
+
+ /**
+ * Creates Libvirt domain XML document from {@link InputStream} providing Libvirt domain XML
+ * content.
+ *
+ * @param xml {@link InputStream} providing Libvirt domain XML content.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the domain XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid domain XML document.
+ */
+ public Domain( InputStream xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ super( xml, LibvirtXmlResources.getLibvirtRng( "domain.rng" ) );
+ }
+
+ /**
+ * Creates Libvirt domain XML document from {@link InputSource} providing Libvirt domain XML
+ * content.
+ *
+ * @param xml {@link InputSource} providing Libvirt domain XML content.
+ *
+ * @throws LibvirtXmlDocumentException creation of XML context failed.
+ * @throws LibvirtXmlSerializationException serialization of the domain XML content failed.
+ * @throws LibvirtXmlValidationException XML content is not a valid domain XML document.
+ */
+ public Domain( InputSource xml )
+ throws LibvirtXmlDocumentException, LibvirtXmlSerializationException, LibvirtXmlValidationException
+ {
+ super( xml, LibvirtXmlResources.getLibvirtRng( "domain.rng" ) );
+ }
+
+ /**
+ * Types of hypervisors specifiable for a virtual machine in the Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum Type
+ {
+ // @formatter:off
+ QEMU ( "qemu" ),
+ KQEMU ( "kqemu" ),
+ KVM ( "kvm" ),
+ XEN ( "xen" ),
+ LXC ( "lxc" ),
+ UML ( "uml" ),
+ OPENVZ( "openvz" ),
+ TEST ( "test" ),
+ VMWARE( "vmware" ),
+ HYPERV( "hyperv" ),
+ VBOX ( "vbox" ),
+ PHYP ( "phyp" ),
+ VZ ( "vz" ),
+ BHYVE ( "bhyve" );
+ // @formatter:on
+
+ /**
+ * Name of the hypervisor in a Libvirt domain XML document.
+ */
+ private String type;
+
+ /**
+ * Creates a hypervisor type.
+ *
+ * @param type valid name of the hypervisor in a Libvirt domain XML document.
+ */
+ Type( String type )
+ {
+ this.type = type;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.type;
+ }
+
+ /**
+ * Creates a hypervisor type from its name with error check.
+ *
+ * @param type name of the hypervisor in the Libvirt domain XML document.
+ * @return valid hypervisor type.
+ */
+ public static Type fromString( String type )
+ {
+ for ( Type t : Type.values() ) {
+ if ( t.type.equalsIgnoreCase( type ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Returns hypervisor type defined in the Libvirt domain XML document.
+ *
+ * @return hypervisor type.
+ */
+ public Type getType()
+ {
+ String typeValue = this.getRootXmlNode().getXmlElementAttributeValue( null, "type" );
+ return Type.fromString( typeValue );
+ }
+
+ /**
+ * Sets hypervisor type in Libvirt domain XML document.
+ *
+ * @param type hypervisor type for Libvirt domain XML document.
+ */
+ public void setType( Type type )
+ {
+ this.getRootXmlNode().setXmlElementAttributeValue( null, "type", type.toString() );
+ }
+
+ /**
+ * Returns virtual machine name defined in the Libvirt domain XML document.
+ *
+ * @return name of the virtual machine.
+ */
+ public String getName()
+ {
+ return this.getRootXmlNode().getXmlElementValue( "name" );
+ }
+
+ /**
+ * Sets virtual machine name in the Libvirt domain XML document.
+ *
+ * @param name virtual machine name for Libvirt domain XML document.
+ */
+ public void setName( String name )
+ {
+ this.getRootXmlNode().setXmlElementValue( "name", name );
+ }
+
+ /**
+ * Returns virtual machine title defined in the Libvirt domain XML document.
+ *
+ * @return title of the virtual machine.
+ */
+ public String getTitle()
+ {
+ return this.getRootXmlNode().getXmlElementValue( "title" );
+ }
+
+ /**
+ * Sets virtual machine title in the Libvirt domain XML document.
+ *
+ * @param title virtual machine title for Libvirt domain XML document.
+ */
+ public void setTitle( String title )
+ {
+ this.getRootXmlNode().setXmlElementValue( "title", title );
+ }
+
+ /**
+ * Returns virtual machine description defined in the Libvirt domain XML document.
+ *
+ * @return description of virtual machine.
+ */
+ public String getDescription()
+ {
+ return this.getRootXmlNode().getXmlElementValue( "description" );
+ }
+
+ /**
+ * Sets virtual machine description in the Libvirt domain XML document.
+ *
+ * @param description virtual machine description for Libvirt domain XML document.
+ */
+ public void setDescription( String description )
+ {
+ this.getRootXmlNode().setXmlElementValue( "description", description );
+ }
+
+ /**
+ * Returns virtual machine UUID defined in the Libvirt domain XML document.
+ *
+ * @return UUID of virtual machine.
+ */
+ public String getUuid()
+ {
+ return this.getRootXmlNode().getXmlElementValue( "uuid" );
+ }
+
+ /**
+ * Sets virtual machine UUID in the Libvirt domain XML document.
+ *
+ * @param uuid virtual machine UUID for Libvirt domain XML document.
+ */
+ public void setUuid( String uuid )
+ {
+ this.getRootXmlNode().setXmlElementValue( "uuid", uuid );
+ }
+
+ /**
+ * Removes virtual machine UUID in the Libvirt domain XML document.
+ */
+ public void removeUuid()
+ {
+ this.getRootXmlNode().removeXmlElement( "uuid" );
+ }
+
+ /**
+ * Returns virtual machine memory defined in the Libvirt domain XML document.
+ *
+ * @return memory of virtual machine.
+ */
+ public BigInteger getMemory()
+ {
+ String memValue = this.getRootXmlNode().getXmlElementValue( "memory" );
+ String memUnit = this.getRootXmlNode().getXmlElementAttributeValue( "memory", "unit" );
+ return DomainUtils.decodeMemory( memValue, memUnit );
+ }
+
+ /**
+ * Sets virtual machine memory in the Libvirt domain XML document.
+ *
+ * @param memory virtual machine memory in the Libvirt domain XML document.
+ */
+ public void setMemory( BigInteger memory )
+ {
+ this.getRootXmlNode().setXmlElementAttributeValue( "memory", "unit", "KiB" );
+ this.getRootXmlNode().setXmlElementValue( "memory", DomainUtils.encodeMemory( memory, "KiB" ) );
+ }
+
+ /**
+ * Returns current virtual machine memory defined in the Libvirt domain XML document.
+ *
+ * @return current memory of virtual machine.
+ */
+ public BigInteger getCurrentMemory()
+ {
+ String memValue = this.getRootXmlNode().getXmlElementValue( "currentMemory" );
+ String memUnit = this.getRootXmlNode().getXmlElementAttributeValue( "currentMemory", "unit" );
+ return DomainUtils.decodeMemory( memValue, memUnit );
+ }
+
+ /**
+ * Set current virtual machine memory in the Libvirt domain XML document.
+ *
+ * @param currentMemory current virtual machine memory in the Libvirt domain XML document.
+ */
+ public void setCurrentMemory( BigInteger currentMemory )
+ {
+ this.getRootXmlNode().setXmlElementAttributeValue( "currentMemory", "unit", "KiB" );
+ this.getRootXmlNode().setXmlElementValue( "currentMemory", DomainUtils.encodeMemory( currentMemory, "KiB" ) );
+ }
+
+ /**
+ * Returns number of virtual machine CPUs defined in the Libvirt domain XML document.
+ *
+ * @return number of CPUs of the virtual machine.
+ */
+ public int getVCpu()
+ {
+ String number = this.getRootXmlNode().getXmlElementValue( "vcpu" );
+ return Integer.parseUnsignedInt( number );
+ }
+
+ /**
+ * Set number of virtual machine CPUs in the Libvirt domain XML document.
+ *
+ * @param number virtual machine CPUs.
+ */
+ public void setVCpu( int number )
+ {
+ this.getRootXmlNode().setXmlElementValue( "vcpu", Integer.toString( number ) );
+ }
+
+ /**
+ * Returns virtual machine CPU model defined in the Libvirt domain XML document.
+ *
+ * @return CPU model of virtual machine.
+ */
+ public String getCpuModel()
+ {
+ return this.getRootXmlNode().getXmlElementValue( "cpu/model" );
+ }
+
+ /**
+ * Sets virtual machine CPU model in the Libvirt domain XML document.
+ *
+ * @param model virtual machine CPU model.
+ */
+ public void setCpuModel( String model )
+ {
+ this.getRootXmlNode().setXmlElementValue( "cpu/model", model );
+ }
+
+ /**
+ * CPU modes specifiable for a virtual machine in the Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum CpuMode
+ {
+ // @formatter:off
+ CUSTOM ( "custom" ),
+ HOST_MODEL ( "host-model" ),
+ HOST_PASSTHROUGH( "host-passthrough" );
+ // @formatter:on
+
+ /**
+ * Name of the CPU mode in a Libvirt domain XML document.
+ */
+ private String cpuMode;
+
+ /**
+ * Creates a CPU mode.
+ *
+ * @param mode valid name of the CPU mode in the Libvirt domain XML document.
+ */
+ CpuMode( String mode )
+ {
+ this.cpuMode = mode;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.cpuMode;
+ }
+
+ /**
+ * Creates a CPU mode from its name with error check.
+ *
+ * @param mode name of the CPU mode in the Libvirt domain XML document.
+ * @return valid CPU mode.
+ */
+ public static CpuMode fromString( String mode )
+ {
+ for ( CpuMode t : CpuMode.values() ) {
+ if ( t.cpuMode.equalsIgnoreCase( mode ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Returns virtual machine CPU mode defined in the Libvirt domain XML document.
+ *
+ * @return CPU mode of the virtual machine.
+ */
+ public CpuMode getCpuMode()
+ {
+ String cpuMode = this.getRootXmlNode().getXmlElementAttributeValue( "cpu", "mode" );
+ return CpuMode.fromString( cpuMode );
+ }
+
+ /**
+ * Sets virtual machine CPU mode in the Libvirt domain XML document.
+ *
+ * @param mode virtual machine CPU mode.
+ */
+ public void setCpuMode( CpuMode mode )
+ {
+ this.getRootXmlNode().setXmlElementAttributeValue( "cpu", "mode", mode.toString() );
+ }
+
+ /**
+ * CPU checks specifiable for a virtual machine in the Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum CpuCheck
+ {
+ // @formatter:off
+ NONE ( "none" ),
+ PARTIAL( "partial" ),
+ FULL ( "full" );
+ // @formatter:on
+
+ /**
+ * Name of the CPU check in the Libvirt domain XML document.
+ */
+ private String cpuCheck;
+
+ /**
+ * Creates a CPU check.
+ *
+ * @param check valid name of the CPU check in the Libvirt domain XML document.
+ */
+ CpuCheck( String check )
+ {
+ this.cpuCheck = check;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.cpuCheck;
+ }
+
+ /**
+ * Creates a CPU check from its name with error check.
+ *
+ * @param mode name of the CPU check in the Libvirt domain XML document.
+ * @return valid CPU check.
+ */
+ public static CpuCheck fromString( String check )
+ {
+ for ( CpuCheck t : CpuCheck.values() ) {
+ if ( t.cpuCheck.equalsIgnoreCase( check ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Returns virtual machine CPU check defined in the Libvirt domain XML document.
+ *
+ * @return CPU check of the virtual machine.
+ */
+ public CpuCheck getCpuCheck()
+ {
+ String cpuCheck = this.getRootXmlNode().getXmlElementAttributeValue( "cpu", "check" );
+ return CpuCheck.fromString( cpuCheck );
+ }
+
+ /**
+ * Sets virtual machine CPU check in the Libvirt domain XML document.
+ *
+ * @param check virtual machine CPU check.
+ */
+ public void setCpuCheck( CpuCheck check )
+ {
+ this.getRootXmlNode().setXmlElementAttributeValue( "cpu", "check", check.toString() );
+ }
+
+ /**
+ * Returns virtual machine devices defined in the Libvirt domain XML document.
+ *
+ * @return devices of the virtual machine.
+ */
+ public ArrayList<Device> getDevices()
+ {
+ ArrayList<Device> devices = new ArrayList<Device>();
+ Node devicesNode = this.getRootXmlNode().getXmlElement( "devices" );
+
+ if ( devicesNode != null ) {
+
+ NodeList devicesElements = devicesNode.getChildNodes();
+
+ for ( int i = 0; i < devicesElements.getLength(); i++ ) {
+ LibvirtXmlNode deviceNode = null;
+ deviceNode = new LibvirtXmlNode( this.getRootXmlNode().getXmlDocument(), devicesElements.item( i ) );
+ Device device = Device.newInstance( deviceNode );
+
+ if ( device != null ) {
+ devices.add( device );
+ }
+ }
+ }
+
+ return devices;
+ }
+
+ /**
+ * Filter list of virtual machine devices of type {@link Device} and cast filtered instances to
+ * more specific device type <code>R</code>.
+ *
+ * @param <R> specific device type for filtering and casting.
+ * @param cls specific device type's class.
+ * @param devices list of virtual machines devices.
+ * @return filtered list of virtual machines devices of type <code>R</code>.
+ */
+ private static <R> ArrayList<R> filterDevices( Class<R> cls, ArrayList<Device> devices )
+ {
+ Predicate<Device> byFilter = device -> cls.isInstance( device );
+ Function<Device, R> castFunction = device -> cls.cast( device );
+
+ return devices.stream().filter( byFilter ).map( castFunction )
+ .collect( Collectors.toCollection( ArrayList::new ) );
+ }
+
+ /**
+ * Returns list of virtual machine controller devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine controller devices.
+ */
+ public ArrayList<Controller> getControllerDevices()
+ {
+ return Domain.filterDevices( Controller.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine floppy controller devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine floppy controller devices.
+ */
+ public ArrayList<ControllerFloppy> getFloppyControllerDevices()
+ {
+ return Domain.filterDevices( ControllerFloppy.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine IDE controller devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine IDE controller devices.
+ */
+ public ArrayList<ControllerIde> getIdeControllerDevices()
+ {
+ return Domain.filterDevices( ControllerIde.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine floppy controller devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine floppy controller devices.
+ */
+ public ArrayList<ControllerPci> getPciControllerDevices()
+ {
+ return Domain.filterDevices( ControllerPci.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine SATA controller devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine SATA controller devices.
+ */
+ public ArrayList<ControllerSata> getSataControllerDevices()
+ {
+ return Domain.filterDevices( ControllerSata.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine SCSI controller devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine SCSI controller devices.
+ */
+ public ArrayList<ControllerScsi> getScsiControllerDevices()
+ {
+ return Domain.filterDevices( ControllerScsi.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine USB controller devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine USB controller devices.
+ */
+ public ArrayList<ControllerUsb> getUsbControllerDevices()
+ {
+ return Domain.filterDevices( ControllerUsb.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine disk devices specified in the Libvirt domain XML document.
+ *
+ * @return list of virtual machine disk devices.
+ */
+ public ArrayList<Disk> getDiskDevices()
+ {
+ return Domain.filterDevices( Disk.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine disk CDROM devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine disk CDROM devices.
+ */
+ public ArrayList<DiskCdrom> getDiskCdromDevices()
+ {
+ return Domain.filterDevices( DiskCdrom.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine disk floppy devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine disk floppy devices.
+ */
+ public ArrayList<DiskFloppy> getDiskFloppyDevices()
+ {
+ return Domain.filterDevices( DiskFloppy.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine disk storage devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine disk storage devices.
+ */
+ public ArrayList<DiskStorage> getDiskStorageDevices()
+ {
+ return Domain.filterDevices( DiskStorage.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine hostdev devices specified in the Libvirt domain XML document.
+ *
+ * @return list of virtual machine hostdev devices.
+ */
+ public ArrayList<Hostdev> getHostdevDevices()
+ {
+ return Domain.filterDevices( Hostdev.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine network interface devices specified in the Libvirt domain XML
+ * document.
+ *
+ * @return list of virtual machine network interface devices.
+ */
+ public ArrayList<Interface> getInterfaceDevices()
+ {
+ return Domain.filterDevices( Interface.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine graphic devices specified in the Libvirt domain XML document.
+ *
+ * @return list of virtual machine graphic devices.
+ */
+ public ArrayList<Graphics> getGraphicDevices()
+ {
+ return Domain.filterDevices( Graphics.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine sound devices specified in the Libvirt domain XML document.
+ *
+ * @return list of virtual machine sound devices.
+ */
+ public ArrayList<Sound> getSoundDevices()
+ {
+ return Domain.filterDevices( Sound.class, this.getDevices() );
+ }
+
+ /**
+ * Returns list of virtual machine video devices specified in the Libvirt domain XML document.
+ *
+ * @return list of virtual machine video devices.
+ */
+ public ArrayList<Video> getVideoDevices()
+ {
+ return Domain.filterDevices( Video.class, this.getDevices() );
+ }
+
+ /**
+ * Adds a virtual machine device to the Libvirt domain XML document.
+ *
+ * @param device virtual machine device that is added to the Libvirt domain XML document.
+ * @return reference to the added device for configuration purposes if creation was successful.
+ */
+ public Device addDevice( Device device )
+ {
+ Device addedDevice = null;
+
+ if ( device != null ) {
+ Node devicesNode = this.getRootXmlNode().getXmlElement( "devices" );
+
+ if ( devicesNode != null ) {
+ LibvirtXmlNode parentDevicesNode = null;
+ parentDevicesNode = new LibvirtXmlNode( this.getRootXmlNode().getXmlDocument(), devicesNode );
+ addedDevice = Device.createInstance( device, parentDevicesNode );
+ }
+ }
+
+ return addedDevice;
+ }
+
+ /**
+ * Adds a virtual machine controller device to the Libvirt domain XML document.
+ *
+ * @return reference to the added controller device if creation was successful.
+ */
+ public Controller addControllerDevice()
+ {
+ return Controller.class.cast( this.addDevice( new Controller() ) );
+ }
+
+ /**
+ * Adds a virtual machine floppy controller device to the Libvirt domain XML document.
+ *
+ * @return reference to the added floppy controller device if creation was successful.
+ */
+ public ControllerFloppy addControllerFloppyDevice()
+ {
+ return ControllerFloppy.class.cast( this.addDevice( new ControllerFloppy() ) );
+ }
+
+ /**
+ * Adds a virtual machine IDE controller device to the Libvirt domain XML document.
+ *
+ * @return reference to the added IDE controller device if creation was successful.
+ */
+ public ControllerIde addControllerIdeDevice()
+ {
+ return ControllerIde.class.cast( this.addDevice( new ControllerIde() ) );
+ }
+
+ /**
+ * Adds a virtual machine PCI controller device to the Libvirt domain XML document.
+ *
+ * @return reference to the added PCI controller device if creation was successful.
+ */
+ public ControllerPci addControllerPciDevice()
+ {
+ return ControllerPci.class.cast( this.addDevice( new ControllerPci() ) );
+ }
+
+ /**
+ * Adds a virtual machine SATA controller device to the Libvirt domain XML document.
+ *
+ * @return reference to the added SATA controller device if creation was successful.
+ */
+ public ControllerSata addControllerSataDevice()
+ {
+ return ControllerSata.class.cast( this.addDevice( new ControllerSata() ) );
+ }
+
+ /**
+ * Adds a virtual machine SCSI controller device to the Libvirt domain XML document.
+ *
+ * @return reference to the added SCSI controller device if creation was successful.
+ */
+ public ControllerScsi addControllerScsiDevice()
+ {
+ return ControllerScsi.class.cast( this.addDevice( new ControllerScsi() ) );
+ }
+
+ /**
+ * Adds a virtual machine USB controller device to the Libvirt domain XML document.
+ *
+ * @return reference to the added USB controller device if creation was successful.
+ */
+ public ControllerUsb addControllerUsbDevice()
+ {
+ return ControllerUsb.class.cast( this.addDevice( new ControllerUsb() ) );
+ }
+
+ /**
+ * Adds a virtual machine disk device to the Libvirt domain XML document.
+ *
+ * @return reference to the added disk device if creation was successful.
+ */
+ public Disk addDiskDevice()
+ {
+ return Disk.class.cast( this.addDevice( new Disk() ) );
+ }
+
+ /**
+ * Adds a virtual machine CDROM disk device to the Libvirt domain XML document.
+ *
+ * @return reference to the added CDROM disk device if creation was successful.
+ */
+ public DiskCdrom addDiskCdromDevice()
+ {
+ return DiskCdrom.class.cast( this.addDevice( new DiskCdrom() ) );
+ }
+
+ /**
+ * Adds a virtual machine floppy disk device to the Libvirt domain XML document.
+ *
+ * @return reference to the added floppy disk device if creation was successful.
+ */
+ public DiskFloppy addDiskFloppyDevice()
+ {
+ return DiskFloppy.class.cast( this.addDevice( new DiskFloppy() ) );
+ }
+
+ /**
+ * Adds a virtual machine storage disk device to the Libvirt domain XML document.
+ *
+ * @return reference to the added storage disk device if creation was successful.
+ */
+ public DiskStorage addDiskStorageDevice()
+ {
+ return DiskStorage.class.cast( this.addDevice( new DiskStorage() ) );
+ }
+
+ /**
+ * Adds a virtual machine disk device to the Libvirt domain XML document.
+ *
+ * @return reference to the added disk device if creation was successful.
+ */
+ public Hostdev addHostdevDevice()
+ {
+ return Hostdev.class.cast( this.addDevice( new Hostdev() ) );
+ }
+
+ /**
+ * Adds a virtual machine network device to the Libvirt domain XML document.
+ *
+ * @return reference to the added network device if creation was successful.
+ */
+ public Interface addInterfaceDevice()
+ {
+ return Interface.class.cast( this.addDevice( new Interface() ) );
+ }
+
+ /**
+ * Adds a virtual machine network bridge interface device to the Libvirt domain XML document.
+ *
+ * @return reference to the added network interface device if creation was successful.
+ */
+ public InterfaceBridge addInterfaceBridgeDevice()
+ {
+ return InterfaceBridge.class.cast( this.addDevice( new InterfaceBridge() ) );
+ }
+
+ /**
+ * Adds a virtual machine network interface device to the Libvirt domain XML document.
+ *
+ * @return reference to the added network interface device if creation was successful.
+ */
+ public InterfaceNetwork addInterfaceNetworkDevice()
+ {
+ return InterfaceNetwork.class.cast( this.addDevice( new InterfaceNetwork() ) );
+ }
+
+ /**
+ * Adds a virtual machine graphics device to the Libvirt domain XML document.
+ *
+ * @return reference to the added graphics device if creation was successful.
+ */
+ public Graphics addGraphicsDevice()
+ {
+ return Graphics.class.cast( this.addDevice( new Graphics() ) );
+ }
+
+ /**
+ * Adds a virtual machine SDL graphics device to the Libvirt domain XML document.
+ *
+ * @return reference to the added SDL graphics device if creation was successful.
+ */
+ public GraphicsSdl addGraphicsSdlDevice()
+ {
+ return GraphicsSdl.class.cast( this.addDevice( new GraphicsSdl() ) );
+ }
+
+ /**
+ * Adds a virtual machine SPICE graphics device to the Libvirt domain XML document.
+ *
+ * @return reference to the added SPICE graphics device if creation was successful.
+ */
+ public GraphicsSpice addGraphicsSpiceDevice()
+ {
+ return GraphicsSpice.class.cast( this.addDevice( new GraphicsSpice() ) );
+ }
+
+ /**
+ * Adds a virtual machine VNC graphics device to the Libvirt domain XML document.
+ *
+ * @return reference to the added VNC graphics device if creation was successful.
+ */
+ public GraphicsVnc addGraphicsVncDevice()
+ {
+ return GraphicsVnc.class.cast( this.addDevice( new GraphicsVnc() ) );
+ }
+
+ /**
+ * Adds a virtual machine sound device to the Libvirt domain XML document.
+ *
+ * @return reference to the added sound device if creation was successful.
+ */
+ public Sound addSoundDevice()
+ {
+ return Sound.class.cast( this.addDevice( new Sound() ) );
+ }
+
+ /**
+ * Adds a virtual machine video device to the Libvirt domain XML document.
+ *
+ * @return reference to the added video device if creation was successful.
+ */
+ public Video addVideoDevice()
+ {
+ return Video.class.cast( this.addDevice( new Video() ) );
+ }
+
+ /**
+ * Removes boot oder entries in the Libvirt domain XML document.
+ */
+ public void removeBootOrder()
+ {
+ // remove boot order entries of all disk devices
+ for ( Disk diskDevice : this.getDiskDevices() ) {
+ diskDevice.removeBootOrder();
+ }
+
+ // remove boot order entries of all network interface devices
+ for ( Interface interfaceDevice : this.getInterfaceDevices() ) {
+ interfaceDevice.removeBootOrder();
+ }
+
+ // remove boot order entries of all hostdev devices
+ for ( Hostdev hostdevDevice : this.getHostdevDevices() ) {
+ hostdevDevice.removeBootOrder();
+ }
+
+ // remove boot oder entries under the 'os' element
+ this.getRootXmlNode().removeXmlElement( "os/boot" );
+ }
+
+ /**
+ * Removes underlying source for all disk devices in the Libvirt domain XML document.
+ *
+ * @implNote Calling this method will result in an invalid Libvirt domain XML document.
+ */
+ public void removeDiskDevicesStorage()
+ {
+ for ( Disk diskDevice : this.getDiskDevices() ) {
+ diskDevice.removeStorage();
+ }
+ }
+
+ /**
+ * Removes network source for all interface devices in the Libvirt domain XML document.
+ */
+ public void removeInterfaceDevicesSource()
+ {
+ for ( Interface interfaceDevice : this.getInterfaceDevices() ) {
+ interfaceDevice.removeSource();
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/DomainUtils.java b/src/main/java/org/openslx/libvirt/domain/DomainUtils.java
new file mode 100644
index 0000000..2462371
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/DomainUtils.java
@@ -0,0 +1,118 @@
+package org.openslx.libvirt.domain;
+
+import java.math.BigInteger;
+
+/**
+ * Collection of helper functions to maintain a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public final class DomainUtils
+{
+
+ /**
+ * Converts memory value with specified SI unit to absolute value in bytes.
+ *
+ * @param value amount of memory in specified SI unit.
+ * @param unit SI unit name, one of: bytes, KB, k, KiB, MB, M, MiB, GB, G, GiB, TB, T, TiB, for
+ * <code>value</code>.
+ * @return absolute amount of memory in bytes.
+ */
+ public static BigInteger decodeMemory( String value, String unit )
+ {
+ BigInteger factor = null;
+ BigInteger result = new BigInteger( value );
+
+ switch ( unit ) {
+ case "b":
+ case "bytes":
+ factor = new BigInteger( "1" );
+ break;
+ case "KB":
+ factor = new BigInteger( "1000" );
+ break;
+ case "k":
+ case "KiB":
+ factor = new BigInteger( "1024" );
+ break;
+ case "MB":
+ factor = new BigInteger( "1000000" );
+ break;
+ case "M":
+ case "MiB":
+ factor = new BigInteger( "1048576" );
+ break;
+ case "GB":
+ factor = new BigInteger( "1000000000" );
+ break;
+ case "G":
+ case "GiB":
+ factor = new BigInteger( "1073741824" );
+ break;
+ case "TB":
+ factor = new BigInteger( "1000000000000" );
+ break;
+ case "T":
+ case "TiB":
+ factor = new BigInteger( "1099511627776" );
+ break;
+ default:
+ return null;
+ }
+
+ return result.multiply( factor );
+ }
+
+ /**
+ * Convert memory from absolute value in bytes to value in specified SI unit.
+ *
+ * @param value absolute amount of memory in bytes.
+ * @param unit SI unit name, one of: bytes, KB, k, KiB, MB, M, MiB, GB, G, GiB, TB, T, TiB for
+ * returned memory value.
+ * @return amount of memory in specified SI unit.
+ */
+ public static String encodeMemory( BigInteger value, String unit )
+ {
+ BigInteger dividend = null;
+
+ switch ( unit ) {
+ case "b":
+ case "bytes":
+ dividend = new BigInteger( "1" );
+ break;
+ case "KB":
+ dividend = new BigInteger( "1000" );
+ break;
+ case "k":
+ case "KiB":
+ dividend = new BigInteger( "1024" );
+ break;
+ case "MB":
+ dividend = new BigInteger( "1000000" );
+ break;
+ case "M":
+ case "MiB":
+ dividend = new BigInteger( "1048576" );
+ break;
+ case "GB":
+ dividend = new BigInteger( "1000000000" );
+ break;
+ case "G":
+ case "GiB":
+ dividend = new BigInteger( "1073741824" );
+ break;
+ case "TB":
+ dividend = new BigInteger( "1000000000000" );
+ break;
+ case "T":
+ case "TiB":
+ dividend = new BigInteger( "1099511627776" );
+ break;
+ default:
+ return null;
+ }
+
+ return value.divide( dividend ).toString();
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/Controller.java b/src/main/java/org/openslx/libvirt/domain/device/Controller.java
new file mode 100644
index 0000000..626462b
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/Controller.java
@@ -0,0 +1,181 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A controller (PCI, USB, ...) device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Controller extends Device
+{
+ /**
+ * Creates an empty controller device.
+ */
+ public Controller()
+ {
+ super();
+ }
+
+ /**
+ * Creates a controller device representing an existing Libvirt XML controller device element.
+ *
+ * @param xmlNode existing Libvirt XML controller device element.
+ */
+ public Controller( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns index of the controller.
+ *
+ * @return index of the controller.
+ */
+ public int getIndex()
+ {
+ String index = this.getXmlElementAttributeValue( "index" );
+ return Integer.parseInt( index );
+ }
+
+ /**
+ * Sets index for the controller.
+ *
+ * @param index index for the controller.
+ */
+ public void setIndex( int index )
+ {
+ this.setXmlElementAttributeValue( "index", Integer.toString( index ) );
+ }
+
+ /**
+ * Creates a non-existent controller device as Libvirt XML device element.
+ *
+ * @param controller controller device that is created.
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created controller device instance.
+ */
+ public static Controller createInstance( Controller controller, LibvirtXmlNode xmlNode )
+ {
+ Controller addedController = null;
+
+ if ( controller instanceof ControllerFloppy ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.FLOPPY.toString() );
+ addedController = ControllerFloppy.createInstance( xmlNode );
+ } else if ( controller instanceof ControllerIde ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.IDE.toString() );
+ addedController = ControllerIde.createInstance( xmlNode );
+ } else if ( controller instanceof ControllerPci ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.PCI.toString() );
+ addedController = ControllerPci.createInstance( xmlNode );
+ } else if ( controller instanceof ControllerSata ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.SATA.toString() );
+ addedController = ControllerSata.createInstance( xmlNode );
+ } else if ( controller instanceof ControllerScsi ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.SCSI.toString() );
+ addedController = ControllerScsi.createInstance( xmlNode );
+ } else if ( controller instanceof ControllerUsb ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.USB.toString() );
+ addedController = ControllerUsb.createInstance( xmlNode );
+ }
+
+ return addedController;
+ }
+
+ /**
+ * Creates a controller device representing an existing Libvirt XML controller device element.
+ *
+ * @param xmlNode existing Libvirt XML controller device element.
+ * @return controller device instance.
+ */
+ public static Controller newInstance( LibvirtXmlNode xmlNode )
+ {
+
+ Controller deviceController = null;
+ Type type = Type.fromString( xmlNode.getXmlElementAttributeValue( "type" ) );
+
+ if ( type == null ) {
+ return null;
+ }
+
+ switch ( type ) {
+ case FLOPPY:
+ deviceController = ControllerFloppy.newInstance( xmlNode );
+ break;
+ case IDE:
+ deviceController = ControllerIde.newInstance( xmlNode );
+ break;
+ case PCI:
+ deviceController = ControllerPci.newInstance( xmlNode );
+ break;
+ case SATA:
+ deviceController = ControllerSata.newInstance( xmlNode );
+ break;
+ case SCSI:
+ deviceController = ControllerScsi.newInstance( xmlNode );
+ break;
+ case USB:
+ deviceController = ControllerUsb.newInstance( xmlNode );
+ break;
+ }
+
+ return deviceController;
+ }
+
+ /**
+ * Type of controller device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ enum Type
+ {
+ // @formatter:off
+ FLOPPY( "fdc" ),
+ IDE ( "ide" ),
+ PCI ( "pci" ),
+ SATA ( "sata" ),
+ SCSI ( "scsi" ),
+ USB ( "usb" );
+ // @formatter:on
+
+ /**
+ * Name of the controller device type.
+ */
+ private String type = null;
+
+ /**
+ * Creates controller device type.
+ *
+ * @param type valid name of the controller device type in a Libvirt domain XML document.
+ */
+ Type( String type )
+ {
+ this.type = type;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.type;
+ }
+
+ /**
+ * Creates controller device type from its name with error check.
+ *
+ * @param type name of the controller device type in a Libvirt domain XML document.
+ * @return valid controller device type.
+ */
+ public static Type fromString( String type )
+ {
+ for ( Type t : Type.values() ) {
+ if ( t.type.equalsIgnoreCase( type ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/ControllerFloppy.java b/src/main/java/org/openslx/libvirt/domain/device/ControllerFloppy.java
new file mode 100644
index 0000000..5e580bd
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/ControllerFloppy.java
@@ -0,0 +1,54 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A floppy controller device in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class ControllerFloppy extends Controller
+{
+ /**
+ * Creates an empty floppy controller device.
+ */
+ public ControllerFloppy()
+ {
+ super();
+ }
+
+ /**
+ * Creates a floppy controller device representing an existing Libvirt XML floppy controller
+ * device element.
+ *
+ * @param xmlNode existing Libvirt XML controller device element.
+ */
+ public ControllerFloppy( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent floppy controller device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created floppy controller device instance.
+ */
+ public static ControllerFloppy createInstance( LibvirtXmlNode xmlNode )
+ {
+ return ControllerFloppy.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a floppy controller device representing an existing Libvirt XML floppy controller
+ * device element.
+ *
+ * @param xmlNode existing Libvirt XML controller device element.
+ * @return floppy controller device instance.
+ */
+ public static ControllerFloppy newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new ControllerFloppy( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/ControllerIde.java b/src/main/java/org/openslx/libvirt/domain/device/ControllerIde.java
new file mode 100644
index 0000000..062b67d
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/ControllerIde.java
@@ -0,0 +1,128 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * An IDE controller device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class ControllerIde extends Controller
+{
+ /**
+ * Creates an empty IDE controller device.
+ */
+ public ControllerIde()
+ {
+ super();
+ }
+
+ /**
+ * Creates an IDE controller device representing an existing Libvirt XML IDE controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML IDE controller device element.
+ */
+ public ControllerIde( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns emulated hardware model of the IDE controller.
+ *
+ * @return hardware model of the IDE controller.
+ */
+ public Model getModel()
+ {
+ String model = this.getXmlElementAttributeValue( "model" );
+ return Model.fromString( model );
+ }
+
+ /**
+ * Sets hardware model for the IDE controller.
+ *
+ * @param model hardware model for the IDE controller.
+ */
+ public void setModel( Model model )
+ {
+ this.setXmlElementAttributeValue( "model", model.toString() );
+ }
+
+ /**
+ * Creates a non-existent IDE controller device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created IDE controller device instance.
+ */
+ public static ControllerIde createInstance( LibvirtXmlNode xmlNode )
+ {
+ return ControllerIde.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates an IDE controller device representing an existing Libvirt XML IDE controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML IDE controller device element.
+ * @return IDE controller device instance.
+ */
+ public static ControllerIde newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new ControllerIde( xmlNode );
+ }
+
+ /**
+ * Model of IDE controller device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ enum Model
+ {
+ // @formatter:off
+ PIIX3( "piix3" ),
+ PIIX4( "pixx4" ),
+ ICH6 ( "ich6" );
+ // @formatter:on
+
+ /**
+ * Name of the IDE controller device model.
+ */
+ private String model = null;
+
+ /**
+ * Creates IDE controller device model.
+ *
+ * @param type valid name of the IDE controller device model in a Libvirt domain XML document.
+ */
+ Model( String model )
+ {
+ this.model = model;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.model;
+ }
+
+ /**
+ * Creates IDE controller device model from its name with error check.
+ *
+ * @param type name of the IDE controller device model in a Libvirt domain XML document.
+ * @return valid IDE controller device model.
+ */
+ public static Model fromString( String model )
+ {
+ for ( Model t : Model.values() ) {
+ if ( t.model.equalsIgnoreCase( model ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/ControllerPci.java b/src/main/java/org/openslx/libvirt/domain/device/ControllerPci.java
new file mode 100644
index 0000000..feb8b1a
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/ControllerPci.java
@@ -0,0 +1,145 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A PCI controller device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class ControllerPci extends Controller
+{
+ /**
+ * Creates an empty PCI controller device.
+ */
+ public ControllerPci()
+ {
+ super();
+ }
+
+ /**
+ * Creates a PCI controller device representing an existing Libvirt XML PCI controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML PCI controller device element.
+ */
+ public ControllerPci( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns model of the PCI controller.
+ *
+ * @return model of the PCI controller.
+ */
+ public Model getModel()
+ {
+ String model = this.getXmlElementAttributeValue( "model" );
+ return Model.fromString( model );
+ }
+
+ /**
+ * Sets model for the PCI controller.
+ *
+ * @param model model for the PCI controller.
+ */
+ public void setModel( Model model )
+ {
+ this.setXmlElementAttributeValue( "model", model.toString() );
+ }
+
+ /**
+ * Returns emulated hardware model of the PCI controller.
+ *
+ * @return emulated hardware model of the PCI controller.
+ */
+ public String getModelEmulated()
+ {
+ return this.getXmlElementAttributeValue( "model", "name" );
+ }
+
+ /**
+ * Creates a non-existent PCI controller device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created PCI controller device instance.
+ */
+ public static ControllerPci createInstance( LibvirtXmlNode xmlNode )
+ {
+ return ControllerPci.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a PCI controller device representing an existing Libvirt XML PCI controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML PCI controller device element.
+ * @return PCI controller device instance.
+ */
+ public static ControllerPci newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new ControllerPci( xmlNode );
+ }
+
+ /**
+ * Model of PCI controller device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ enum Model
+ {
+ // @formatter:off
+ PCI_ROOT ( "pci-root" ),
+ PCI_BRDIGE ( "pci-bridge" ),
+ PCIE_ROOT ( "pcie-root" ),
+ PCI_DMI2BRIDGE ( "dmi-to-pci-bridge" ),
+ PCIE_ROOT_PORT ( "pcie-root-port" ),
+ PCIE_SWITCH_UPSTREAM_PORT ( "pcie-switch-upstream-port" ),
+ PCIE_SWITCH_DOWNSTREAM_PORT( "pcie-switch-downstream-port" ),
+ PCI_EXPANDER_BUS ( "pci-expander-bus" ),
+ PCIE_EXPANDER_BUS ( "pcie-expander-bus" ),
+ PCIE2PCI_BRIDGE ( "pcie-to-pci-bridge" );
+ // @formatter:on
+
+ /**
+ * Name of the PCI controller device model.
+ */
+ private String model = null;
+
+ /**
+ * Creates PCI controller device model.
+ *
+ * @param type valid name of the PCI controller device model in a Libvirt domain XML document.
+ */
+ Model( String model )
+ {
+ this.model = model;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.model;
+ }
+
+ /**
+ * Creates PCI controller device model from its name with error check.
+ *
+ * @param type name of the PCI controller device model in a Libvirt domain XML document.
+ * @return valid PCI controller device model.
+ */
+ public static Model fromString( String model )
+ {
+ for ( Model t : Model.values() ) {
+ if ( t.model.equalsIgnoreCase( model ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/ControllerSata.java b/src/main/java/org/openslx/libvirt/domain/device/ControllerSata.java
new file mode 100644
index 0000000..b784ae0
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/ControllerSata.java
@@ -0,0 +1,54 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A SATA controller device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class ControllerSata extends Controller
+{
+ /**
+ * Creates an empty SATA controller device.
+ */
+ public ControllerSata()
+ {
+ super();
+ }
+
+ /**
+ * Creates a SATA controller device representing an existing Libvirt XML SATA controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML SATA controller device element.
+ */
+ public ControllerSata( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent SATA controller device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created SATA controller device instance.
+ */
+ public static ControllerSata createInstance( LibvirtXmlNode xmlNode )
+ {
+ return ControllerSata.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a SATA controller device representing an existing Libvirt XML SATA controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML SATA controller device element.
+ * @return SATA controller device instance.
+ */
+ public static ControllerSata newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new ControllerSata( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/ControllerScsi.java b/src/main/java/org/openslx/libvirt/domain/device/ControllerScsi.java
new file mode 100644
index 0000000..16c3a0f
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/ControllerScsi.java
@@ -0,0 +1,138 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A SCSI controller device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class ControllerScsi extends Controller
+{
+ /**
+ * Creates an empty SCSI controller device.
+ */
+ public ControllerScsi()
+ {
+ super();
+ }
+
+ /**
+ * Creates a SCSI controller device representing an existing Libvirt XML SCSI controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML SCSI controller device element.
+ */
+ public ControllerScsi( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns hardware model of the PCI controller.
+ *
+ * @return hardware model of the PCI controller.
+ */
+ public Model getModel()
+ {
+ String model = this.getXmlElementAttributeValue( "model" );
+ return Model.fromString( model );
+ }
+
+ /**
+ * Sets hardware model for the PCI controller.
+ *
+ * @param model hardware model for the PCI controller.
+ */
+ public void setModel( Model model )
+ {
+ this.setXmlElementAttributeValue( "model", model.toString() );
+ }
+
+ /**
+ * Creates a non-existent SCSI controller device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created SCSI controller device instance.
+ */
+ public static ControllerScsi createInstance( LibvirtXmlNode xmlNode )
+ {
+ return ControllerScsi.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a SCSI controller device representing an existing Libvirt XML SCSI controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML SCSI controller device element.
+ * @return SCSI controller device instance.
+ */
+ public static ControllerScsi newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new ControllerScsi( xmlNode );
+ }
+
+ /**
+ * Model of SCSI controller device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ enum Model
+ {
+ // @formatter:off
+ AUTO ( "auto" ),
+ BUSLOGIC ( "buslogic" ),
+ IBMVSCSI ( "ibmvscsi" ),
+ LSISAS1068 ( "lsisas1068" ),
+ LSISAS1078 ( "lsisas1078" ),
+ VIRTIO_SCSI ( "virtio-scsi" ),
+ VMPVSCSI ( "vmpvscsi" ),
+ VIRTIO_TRANSITIONAL ( "virtio-transitional" ),
+ VIRTIO_NON_TRANSITIONAL( "virtio-non-transitional" ),
+ NCR53C90 ( "ncr53c90" ),
+ AM53C974 ( "am53c974" ),
+ DC390 ( "dc390" );
+ // @formatter:on
+
+ /**
+ * Name of the SCSI controller device model.
+ */
+ private String model = null;
+
+ /**
+ * Creates SCSI controller device model.
+ *
+ * @param type valid name of the SCSI controller device model in a Libvirt domain XML
+ * document.
+ */
+ Model( String model )
+ {
+ this.model = model;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.model;
+ }
+
+ /**
+ * Creates SCSI controller device model from its name with error check.
+ *
+ * @param type name of the SCSI controller device model in a Libvirt domain XML document.
+ * @return valid SCSI controller device model.
+ */
+ public static Model fromString( String model )
+ {
+ for ( Model t : Model.values() ) {
+ if ( t.model.equalsIgnoreCase( model ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java b/src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java
new file mode 100644
index 0000000..695167c
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java
@@ -0,0 +1,139 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A USB controller device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class ControllerUsb extends Controller
+{
+ /**
+ * Creates an empty USB controller device.
+ */
+ public ControllerUsb()
+ {
+ super();
+ }
+
+ /**
+ * Creates an USB controller device representing an existing Libvirt XML USB controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML USB controller device element.
+ */
+ public ControllerUsb( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns hardware model of the PCI controller.
+ *
+ * @return hardware model of the PCI controller.
+ */
+ public Model getModel()
+ {
+ String model = this.getXmlElementAttributeValue( "model" );
+ return Model.fromString( model );
+ }
+
+ /**
+ * Sets hardware model for the PCI controller.
+ *
+ * @param model hardware model for the PCI controller.
+ */
+ public void setModel( Model model )
+ {
+ this.setXmlElementAttributeValue( "model", model.toString() );
+ }
+
+ /**
+ * Creates a non-existent USB controller device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created USB controller device instance.
+ */
+ public static ControllerUsb createInstance( LibvirtXmlNode xmlNode )
+ {
+ return ControllerUsb.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates an USB controller device representing an existing Libvirt XML USB controller device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML USB controller device element.
+ * @return USB controller device instance.
+ */
+ public static ControllerUsb newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new ControllerUsb( xmlNode );
+ }
+
+ /**
+ * Model of PCI controller device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum Model
+ {
+ // @formatter:off
+ NONE ( "none" ),
+ PIIX3_UHCI ( "piix3-uhci" ),
+ PIIX4_UHCI ( "piix4-uhci" ),
+ EHCI ( "ehci" ),
+ ICH9_EHCI1 ( "ich9-ehci1" ),
+ ICH9_UHCI1 ( "ich9-uhci1" ),
+ ICH9_UHCI2 ( "ich9-uhci2" ),
+ ICH9_UHCI3 ( "ich9-uhci3" ),
+ VT82C686B_UHCI( "vt82c686b-uhci" ),
+ PCI_OHCI ( "pci-ohci" ),
+ NEC_XHCI ( "nec-xhci" ),
+ QUSB1 ( "qusb1" ),
+ QUSB2 ( "qusb2" ),
+ QEMU_XHCI ( "qemu-xhci" );
+ // @formatter:on
+
+ /**
+ * Name of the USB controller device model.
+ */
+ private String model = null;
+
+ /**
+ * Creates USB controller device model.
+ *
+ * @param type valid name of the USB controller device model in a Libvirt domain XML document.
+ */
+ Model( String model )
+ {
+ this.model = model;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.model;
+ }
+
+ /**
+ * Creates USB controller device model from its name with error check.
+ *
+ * @param type name of the USB controller device model in a Libvirt domain XML document.
+ * @return valid USB controller device model.
+ */
+ public static Model fromString( String model )
+ {
+ for ( Model t : Model.values() ) {
+ if ( t.model.equalsIgnoreCase( model ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/Device.java b/src/main/java/org/openslx/libvirt/domain/device/Device.java
new file mode 100644
index 0000000..5c26c55
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/Device.java
@@ -0,0 +1,205 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * A virtual machines device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Device extends LibvirtXmlNode
+{
+ /**
+ * Creates an empty virtual machine device.
+ */
+ public Device()
+ {
+ super();
+ }
+
+ /**
+ * Creates a virtual machine device representing an existing Libvirt XML device element.
+ *
+ * @param xmlNode existing Libvirt XML device element.
+ */
+ public Device( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Removes device from Libvirt domain XML document.
+ */
+ public void remove()
+ {
+ Node node = this.getXmlElement();
+ node.getParentNode().removeChild( node );
+ }
+
+ /**
+ * Creates a Libvirt XML device element as child of a given Libvirt XML parent node.
+ *
+ * @param xmlParentNode parent Libvirt XML node of the Libvirt XML device element that is
+ * created.
+ * @param deviceType type of the Libvirt XML device element.
+ * @return created Libvirt XML device node.
+ */
+ private static LibvirtXmlNode createDeviceElement( LibvirtXmlNode xmlParentNode, Type deviceType )
+ {
+ // create XML element as part of the Libvirt XML document
+ Document xmlDocument = xmlParentNode.getXmlDocument();
+ Element deviceNode = xmlDocument.createElement( deviceType.toString() );
+
+ // append the created XML element to the Libvirt XML document
+ xmlParentNode.getXmlBaseNode().appendChild( deviceNode );
+
+ return new LibvirtXmlNode( xmlParentNode.getXmlDocument(), deviceNode );
+ }
+
+ /**
+ * Creates a non-existent virtual machine device as Libvirt XML device element.
+ *
+ * @param device virtual machine device that is created.
+ * @param xmlParentNode parent Libvirt XML node of the Libvirt XML device that is created.
+ * @return created virtual machine device instance.
+ */
+ public static Device createInstance( Device device, LibvirtXmlNode xmlParentNode )
+ {
+ Device createdDevice = null;
+
+ if ( device instanceof Controller ) {
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.CONTROLLER );
+ createdDevice = Controller.createInstance( Controller.class.cast( device ), xmlNode );
+ } else if ( device instanceof Disk ) {
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.DISK );
+ createdDevice = Disk.createInstance( Disk.class.cast( device ), xmlNode );
+ } else if ( device instanceof Hostdev ) {
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.HOSTDEV );
+ createdDevice = Hostdev.createInstance( Hostdev.class.cast( device ), xmlNode );
+ } else if ( device instanceof Interface ) {
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.INTERFACE );
+ createdDevice = Interface.createInstance( Interface.class.cast( device ), xmlNode );
+ } else if ( device instanceof Graphics ) {
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.GRAPHICS );
+ createdDevice = Graphics.createInstance( Graphics.class.cast( device ), xmlNode );
+ } else if ( device instanceof Sound ) {
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.SOUND );
+ createdDevice = Sound.createInstance( xmlNode );
+ } else if ( device instanceof Video ) {
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.VIDEO );
+ createdDevice = Video.createInstance( xmlNode );
+ }
+
+ return createdDevice;
+ }
+
+ /**
+ * Creates a virtual machine device representing an existing Libvirt XML device element.
+ *
+ * @param xmlNode existing Libvirt XML device element.
+ * @return virtual machine device instance.
+ */
+ public static Device newInstance( LibvirtXmlNode xmlNode )
+ {
+
+ Node element = xmlNode.getXmlElement();
+
+ if ( element == null ) {
+ return null;
+ } else {
+ Device device = null;
+ Type type = Type.fromString( element.getNodeName() );
+
+ if ( type == null ) {
+ return null;
+ }
+
+ switch ( type ) {
+ case CONTROLLER:
+ device = Controller.newInstance( xmlNode );
+ break;
+ case DISK:
+ device = Disk.newInstance( xmlNode );
+ break;
+ case HOSTDEV:
+ device = Hostdev.newInstance( xmlNode );
+ break;
+ case INTERFACE:
+ device = Interface.newInstance( xmlNode );
+ break;
+ case GRAPHICS:
+ device = Graphics.newInstance( xmlNode );
+ break;
+ case SOUND:
+ device = Sound.newInstance( xmlNode );
+ break;
+ case VIDEO:
+ device = Video.newInstance( xmlNode );
+ break;
+ }
+
+ return device;
+ }
+ }
+
+ /**
+ * Type of virtual machine devices.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ enum Type
+ {
+ // @formatter:off
+ CONTROLLER( "controller" ),
+ DISK ( "disk" ),
+ HOSTDEV ( "hostdev" ),
+ INTERFACE ( "interface" ),
+ GRAPHICS ( "graphics" ),
+ SOUND ( "sound" ),
+ VIDEO ( "video" );
+ // @formatter:on
+
+ /**
+ * Name of the virtual machine device type.
+ */
+ private String type = null;
+
+ /**
+ * Creates virtual machine device type.
+ *
+ * @param type valid name of the virtual machine device type in a Libvirt domain XML document.
+ */
+ Type( String type )
+ {
+ this.type = type;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.type;
+ }
+
+ /**
+ * Creates virtual machine device type from its name with error check.
+ *
+ * @param type name of the virtual machine device type in a Libvirt domain XML document.
+ * @return valid virtual machine device type.
+ */
+ public static Type fromString( String type )
+ {
+ for ( Type t : Type.values() ) {
+ if ( t.type.equalsIgnoreCase( type ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/Disk.java b/src/main/java/org/openslx/libvirt/domain/device/Disk.java
new file mode 100644
index 0000000..464e7b6
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/Disk.java
@@ -0,0 +1,427 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+import org.w3c.dom.Node;
+
+/**
+ * A disk (floppy, CDROM, ...) device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Disk extends Device
+{
+ /**
+ * Creates an empty disk device.
+ */
+ public Disk()
+ {
+ super();
+ }
+
+ /**
+ * Creates a disk device representing an existing Libvirt XML disk device element.
+ *
+ * @param xmlNode existing Libvirt XML disk device element.
+ */
+ public Disk( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns storage type of the disk device.
+ *
+ * @return storage type of underlying source for the disk device.
+ */
+ public StorageType getStorageType()
+ {
+ String storageType = this.getXmlElementAttributeValue( "type" );
+ return StorageType.fromString( storageType );
+ }
+
+ /**
+ * Sets storage type for the disk device.
+ *
+ * @param storageType storage type of underlying source for the disk device.
+ *
+ * @implNote Please call {@link #setStorageSource(String)} after calling this method, otherwise
+ * the underlying source for the disk device may be invalid.
+ */
+ protected void setStorageType( StorageType storageType )
+ {
+ this.setXmlElementAttributeValue( "type", storageType.toString() );
+ }
+
+ /**
+ * Returns underlying source of disk device.
+ *
+ * @return file path to underlying source of disk device.
+ */
+ public String getStorageSource()
+ {
+ StorageType storageType = this.getStorageType();
+ String storageSource = null;
+
+ switch ( storageType ) {
+ case FILE:
+ storageSource = this.getXmlElementAttributeValue( "source", "file" );
+ break;
+ case BLOCK:
+ storageSource = this.getXmlElementAttributeValue( "source", "bdev" );
+ break;
+ }
+
+ return storageSource;
+ }
+
+ /**
+ * Sets underlying source for disk device.
+ *
+ * @param source file path to underlying source for disk device.
+ *
+ * @implNote Please call {@link #setStorageType(StorageType)} before calling this method,
+ * otherwise the underlying source for the disk device is not set.
+ */
+ protected void setStorageSource( String source )
+ {
+ StorageType storageType = this.getStorageType();
+
+ // remove all attributes from sub-element 'source'
+ this.removeXmlElementAttributes( "source" );
+
+ // rewrite specific attribute depending on the storage type
+ switch ( storageType ) {
+ case FILE:
+ this.setXmlElementAttributeValue( "source", "file", source );
+ break;
+ case BLOCK:
+ this.setXmlElementAttributeValue( "source", "bdev", source );
+ break;
+ }
+ }
+
+ /**
+ * Sets storage type and underlying source for disk device.
+ *
+ * @param storageType storage type of underlying source for the disk device.
+ * @param source file path to underlying source for disk device.
+ */
+ public void setStorage( StorageType storageType, String source )
+ {
+ this.setStorageType( storageType );
+ this.setStorageSource( source );
+ }
+
+ /**
+ * Removes underlying source of the disk device.
+ *
+ * @implNote Calling this method will result in an invalid Libvirt domain XML content.
+ */
+ public void removeStorage()
+ {
+ this.removeXmlElement( "source" );
+ }
+
+ /**
+ * Removes boot oder entry of the disk device.
+ */
+ public void removeBootOrder()
+ {
+ this.removeXmlElement( "boot" );
+ }
+
+ /**
+ * Returns read only state of disk device.
+ *
+ * @return read only state of disk device.
+ */
+ public boolean isReadOnly()
+ {
+ Node readOnly = this.getXmlElement( "readonly" );
+
+ if ( readOnly == null ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Sets read only state for disk device.
+ *
+ * @param readOnly state for disk device and its read only functionality.
+ */
+ public void setReadOnly( boolean readOnly )
+ {
+ if ( readOnly ) {
+ this.setXmlElement( "readonly" );
+ } else {
+ this.removeXmlElement( "readonly" );
+ }
+ }
+
+ /**
+ * Returns bus type of the disk device.
+ *
+ * @return bus type of the disk device.
+ */
+ public BusType getBusType()
+ {
+ String busType = this.getXmlElementAttributeValue( "target", "bus" );
+ return BusType.fromString( busType );
+ }
+
+ /**
+ * Sets bus type for the disk device.
+ *
+ * @param busType bus type for the disk device.
+ */
+ public void setBusType( BusType busType )
+ {
+ this.setXmlElementAttributeValue( "target", "bus", busType.toString() );
+ }
+
+ /**
+ * Returns target device of the disk device.
+ *
+ * @return target device of the disk device.
+ */
+ public String getTargetDevice()
+ {
+ return this.getXmlElementAttributeValue( "target", "dev" );
+ }
+
+ /**
+ * Sets target device for the disk device.
+ *
+ * @param target device for the disk device.
+ */
+ public void setTargetDevice( String targetDevice )
+ {
+ this.setXmlElementAttributeValue( "target", "dev", targetDevice );
+ }
+
+ /**
+ * Creates a non-existent disk device as Libvirt XML device element.
+ *
+ * @param disk disk device that is created.
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created disk device instance.
+ */
+ public static Disk createInstance( Disk disk, LibvirtXmlNode xmlNode )
+ {
+ Disk addedDisk = null;
+
+ if ( disk instanceof DiskCdrom ) {
+ xmlNode.setXmlElementAttributeValue( "device", Type.CDROM.toString() );
+ addedDisk = DiskCdrom.createInstance( xmlNode );
+ } else if ( disk instanceof DiskFloppy ) {
+ xmlNode.setXmlElementAttributeValue( "device", Type.FLOPPY.toString() );
+ addedDisk = DiskFloppy.createInstance( xmlNode );
+ } else if ( disk instanceof DiskStorage ) {
+ xmlNode.setXmlElementAttributeValue( "device", Type.STORAGE.toString() );
+ addedDisk = DiskStorage.createInstance( xmlNode );
+ }
+
+ return addedDisk;
+ }
+
+ /**
+ * Creates a disk device representing an existing Libvirt XML disk device element.
+ *
+ * @param xmlNode existing Libvirt XML disk device element.
+ * @return disk device instance.
+ */
+ public static Disk newInstance( LibvirtXmlNode xmlNode )
+ {
+ Disk deviceDisk = null;
+ Type type = Type.fromString( xmlNode.getXmlElementAttributeValue( "device" ) );
+
+ if ( type == null ) {
+ return null;
+ }
+
+ switch ( type ) {
+ case CDROM:
+ deviceDisk = DiskCdrom.newInstance( xmlNode );
+ break;
+ case FLOPPY:
+ deviceDisk = DiskFloppy.newInstance( xmlNode );
+ break;
+ case STORAGE:
+ deviceDisk = DiskStorage.newInstance( xmlNode );
+ break;
+ }
+
+ return deviceDisk;
+ }
+
+ /**
+ * Type of disk device.
+ *
+ * Indicates how a disk is to be exposed to the guest OS.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ enum Type
+ {
+ // @formatter:off
+ CDROM ( "cdrom" ),
+ FLOPPY ( "floppy" ),
+ STORAGE( "disk" );
+ // @formatter:on
+
+ /**
+ * Name of the disk device type.
+ */
+ private String type = null;
+
+ /**
+ * Creates disk device type.
+ *
+ * @param type valid name of the disk device type in a Libvirt domain XML document.
+ */
+ Type( String type )
+ {
+ this.type = type;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.type;
+ }
+
+ /**
+ * Creates disk device type from its name with error check.
+ *
+ * @param type name of the disk device type in a Libvirt domain XML document.
+ * @return valid disk device type.
+ */
+ public static Type fromString( String type )
+ {
+ for ( Type t : Type.values() ) {
+ if ( t.type.equalsIgnoreCase( type ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Storage type of a disk device.
+ *
+ * The storage type refers to the underlying source for the disk.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum StorageType
+ {
+ // @formatter:off
+ FILE ( "file" ),
+ BLOCK ( "block" );
+ // @formatter:on
+
+ /**
+ * Name of the disk device type.
+ */
+ private String storageType = null;
+
+ /**
+ * Creates disk device storage type.
+ *
+ * @param storageType valid name of the disk device storage type in a Libvirt domain XML
+ * document.
+ */
+ StorageType( String storageType )
+ {
+ this.storageType = storageType;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.storageType;
+ }
+
+ /**
+ * Creates disk device storage type from its name with error check.
+ *
+ * @param storageType name of the disk device storage type in a Libvirt domain XML document.
+ * @return valid disk device storage type.
+ */
+ public static StorageType fromString( String storageType )
+ {
+ for ( StorageType t : StorageType.values() ) {
+ if ( t.storageType.equalsIgnoreCase( storageType ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Bus type (IDE, SATA, ...) of a disk device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum BusType
+ {
+ // @formatter:off
+ IDE ( "ide" ),
+ FDC ( "fdc" ),
+ SATA ( "sata" ),
+ SCSI ( "scsi" ),
+ SD ( "sd" ),
+ USB ( "usb" ),
+ VIRTIO( "virtio" ),
+ XEN ( "xen" );
+ // @formatter:on
+
+ /**
+ * Name of the disk device bus type.
+ */
+ private String busType = null;
+
+ /**
+ * Creates disk device bus type.
+ *
+ * @param busType valid name of the disk device bus type in a Libvirt domain XML document.
+ */
+ BusType( String busType )
+ {
+ this.busType = busType;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.busType;
+ }
+
+ /**
+ * Creates disk device bus type from its name with error check.
+ *
+ * @param busType name of the disk device bus type in a Libvirt domain XML document.
+ * @return valid disk device bus type.
+ */
+ public static BusType fromString( String busType )
+ {
+ for ( BusType t : BusType.values() ) {
+ if ( t.busType.equalsIgnoreCase( busType ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/DiskCdrom.java b/src/main/java/org/openslx/libvirt/domain/device/DiskCdrom.java
new file mode 100644
index 0000000..2ae08e1
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/DiskCdrom.java
@@ -0,0 +1,55 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A CDROM disk device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class DiskCdrom extends Disk
+{
+ /**
+ * Creates an empty CDROM disk device.
+ */
+ public DiskCdrom()
+ {
+ super();
+ }
+
+ /**
+ * Creates a CDROM disk device representing an existing Libvirt XML CDROM disk device element.
+ *
+ * @param xmlNode existing Libvirt XML CDROM disk device element.
+ */
+ public DiskCdrom( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+
+ // restrict CDROM disk device default read/write access always to readonly
+ this.setReadOnly( true );
+ }
+
+ /**
+ * Creates a non-existent CDROM disk device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created CDROM disk device instance.
+ */
+ public static DiskCdrom createInstance( LibvirtXmlNode xmlNode )
+ {
+ return DiskCdrom.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a CDROM disk device representing an existing Libvirt XML CDROM disk device element.
+ *
+ * @param xmlNode existing Libvirt XML CDROM disk device element.
+ * @return CDROM disk device instance.
+ */
+ public static DiskCdrom newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new DiskCdrom( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/DiskFloppy.java b/src/main/java/org/openslx/libvirt/domain/device/DiskFloppy.java
new file mode 100644
index 0000000..ce9733b
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/DiskFloppy.java
@@ -0,0 +1,52 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A floppy disk device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class DiskFloppy extends Disk
+{
+ /**
+ * Creates an empty floppy disk device.
+ */
+ public DiskFloppy()
+ {
+ super();
+ }
+
+ /**
+ * Creates a floppy disk device representing an existing Libvirt XML floppy disk device element.
+ *
+ * @param xmlNode existing Libvirt XML floppy disk device element.
+ */
+ public DiskFloppy( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent floppy disk device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created floppy disk device instance.
+ */
+ public static DiskFloppy createInstance( LibvirtXmlNode xmlNode )
+ {
+ return DiskFloppy.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a floppy disk device representing an existing Libvirt XML floppy disk device element.
+ *
+ * @param xmlNode existing Libvirt XML floppy disk device element.
+ * @return floppy disk device instance.
+ */
+ public static DiskFloppy newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new DiskFloppy( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/DiskStorage.java b/src/main/java/org/openslx/libvirt/domain/device/DiskStorage.java
new file mode 100644
index 0000000..7fca789
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/DiskStorage.java
@@ -0,0 +1,54 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A storage (HDD, SSD, ...) disk device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class DiskStorage extends Disk
+{
+ /**
+ * Creates an empty storage disk device.
+ */
+ public DiskStorage()
+ {
+ super();
+ }
+
+ /**
+ * Creates a storage disk device representing an existing Libvirt XML storage disk device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML storage disk device element.
+ */
+ public DiskStorage( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent storage disk device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created storage disk device instance.
+ */
+ public static DiskStorage createInstance( LibvirtXmlNode xmlNode )
+ {
+ return DiskStorage.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a storage disk device representing an existing Libvirt XML storage disk device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML storage disk device element.
+ * @return storage disk device instance.
+ */
+ public static DiskStorage newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new DiskStorage( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/Graphics.java b/src/main/java/org/openslx/libvirt/domain/device/Graphics.java
new file mode 100644
index 0000000..314dba2
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/Graphics.java
@@ -0,0 +1,138 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A graphics (display) device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Graphics extends Device
+{
+ /**
+ * Creates an empty graphics device.
+ */
+ public Graphics()
+ {
+ super();
+ }
+
+ /**
+ * Creates a graphics device representing an existing Libvirt XML graphics device element.
+ *
+ * @param xmlNode existing Libvirt XML graphics device element.
+ */
+ public Graphics( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent graphics device as Libvirt XML device element.
+ *
+ * @param graphics graphics device that is created.
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created graphics device instance.
+ */
+ public static Graphics createInstance( Graphics graphics, LibvirtXmlNode xmlNode )
+ {
+ Graphics addedGraphics = null;
+
+ if ( graphics instanceof GraphicsSdl ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.SDL.toString() );
+ addedGraphics = GraphicsSdl.createInstance( xmlNode );
+ } else if ( graphics instanceof GraphicsSpice ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.SPICE.toString() );
+ addedGraphics = GraphicsSpice.createInstance( xmlNode );
+ } else if ( graphics instanceof GraphicsVnc ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.VNC.toString() );
+ addedGraphics = GraphicsVnc.createInstance( xmlNode );
+ }
+
+ return addedGraphics;
+ }
+
+ /**
+ * Creates a graphics device representing an existing Libvirt XML graphics device element.
+ *
+ * @param xmlNode existing Libvirt XML graphics device element.
+ * @return graphics device instance.
+ */
+ public static Graphics newInstance( LibvirtXmlNode xmlNode )
+ {
+ Graphics deviceGraphics = null;
+ Type type = Type.fromString( xmlNode.getXmlElementAttributeValue( "type" ) );
+
+ if ( type == null ) {
+ return null;
+ }
+
+ switch ( type ) {
+ case SDL:
+ deviceGraphics = GraphicsSdl.newInstance( xmlNode );
+ break;
+ case SPICE:
+ deviceGraphics = GraphicsSpice.newInstance( xmlNode );
+ break;
+ case VNC:
+ deviceGraphics = GraphicsVnc.newInstance( xmlNode );
+ break;
+ }
+
+ return deviceGraphics;
+ }
+
+ /**
+ * Type of graphics device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ enum Type
+ {
+ // @formatter:off
+ SDL ( "sdl" ),
+ SPICE( "spice" ),
+ VNC ( "vnc" );
+ // @formatter:on
+
+ /**
+ * Name of graphics device type.
+ */
+ private String type = null;
+
+ /**
+ * Creates graphics device type.
+ *
+ * @param type valid name of the graphics device type in a Libvirt domain XML document.
+ */
+ Type( String type )
+ {
+ this.type = type;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.type;
+ }
+
+ /**
+ * Creates graphics device type from its name with error check.
+ *
+ * @param type name of the graphics device type in a Libvirt domain XML document.
+ * @return valid graphics device type.
+ */
+ public static Type fromString( String type )
+ {
+ for ( Type t : Type.values() ) {
+ if ( t.type.equalsIgnoreCase( type ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/GraphicsSdl.java b/src/main/java/org/openslx/libvirt/domain/device/GraphicsSdl.java
new file mode 100644
index 0000000..c77f56d
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/GraphicsSdl.java
@@ -0,0 +1,54 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A graphics SDL device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class GraphicsSdl extends Graphics
+{
+ /**
+ * Creates an empty graphics SDL device.
+ */
+ public GraphicsSdl()
+ {
+ super();
+ }
+
+ /**
+ * Creates a graphics SDL device representing an existing Libvirt XML graphics SDL device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML graphics SDL device element.
+ */
+ public GraphicsSdl( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent graphics SDL device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created graphics SDL device instance.
+ */
+ public static GraphicsSdl createInstance( LibvirtXmlNode xmlNode )
+ {
+ return GraphicsSdl.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a graphics SDL device representing an existing Libvirt XML graphics SDL device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML graphics SDL device element.
+ * @return graphics SDL device instance.
+ */
+ public static GraphicsSdl newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new GraphicsSdl( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/GraphicsSpice.java b/src/main/java/org/openslx/libvirt/domain/device/GraphicsSpice.java
new file mode 100644
index 0000000..f087296
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/GraphicsSpice.java
@@ -0,0 +1,74 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A graphics SPICE device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class GraphicsSpice extends Graphics
+{
+ /**
+ * Creates an empty graphics SPICE device.
+ */
+ public GraphicsSpice()
+ {
+ super();
+ }
+
+ /**
+ * Creates a graphics SPICE device representing an existing Libvirt XML graphics SPICE device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML graphics SPCIE device element.
+ */
+ public GraphicsSpice( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns the state whether OpenGL hardware acceleration is enabled or not.
+ *
+ * @return tate whether OpenGL hardware acceleration is enabled or not.
+ */
+ public boolean isOpenGlEnabled()
+ {
+ return this.getXmlElementAttributeValueAsBool( "gl", "enable" );
+ }
+
+ /**
+ * Sets the state whether OpenGL hardware acceleration is enabled or not.
+ *
+ * @param enabled state whether OpenGL hardware acceleration is enabled or not.
+ */
+ public void setOpenGl( boolean enabled )
+ {
+ this.setXmlElementAttributeValue( "gl", "enable", enabled );
+ }
+
+ /**
+ * Creates a non-existent graphics SPICE device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created graphics SPICE device instance.
+ */
+ public static GraphicsSpice createInstance( LibvirtXmlNode xmlNode )
+ {
+ return GraphicsSpice.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a graphics SPICE device representing an existing Libvirt XML graphics SPICE device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML graphics SPICE device element.
+ * @return graphics SPICE device instance.
+ */
+ public static GraphicsSpice newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new GraphicsSpice( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/GraphicsVnc.java b/src/main/java/org/openslx/libvirt/domain/device/GraphicsVnc.java
new file mode 100644
index 0000000..f595699
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/GraphicsVnc.java
@@ -0,0 +1,54 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A graphics VNC device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class GraphicsVnc extends Graphics
+{
+ /**
+ * Creates an empty graphics VNC device.
+ */
+ public GraphicsVnc()
+ {
+ super();
+ }
+
+ /**
+ * Creates a graphics VNC device representing an existing Libvirt XML graphics VNC device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML graphics VNC device element.
+ */
+ public GraphicsVnc( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent graphics VNC device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created graphics VNC device instance.
+ */
+ public static GraphicsVnc createInstance( LibvirtXmlNode xmlNode )
+ {
+ return GraphicsVnc.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a graphics VNC device representing an existing Libvirt XML graphics VNC device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML graphics VNC device element.
+ * @return graphics VNC device instance.
+ */
+ public static GraphicsVnc newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new GraphicsVnc( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/Hostdev.java b/src/main/java/org/openslx/libvirt/domain/device/Hostdev.java
new file mode 100644
index 0000000..cb09099
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/Hostdev.java
@@ -0,0 +1,139 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A hostdev device node in a Libvirt domain XML document for PCI, USB, ... passthrough.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Hostdev extends Device
+{
+ /**
+ * Creates an empty hostdev device.
+ */
+ public Hostdev()
+ {
+ super();
+ }
+
+ /**
+ * Creates a hostdev device representing an existing Libvirt XML hostdev device element.
+ *
+ * @param xmlNode existing Libvirt XML hostdev device element.
+ */
+ public Hostdev( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Removes boot oder entry of the hostdev device.
+ */
+ public void removeBootOrder()
+ {
+ this.removeXmlElement( "boot" );
+ }
+
+ /**
+ * Creates a non-existent hostdev device as Libvirt XML device element.
+ *
+ * @param hostdev hostdev device that is created.
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created hostdev device instance.
+ */
+ public static Hostdev createInstance( Hostdev hostdev, LibvirtXmlNode xmlNode )
+ {
+ Hostdev addedHostdev = null;
+
+ if ( hostdev instanceof HostdevPci ) {
+ xmlNode.setXmlElementAttributeValue( "device", Type.PCI.toString() );
+ addedHostdev = HostdevPci.createInstance( xmlNode );
+ } else if ( hostdev instanceof HostdevUsb ) {
+ xmlNode.setXmlElementAttributeValue( "device", Type.USB.toString() );
+ addedHostdev = HostdevUsb.createInstance( xmlNode );
+ }
+
+ return addedHostdev;
+ }
+
+ /**
+ * Creates a hostdev device representing an existing Libvirt XML hostdev device element.
+ *
+ * @param xmlNode existing Libvirt XML hostdev device element.
+ * @return hostdev device instance.
+ */
+ public static Hostdev newInstance( LibvirtXmlNode xmlNode )
+ {
+ Hostdev deviceHostdev = null;
+ Type type = Type.fromString( xmlNode.getXmlElementAttributeValue( "type" ) );
+
+ if ( type == null ) {
+ return null;
+ }
+
+ switch ( type ) {
+ case PCI:
+ deviceHostdev = HostdevPci.newInstance( xmlNode );
+ break;
+ case USB:
+ deviceHostdev = HostdevUsb.newInstance( xmlNode );
+ break;
+ }
+
+ return deviceHostdev;
+ }
+
+ /**
+ * Type of hostdev device subsystem passthrough.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ enum Type
+ {
+ // @formatter:off
+ PCI( "pci" ),
+ USB( "usb" );
+ // @formatter:on
+
+ /**
+ * Name of the hostdev device type.
+ */
+ private String type = null;
+
+ /**
+ * Creates hostdev device type.
+ *
+ * @param type valid name of the hostdev device type in a Libvirt domain XML document.
+ */
+ Type( String type )
+ {
+ this.type = type;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.type;
+ }
+
+ /**
+ * Creates hostdev device type from its name with error check.
+ *
+ * @param type name of the hostdev device storage in a Libvirt domain XML document.
+ * @return valid hostdev device type.
+ */
+ public static Type fromString( String type )
+ {
+ for ( Type t : Type.values() ) {
+ if ( t.type.equalsIgnoreCase( type ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevPci.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevPci.java
new file mode 100644
index 0000000..3b26fb0
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevPci.java
@@ -0,0 +1,79 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A hostdev PCI device node in a Libvirt domain XML document for PCI passthrough.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class HostdevPci extends Hostdev
+{
+ /**
+ * Creates an empty hostdev PCI device.
+ */
+ public HostdevPci()
+ {
+ super();
+ }
+
+ /**
+ * Creates a hostdev PCI device representing an existing Libvirt XML hostdev PCI device element.
+ *
+ * @param xmlNode existing Libvirt XML hostdev PCI device element.
+ */
+ public HostdevPci( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Checks if PCI hostdev device is managed.
+ *
+ * If {@link #isManaged()} returns <code>true</code> the hostdev PCI device is detached from the
+ * host before being passed on to the guest and reattached to the host after the guest exits.
+ *
+ * @return state whether PCI hostdev device is managed.
+ */
+ public boolean isManaged()
+ {
+ return this.getXmlElementAttributeValueAsBool( "managed" );
+ }
+
+ /**
+ * Sets state whether PCI hostdev device is managed.
+ *
+ * If the <code>managed</code> parameter is set to <code>true</code> the PCI hostdev device is
+ * detached from the host before being passed on to the guest and reattached to the host after
+ * the guest exits.
+ *
+ * @return state whether PCI hostdev device is managed.
+ */
+ public void setManaged( boolean managed )
+ {
+ this.setXmlElementAttributeValue( "managed", managed );
+ }
+
+ /**
+ * Creates a non-existent hostdev PCI device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created hostdev PCI device instance.
+ */
+ public static HostdevPci createInstance( LibvirtXmlNode xmlNode )
+ {
+ return HostdevPci.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a hostdev PCI device representing an existing Libvirt XML hostdev PCI device element.
+ *
+ * @param xmlNode existing Libvirt XML hostdev PCI device element.
+ * @return hostdev PCI device instance.
+ */
+ public static HostdevPci newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new HostdevPci( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevUsb.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevUsb.java
new file mode 100644
index 0000000..e1fcc0c
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevUsb.java
@@ -0,0 +1,52 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A hostdev USB device node in a Libvirt domain XML document for USB passthrough.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class HostdevUsb extends Hostdev
+{
+ /**
+ * Creates an empty hostdev USB device.
+ */
+ public HostdevUsb()
+ {
+ super();
+ }
+
+ /**
+ * Creates a hostdev USB device representing an existing Libvirt XML hostdev USB device element.
+ *
+ * @param xmlNode existing Libvirt XML hostdev USB device element.
+ */
+ public HostdevUsb( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent hostdev USB device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created hostdev USB device instance.
+ */
+ public static HostdevUsb createInstance( LibvirtXmlNode xmlNode )
+ {
+ return HostdevUsb.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a hostdev USB device representing an existing Libvirt XML hostdev USB device element.
+ *
+ * @param xmlNode existing Libvirt XML hostdev USB device element.
+ * @return hostdev USB device instance.
+ */
+ public static HostdevUsb newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new HostdevUsb( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/Interface.java b/src/main/java/org/openslx/libvirt/domain/device/Interface.java
new file mode 100644
index 0000000..b09c7da
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/Interface.java
@@ -0,0 +1,316 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A network interface device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Interface extends Device
+{
+ /**
+ * Creates an empty network device.
+ */
+ public Interface()
+ {
+ super();
+ }
+
+ /**
+ * Creates a network device representing an existing Libvirt XML network interface device
+ * element.
+ *
+ * @param xmlNode existing Libvirt XML network interface device element.
+ */
+ public Interface( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns hardware model of the network device.
+ *
+ * @return hardware model of the network device.
+ */
+ public Model getModel()
+ {
+ String model = this.getXmlElementAttributeValue( "model", "type" );
+ return Model.fromString( model );
+ }
+
+ /**
+ * Sets hardware model for the network device.
+ *
+ * @param model hardware model for the network device.
+ */
+ public void setModel( Model model )
+ {
+ this.setXmlElementAttributeValue( "model", "type", model.toString() );
+ }
+
+ /**
+ * Returns type of the network device.
+ *
+ * @return type of the network device.
+ */
+ public Type getType()
+ {
+ return Type.fromString( this.getXmlElementAttributeValue( "type" ) );
+ }
+
+ /**
+ * Sets type of the network device.
+ *
+ * @return type of the network device.
+ */
+ public void setType(Type type)
+ {
+ String source = this.getSource();
+
+ // change type and set source again
+ this.setXmlElementAttributeValue( "type", type.toString() );
+ this.setSource( source );
+ }
+
+ /**
+ * Returns the source of the network device.
+ *
+ * @return source of the network device.
+ */
+ public String getSource()
+ {
+ Type type = this.getType();
+ String source = null;
+
+ switch ( type ) {
+ case BRIDGE:
+ source = this.getXmlElementAttributeValue( "source", "bridge" );
+ break;
+ case NETWORK:
+ source = this.getXmlElementAttributeValue( "source", "network" );
+ break;
+ }
+
+ return source;
+ }
+
+ /**
+ * Sets the source for the network device.
+ *
+ * @param source for the network device.
+ */
+ public void setSource( String source )
+ {
+ Type type = this.getType();
+
+ // remove all attributes from sub-element 'source'
+ this.removeXmlElementAttributes( "source" );
+
+ switch ( type ) {
+ case BRIDGE:
+ this.setXmlElementAttributeValue( "source", "bridge", source );
+ break;
+ case NETWORK:
+ this.setXmlElementAttributeValue( "source", "network", source );
+ break;
+ }
+ }
+
+ /**
+ * Removes boot oder entry of the network interface device.
+ */
+ public void removeBootOrder()
+ {
+ this.removeXmlElement( "boot" );
+ }
+
+ /**
+ * Removes network source of the network interface device.
+ */
+ public void removeSource()
+ {
+ this.removeXmlElement( "source" );
+ }
+
+ /**
+ * Creates a non-existent network interface device as Libvirt XML device element.
+ *
+ * @param iface network device that is created.
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created network device instance.
+ */
+ public static Interface createInstance( Interface iface, LibvirtXmlNode xmlNode )
+ {
+ Interface addedInterface = null;
+
+ if ( iface instanceof InterfaceBridge ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.BRIDGE.toString() );
+ addedInterface = InterfaceBridge.createInstance( xmlNode );
+ } else if ( iface instanceof InterfaceNetwork ) {
+ xmlNode.setXmlElementAttributeValue( "type", Type.NETWORK.toString() );
+ addedInterface = InterfaceNetwork.createInstance( xmlNode );
+ }
+
+ return addedInterface;
+ }
+
+ /**
+ * Creates a network interface device representing an existing Libvirt XML network interface
+ * device element.
+ *
+ * @param xmlNode existing Libvirt XML network interface device element.
+ * @return network interface device instance.
+ */
+ public static Interface newInstance( LibvirtXmlNode xmlNode )
+ {
+ Interface deviceInterface = null;
+ Type type = Type.fromString( xmlNode.getXmlElementAttributeValue( "type" ) );
+
+ if ( type == null ) {
+ return null;
+ }
+
+ switch ( type ) {
+ case BRIDGE:
+ deviceInterface = InterfaceBridge.newInstance( xmlNode );
+ break;
+ case NETWORK:
+ deviceInterface = InterfaceNetwork.newInstance( xmlNode );
+ break;
+ }
+
+ return deviceInterface;
+ }
+
+ /**
+ * Type of network interface device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum Type
+ {
+ // @formatter:off
+ BRIDGE ( "bridge" ),
+ NETWORK( "network" );
+ // @formatter:on
+
+ /**
+ * Name of the network interface device type.
+ */
+ private String type = null;
+
+ /**
+ * Creates network interface device type.
+ *
+ * @param type valid name of the network interface device type in a Libvirt domain XML
+ * document.
+ */
+ Type( String type )
+ {
+ this.type = type;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.type;
+ }
+
+ /**
+ * Creates network interface device type from its name with error check.
+ *
+ * @param type name of the network interface device type in a Libvirt domain XML document.
+ * @return valid network interface device type.
+ */
+ public static Type fromString( String type )
+ {
+ for ( Type t : Type.values() ) {
+ if ( t.type.equalsIgnoreCase( type ) ) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Model of network interface device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum Model
+ {
+ // @formatter:off
+ E1000 ( "e1000" ),
+ E1000_82544GC ( "e1000-82544gc" ),
+ E1000_82545EM ( "e1000-82545em" ),
+ E1000E ( "e1000e" ),
+ I82550 ( "i82550" ),
+ I82551 ( "i82551" ),
+ I82557A ( "i82557a" ),
+ I82557B ( "i82557b" ),
+ I82557C ( "i82557c" ),
+ I82558A ( "i82558a" ),
+ I82558B ( "i82558b" ),
+ I82559A ( "i82559a" ),
+ I82559B ( "i82559b" ),
+ I82559C ( "i82559c" ),
+ I82559ER ( "i82559er" ),
+ I82562 ( "i82562" ),
+ I82801 ( "i82801" ),
+ NE2K_PCI ( "ne2k_pci" ),
+ PCNET ( "pcnet" ),
+ RTL8139 ( "rtl8139" ),
+ TULIP ( "tulip" ),
+ VIRTIO ( "virtio" ),
+ VIRTIO_NET_PCI ( "virtio-net-pci" ),
+ VIRTIO_NET_PCI_NON_TRANSITIONAL( "virtio-net-pci-non-transitional" ),
+ VIRTIO_NET_PCI_TRANSITIONAL ( "virtio-net-pci-transitional" ),
+ VMXNET3 ( "vmxnet3" );
+ // @formatter:on
+
+ /**
+ * Name of the network interface device model.
+ */
+ private String model = null;
+
+ /**
+ * Creates network interface device model.
+ *
+ * @param type valid name of the network interface device model in a Libvirt domain XML
+ * document.
+ */
+ Model( String model )
+ {
+ this.model = model;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.model;
+ }
+
+ /**
+ * Creates network interface device model from its name with error check.
+ *
+ * @param type name of the network interface device model in a Libvirt domain XML document.
+ * @return valid network interface device model.
+ */
+ public static Model fromString( String model )
+ {
+ for ( Model m : Model.values() ) {
+ if ( m.model.equalsIgnoreCase( model ) ) {
+ return m;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/InterfaceBridge.java b/src/main/java/org/openslx/libvirt/domain/device/InterfaceBridge.java
new file mode 100644
index 0000000..02eabb0
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/InterfaceBridge.java
@@ -0,0 +1,54 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A network bridge interface device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class InterfaceBridge extends Interface
+{
+ /**
+ * Creates an empty network bridge interface device.
+ */
+ public InterfaceBridge()
+ {
+ super();
+ }
+
+ /**
+ * Creates a network bridge interface device representing an existing Libvirt XML network bridge
+ * interface device element.
+ *
+ * @param xmlNode existing Libvirt XML network bridge interface device element.
+ */
+ public InterfaceBridge( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent network bridge interface device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created network bridge interface device instance.
+ */
+ public static InterfaceBridge createInstance( LibvirtXmlNode xmlNode )
+ {
+ return InterfaceBridge.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a network bridge interface device representing an existing Libvirt XML network bridge
+ * interface device element.
+ *
+ * @param xmlNode existing Libvirt XML network bridge interface device element.
+ * @return network bridge interface device instance.
+ */
+ public static InterfaceBridge newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new InterfaceBridge( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/InterfaceNetwork.java b/src/main/java/org/openslx/libvirt/domain/device/InterfaceNetwork.java
new file mode 100644
index 0000000..ae1fd40
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/InterfaceNetwork.java
@@ -0,0 +1,54 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A network interface device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class InterfaceNetwork extends Interface
+{
+ /**
+ * Creates an empty network interface device.
+ */
+ public InterfaceNetwork()
+ {
+ super();
+ }
+
+ /**
+ * Creates a network interface device representing an existing Libvirt XML network interface
+ * device element.
+ *
+ * @param xmlNode existing Libvirt XML network interface device element.
+ */
+ public InterfaceNetwork( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Creates a non-existent network interface device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created network interface device device instance.
+ */
+ public static InterfaceNetwork createInstance( LibvirtXmlNode xmlNode )
+ {
+ return InterfaceNetwork.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a network interface device representing an existing Libvirt XML network interface
+ * device element.
+ *
+ * @param xmlNode existing Libvirt XML network interface device element.
+ * @return network interface device instance.
+ */
+ public static InterfaceNetwork newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new InterfaceNetwork( xmlNode );
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/Sound.java b/src/main/java/org/openslx/libvirt/domain/device/Sound.java
new file mode 100644
index 0000000..e424ed4
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/Sound.java
@@ -0,0 +1,128 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A sound device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Sound extends Device
+{
+ /**
+ * Creates an empty sound device.
+ */
+ public Sound()
+ {
+ super();
+ }
+
+ /**
+ * Creates a sound device representing an existing Libvirt XML sound device element.
+ *
+ * @param xmlNode existing Libvirt XML sound device element.
+ */
+ public Sound( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns hardware model of the sound device.
+ *
+ * @return hardware model of the sound device.
+ */
+ public Model getModel()
+ {
+ String model = this.getXmlElementAttributeValue( "model" );
+ return Model.fromString( model );
+ }
+
+ /**
+ * Sets hardware model for the sound device.
+ *
+ * @param model hardware model for the sound device.
+ */
+ public void setModel( Model model )
+ {
+ this.setXmlElementAttributeValue( "model", model.toString() );
+ }
+
+ /**
+ * Creates a non-existent sound device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created sound device instance.
+ */
+ public static Sound createInstance( LibvirtXmlNode xmlNode )
+ {
+ return Sound.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a sound device representing an existing Libvirt XML sound device element.
+ *
+ * @param xmlNode existing Libvirt XML sound device element.
+ * @return sound device instance.
+ */
+ public static Sound newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new Sound( xmlNode );
+ }
+
+ /**
+ * Model of sound device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum Model
+ {
+ // @formatter:off
+ AC97 ( "ac97" ),
+ ES1370( "es1370" ),
+ ICH6 ( "ich6" ),
+ ICH9 ( "ich9" ),
+ SB16 ( "sb16" );
+ // @formatter:on
+
+ /**
+ * Name of the sound device model.
+ */
+ private String model;
+
+ /**
+ * Creates sound device model.
+ *
+ * @param type valid name of the sound device model in a Libvirt domain XML document.
+ */
+ Model( String model )
+ {
+ this.model = model;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.model;
+ }
+
+ /**
+ * Creates sound device model from its name with error check.
+ *
+ * @param type name of the sound device model in a Libvirt domain XML document.
+ * @return valid sound device model.
+ */
+ public static Model fromString( String model )
+ {
+ for ( Model m : Model.values() ) {
+ if ( m.model.equalsIgnoreCase( model ) ) {
+ return m;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/Video.java b/src/main/java/org/openslx/libvirt/domain/device/Video.java
new file mode 100644
index 0000000..e901b85
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/domain/device/Video.java
@@ -0,0 +1,188 @@
+package org.openslx.libvirt.domain.device;
+
+import org.openslx.libvirt.xml.LibvirtXmlNode;
+
+/**
+ * A video (GPU) device node in a Libvirt domain XML document.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class Video extends Device
+{
+ /**
+ * Creates an empty video device.
+ */
+ public Video()
+ {
+ super();
+ }
+
+ /**
+ * Creates a video device representing an existing Libvirt XML video device element.
+ *
+ * @param xmlNode existing Libvirt XML video device element.
+ */
+ public Video( LibvirtXmlNode xmlNode )
+ {
+ super( xmlNode );
+ }
+
+ /**
+ * Returns hardware model of the video device.
+ *
+ * @return hardware model of the video device.
+ */
+ public Model getModel()
+ {
+ String model = this.getXmlElementAttributeValue( "model", "type" );
+ return Model.fromString( model );
+ }
+
+ /**
+ * Sets hardware model for the video device.
+ *
+ * @param model hardware model for the video device.
+ */
+ public void setModel( Model model )
+ {
+ this.setXmlElementAttributeValue( "model", "type", model.toString() );
+ }
+
+ /**
+ * Checks whether 2D hardware video acceleration is turned on or off.
+ *
+ * @return state of 2D hardware video acceleration.
+ */
+ public boolean get2DAcceleration()
+ {
+ return this.getXmlElementAttributeValueAsBool( "model/acceleration", "accel2d" );
+ }
+
+ /**
+ * Turns 2D hardware video acceleration on or off.
+ *
+ * @param acceleration state of 2D hardware video acceleration.
+ */
+ public void set2DAcceleration( boolean acceleration )
+ {
+ Model model = this.getModel();
+
+ if ( model != null ) {
+ if ( model == Model.VIRTIO ) {
+ // only set acceleration on supported Virtio GPUs
+ this.setXmlElementAttributeValue( "model/acceleration", "accel2d", acceleration );
+ } else {
+ String errorMsg = new String(
+ "Video card model '" + model.toString() + "' does not support enabled 2D hardware acceleration." );
+ throw new IllegalArgumentException( errorMsg );
+ }
+ }
+ }
+
+ /**
+ * Checks whether 3D hardware video acceleration is turned on or off.
+ *
+ * @return state of 3D hardware video acceleration.
+ */
+ public boolean get3DAcceleration()
+ {
+ return this.getXmlElementAttributeValueAsBool( "model/acceleration", "accel3d" );
+ }
+
+ /**
+ * Turns 3D hardware video acceleration on or off.
+ *
+ * @param acceleration state of 3D hardware video acceleration.
+ */
+ public void set3DAcceleration( boolean acceleration )
+ {
+ Model model = this.getModel();
+
+ if ( model == Model.VIRTIO ) {
+ // only set acceleration on supported Virtio GPUs
+ this.setXmlElementAttributeValue( "model/acceleration", "accel3d", acceleration );
+ } else {
+ String errorMsg = new String(
+ "Video card model '" + model.toString() + "' does not support enabled 3D hardware acceleration." );
+ throw new IllegalArgumentException( errorMsg );
+ }
+ }
+
+ /**
+ * Creates a non-existent video device as Libvirt XML device element.
+ *
+ * @param xmlNode Libvirt XML node of the Libvirt XML device that is created.
+ * @return created video device instance.
+ */
+ public static Video createInstance( LibvirtXmlNode xmlNode )
+ {
+ return Video.newInstance( xmlNode );
+ }
+
+ /**
+ * Creates a video device representing an existing Libvirt XML video device element.
+ *
+ * @param xmlNode existing Libvirt XML video device element.
+ * @return video device instance.
+ */
+ public static Video newInstance( LibvirtXmlNode xmlNode )
+ {
+ return new Video( xmlNode );
+ }
+
+ /**
+ * Model of video device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+ public enum Model
+ {
+ // @formatter:off
+ NONE ( "none" ),
+ QXL ( "qxl" ),
+ VGA ( "vga" ),
+ VMVGA ( "vmvga" ),
+ VIRTIO( "virtio" );
+ // @formatter:on
+
+ /**
+ * Name of the video device model.
+ */
+ private String model = null;
+
+ /**
+ * Creates video device model.
+ *
+ * @param type valid name of the video device model in a Libvirt domain XML document.
+ */
+ Model( String model )
+ {
+ this.model = model;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.model;
+ }
+
+ /**
+ * Creates video device model from its name with error check.
+ *
+ * @param type name of the video device model in a Libvirt domain XML document.
+ * @return valid video device model.
+ */
+ public static Model fromString( String model )
+ {
+ for ( Model m : Model.values() ) {
+ if ( m.model.equalsIgnoreCase( model ) ) {
+ return m;
+ }
+ }
+
+ return null;
+ }
+ }
+}
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..8fe642b
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlDocument.java
@@ -0,0 +1,373 @@
+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.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( InputStream 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, InputStream 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, InputStream 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, InputStream 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, InputStream 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 <i>yes</i>, the returned {@link boolean}
+ * value is set to <i>true</i>. Otherwise, if the text value of the XML attribute equals to
+ * <i>no</i>, the returned {@link boolean} value is set to <i>false</i>.
+ *
+ * @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 <i>yes</i>, the returned {@link boolean}
+ * value is set to <i>true</i>. Otherwise, if the text value of the XML attribute equals to
+ * <i>no</i>, the returned {@link boolean} value is set to <i>false</i>.
+ *
+ * @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 <i>true</i>, the text value of the
+ * selected XML attribute is set to <i>yes</i>. Otherwise, if the binary choice value for the
+ * selected XML attribute equals to <i>false</i>, the text value of the selected XML attribute is
+ * set to <i>no</i>.
+ *
+ * @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 <i>true</i>, the text value of the
+ * selected XML attribute is set to <i>yes</i>. Otherwise, if the binary choice value for the
+ * selected XML attribute equals to <i>false</i>, the text value of the selected XML attribute is
+ * set to <i>no</i>.
+ *
+ * @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..e074948
--- /dev/null
+++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlSchemaValidator.java
@@ -0,0 +1,282 @@
+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.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( InputStream 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 <code>rngSchema</code> has failed.
+ */
+ private void createValidationContext( InputStream 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( new StreamSource( 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
+ * <code>com.thaiopensource.relaxng.jaxp.XMLSyntaxSchemaFactory</code>.
+ */
+ 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 <code>xmlDocument</code> is non-null, otherwise the
+ * validation succeeds immediately. If the validation of the <code>xmlDocument</code> 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/main/java/org/openslx/util/vm/DiskImage.java b/src/main/java/org/openslx/util/vm/DiskImage.java
index 15b3800..617fadd 100644
--- a/src/main/java/org/openslx/util/vm/DiskImage.java
+++ b/src/main/java/org/openslx/util/vm/DiskImage.java
@@ -5,6 +5,8 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.Virtualizer;
@@ -26,8 +28,7 @@ public class DiskImage
public enum ImageFormat
{
- VMDK( "vmdk" ), QCOW2( "qcow2" ), VDI( "vdi" ),
- DOCKER("dockerfile");
+ VMDK( "vmdk" ), QCOW2( "qcow2" ), VDI( "vdi" ), DOCKER( "dockerfile" );
public final String extension;
@@ -53,7 +54,7 @@ public class DiskImage
return VDI;
if ( virtId.equals( TConst.VIRT_QEMU ) )
return QCOW2;
- if ( virtId.equals( TConst.VIRT_DOCKER) )
+ if ( virtId.equals( TConst.VIRT_DOCKER ) )
return DOCKER;
return null;
}
@@ -166,23 +167,193 @@ public class DiskImage
return;
}
- // TODO: qcow
+ // qcow2 disk image
file.seek( 0 );
if ( file.readInt() == QEMU_MAGIC ) {
- // dummy values
- this.isStandalone = true;
- this.isCompressed = false;
- this.isSnapshot = false;
+ //
+ // qcow2 (version 2 and 3) header format:
+ //
+ // magic (4 byte)
+ // version (4 byte)
+ // backing_file_offset (8 byte)
+ // backing_file_size (4 byte)
+ // cluster_bits (4 byte)
+ // size (8 byte)
+ // crypt_method (4 byte)
+ // l1_size (4 byte)
+ // l1_table_offset (8 byte)
+ // refcount_table_offset (8 byte)
+ // refcount_table_clusters (4 byte)
+ // nb_snapshots (4 byte)
+ // snapshots_offset (8 byte)
+ // incompatible_features (8 byte) [*]
+ // compatible_features (8 byte) [*]
+ // autoclear_features (8 byte) [*]
+ // refcount_order (8 byte) [*]
+ // header_length (4 byte) [*]
+ //
+ // [*] these fields are only available in the qcow2 version 3 header format
+ //
+
+ //
+ // check qcow2 file format version
+ //
+ file.seek( 4 );
+ final int qcowVersion = file.readInt();
+ if ( qcowVersion < 2 || qcowVersion > 3 ) {
+ // disk image format is not a qcow2 disk format
+ throw new UnknownImageFormatException();
+ } else {
+ // disk image format is a valid qcow2 disk format
+ this.hwVersion = qcowVersion;
+ this.subFormat = null;
+ }
+
+ //
+ // check if qcow2 image does not refer to any backing file
+ //
+ file.seek( 8 );
+ this.isStandalone = ( file.readLong() == 0 ) ? true : false;
+
+ //
+ // check if qcow2 image does not contain any snapshot
+ //
+ file.seek( 56 );
+ this.isSnapshot = ( file.readInt() == 0 ) ? true : false;
+
+ //
+ // check if qcow2 image uses extended L2 tables
+ //
+ boolean qcowUseExtendedL2 = false;
+
+ // extended L2 tables are only possible in qcow2 version 3 header format
+ if ( qcowVersion == 3 ) {
+ // read incompatible feature bits
+ file.seek( 72 );
+ final long qcowIncompatibleFeatures = file.readLong();
+
+ // support for extended L2 tables is enabled if bit 4 is set
+ qcowUseExtendedL2 = ( ( ( qcowIncompatibleFeatures & 0x000000000010 ) >>> 4 ) == 1 );
+ }
+
+ //
+ // check if qcow2 image contains compressed clusters
+ //
+ boolean qcowCompressed = false;
+
+ // get number of entries in L1 table
+ file.seek( 36 );
+ final int qcowL1TableSize = file.readInt();
+
+ // check if a valid L1 table is present
+ if ( qcowL1TableSize > 0 ) {
+ // qcow2 image contains active L1 table with more than 0 entries: l1_size > 0
+ // search for first L2 table and its first entry to get compression bit
+
+ // get cluster bits to calculate the cluster size
+ file.seek( 20 );
+ final int qcowClusterBits = file.readInt();
+ final int qcowClusterSize = ( 1 << qcowClusterBits );
+
+ // entries of a L1 table have always the size of 8 byte (64 bit)
+ final int qcowL1TableEntrySize = 8;
+
+ // entries of a L2 table have either the size of 8 or 16 byte (64 or 128 bit)
+ final int qcowL2TableEntrySize = ( qcowUseExtendedL2 ) ? 16 : 8;
+
+ // calculate number of L2 table entries
+ final int qcowL2TableSize = qcowClusterSize / qcowL2TableEntrySize;
+
+ // get offset of L1 table
+ file.seek( 40 );
+ long qcowL1TableOffset = file.readLong();
+
+ // check for each L2 table referenced from an L1 table its entries
+ // until a compressed cluster descriptor is found
+ for ( long i = 0; i < qcowL1TableSize; i++ ) {
+ // get offset of current L2 table from the current L1 table entry
+ long qcowL1TableEntryOffset = qcowL1TableOffset + ( i * qcowL1TableEntrySize );
+ file.seek( qcowL1TableEntryOffset );
+ long qcowL1TableEntry = file.readLong();
+
+ // extract offset (bits 9 - 55) from L1 table entry
+ long qcowL2TableOffset = ( qcowL1TableEntry & 0x00fffffffffffe00L );
+
+ if ( qcowL2TableOffset == 0 ) {
+ // L2 table and all clusters described by this L2 table are unallocated
+ continue;
+ }
+
+ // get each L2 table entry and check if it is a compressed cluster descriptor
+ for ( long j = 0; j < qcowL2TableSize; j++ ) {
+ // get current L2 table entry
+ long qcowL2TableEntryOffset = qcowL2TableOffset + ( j * qcowL2TableEntrySize );
+ file.seek( qcowL2TableEntryOffset );
+ long qcowL2TableEntry = file.readLong();
+
+ // extract cluster type (standard or compressed) (bit 62)
+ boolean qcowClusterCompressed = ( ( ( qcowL2TableEntry & 0x4000000000000000L ) >>> 62 ) == 1 );
+
+ // check if qcow2 disk image contains at least one compressed cluster descriptor
+ if ( qcowClusterCompressed ) {
+ qcowCompressed = true;
+ break;
+ }
+ }
+
+ // terminate if one compressed cluster descriptor is already found
+ if ( qcowCompressed ) {
+ break;
+ }
+ }
+ } else {
+ // qcow2 image does not contain an active L1 table with any entry: l1_size = 0
+ qcowCompressed = false;
+ }
+
+ this.isCompressed = qcowCompressed;
this.format = ImageFormat.QCOW2;
- this.subFormat = null;
this.diskDescription = null;
- this.hwVersion = 0;
+
return;
}
}
throw new UnknownImageFormatException();
}
+
+ /**
+ * Creates new disk image and checks if image is supported by hypervisor's image formats.
+ *
+ * @param disk file to a disk storing the virtual machine content.
+ * @param supportedImageTypes list of supported image types of a hypervisor's image format.
+ * @throws FileNotFoundException cannot find virtual machine image file.
+ * @throws IOException cannot access the virtual machine image file.
+ * @throws UnknownImageFormatException virtual machine image file has an unknown image format.
+ */
+ public DiskImage( File disk, List<ImageFormat> supportedImageTypes )
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ this( disk );
+ if ( !this.isSupportedByHypervisor( supportedImageTypes ) ) {
+ throw new UnknownImageFormatException();
+ }
+ }
+
+ /**
+ * Checks if image format is supported by a list of supported hypervisor image formats.
+ *
+ * @param supportedImageTypes list of supported image types of a hypervisor.
+ * @return <code>true</code> if image type is supported by the hypervisor; otherwise
+ * <code>false</code>.
+ */
+ public boolean isSupportedByHypervisor( List<ImageFormat> supportedImageTypes )
+ {
+ Predicate<ImageFormat> matchDiskFormat = supportedImageType -> supportedImageType.toString()
+ .equalsIgnoreCase( this.getImageFormat().toString() );
+ return supportedImageTypes.stream().anyMatch( matchDiskFormat );
+ }
+
private int findNull( byte[] buffer )
{
for ( int i = 0; i < buffer.length; ++i ) {
diff --git a/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java b/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java
index eea4d8b..246f390 100644
--- a/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java
+++ b/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java
@@ -3,16 +3,25 @@ package org.openslx.util.vm;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.thrifthelper.TConst;
+import org.openslx.util.vm.DiskImage.ImageFormat;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
public class DockerMetaDataDummy extends VmMetaData {
- private static final Logger LOGGER = Logger.getLogger(DockerMetaDataDummy.class);
+ /**
+ * List of supported image formats by the Docker hypervisor.
+ */
+ private static final List<DiskImage.ImageFormat> SUPPORTED_IMAGE_FORMATS = Collections.unmodifiableList(
+ Arrays.asList( ImageFormat.DOCKER ) );
+
+ private static final Logger LOGGER = Logger.getLogger( DockerMetaDataDummy.class);
private final Virtualizer virtualizer = new Virtualizer(TConst.VIRT_DOCKER, "Docker");
@@ -71,6 +80,12 @@ public class DockerMetaDataDummy extends VmMetaData {
@Override public byte[] getFilteredDefinitionArray() {
return containerDefinition;
}
+
+ @Override
+ public List<DiskImage.ImageFormat> getSupportedImageFormats()
+ {
+ return DockerMetaDataDummy.SUPPORTED_IMAGE_FORMATS;
+ }
@Override public void applySettingsForLocalEdit() {
diff --git a/src/main/java/org/openslx/util/vm/QemuMetaData.java b/src/main/java/org/openslx/util/vm/QemuMetaData.java
index 201ffd8..d3b8451 100644
--- a/src/main/java/org/openslx/util/vm/QemuMetaData.java
+++ b/src/main/java/org/openslx/util/vm/QemuMetaData.java
@@ -1,233 +1,854 @@
package org.openslx.util.vm;
import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
+import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
+import java.util.Map.Entry;
-import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.OperatingSystem;
import org.openslx.bwlp.thrift.iface.Virtualizer;
+import org.openslx.libvirt.domain.Domain;
+import org.openslx.libvirt.domain.device.ControllerUsb;
+import org.openslx.libvirt.domain.device.Disk.BusType;
+import org.openslx.libvirt.domain.device.Disk.StorageType;
+import org.openslx.libvirt.domain.device.DiskCdrom;
+import org.openslx.libvirt.domain.device.DiskFloppy;
+import org.openslx.libvirt.domain.device.DiskStorage;
+import org.openslx.libvirt.domain.device.Graphics;
+import org.openslx.libvirt.domain.device.GraphicsSpice;
+import org.openslx.libvirt.domain.device.Interface;
+import org.openslx.libvirt.domain.device.Sound;
+import org.openslx.libvirt.domain.device.Video;
+import org.openslx.libvirt.xml.LibvirtXmlDocumentException;
+import org.openslx.libvirt.xml.LibvirtXmlSerializationException;
+import org.openslx.libvirt.xml.LibvirtXmlValidationException;
import org.openslx.thrifthelper.TConst;
import org.openslx.util.vm.DiskImage.ImageFormat;
-import org.openslx.util.vm.DiskImage.UnknownImageFormatException;
-public class QemuMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta, VBoxHWVersionMeta, VBoxEthernetDevTypeMeta, VBoxUsbSpeedMeta>
+/**
+ * Metadata to describe the hardware type of a QEMU sound card.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+class QemuSoundCardMeta
{
+ /**
+ * Stores the hardware model of the QEMU sound card.
+ */
+ private final Sound.Model model;
+
+ /**
+ * Creates metadata to describe the hardware model of a QEMU sound card.
+ *
+ * @param model hardware model of the QEMU sound card.
+ */
+ public QemuSoundCardMeta( Sound.Model model )
+ {
+ this.model = model;
+ }
- private final Map<String, String> arguments = new HashMap<String, String>();
- // the above map's elements will take the place of <args> in the config string
- private String config;
- private static final Logger LOGGER = Logger.getLogger( QemuMetaData.class );
+ /**
+ * Returns hardware model of the QEMU sound card.
+ *
+ * @return hardware model of the QEMU sound card.
+ */
+ public Sound.Model getModel()
+ {
+ return this.model;
+ }
+}
- private static final Virtualizer virtualizer = new Virtualizer( TConst.VIRT_QEMU, "QEMU-KVM" );
+/**
+ * Metadata to describe the hardware acceleration state of QEMU virtual graphics.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+class QemuDDAccelMeta
+{
+ /**
+ * Stores state of the hardware acceleration for QEMU virtual graphics.
+ */
+ private final boolean enabled;
+
+ /**
+ * Creates metadata to describe the hardware acceleration state of QEMU virtual graphics.
+ *
+ * @param enabled state of the hardware acceleration for QEMU virtual graphics.
+ */
+ public QemuDDAccelMeta( boolean enabled )
+ {
+ this.enabled = enabled;
+ }
- public QemuMetaData( List<OperatingSystem> osList, File file ) throws FileNotFoundException, IOException, UnsupportedVirtualizerFormatException
+ /**
+ * Returns state of the hardware acceleration of QEMU virtual graphics.
+ *
+ * @return state of the hardware acceleration for QEMU virtual graphics.
+ */
+ public boolean isEnabled()
+ {
+ return this.enabled;
+ }
+}
+
+/**
+ * Metadata to describe the version of a QEMU virtual machine configuration.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+class QemuHWVersionMeta
+{
+ /**
+ * Stores the version of a QEMU virtual machine configuration.
+ */
+ private final int version;
+
+ /**
+ * Creates metadata to describe the version of a QEMU virtual machine configuration.
+ *
+ * @param version version of the QEMU virtual machine configuration.
+ */
+ public QemuHWVersionMeta( int version )
+ {
+ this.version = version;
+ }
+
+ /**
+ * Returns version of the QEMU virtual machine configuration.
+ *
+ * @return version of the QEMU virtual machine configuration.
+ */
+ public int getVersion()
+ {
+ return this.version;
+ }
+}
+
+/**
+ * Metadata to describe the hardware type of a QEMU ethernet device.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+class QemuEthernetDevTypeMeta
+{
+ /**
+ * Stores the hardware model of the QEMU ethernet device.
+ */
+ private final Interface.Model model;
+
+ /**
+ * Creates metadata to describe the hardware type of a QEMU ethernet device.
+ *
+ * @param model hardware type of the QEMU ethernet device.
+ */
+ public QemuEthernetDevTypeMeta( Interface.Model model )
+ {
+ this.model = model;
+ }
+
+ /**
+ * Returns the hardware type of a QEMU ethernet device.
+ *
+ * @return hardware type of the QEMU ethernet device.
+ */
+ public Interface.Model getModel()
+ {
+ return this.model;
+ }
+}
+
+/**
+ * Metadata to describe a QEMU USB controller.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+class QemuUsbSpeedMeta
+{
+ /**
+ * Stores the USB speed of the QEMU USB controller.
+ */
+ private final int speed;
+
+ /**
+ * Stores the QEMU hardware model of the USB controller.
+ */
+ private final ControllerUsb.Model model;
+
+ /**
+ * Creates metadata to describe a QEMU USB controller.
+ *
+ * @param speed USB speed of the QEMU USB controller.
+ * @param model QEMU hardware model of the USB controller.
+ */
+ public QemuUsbSpeedMeta( int speed, ControllerUsb.Model model )
+ {
+ this.speed = speed;
+ this.model = model;
+ }
+
+ /**
+ * Returns the speed of the QEMU USB controller.
+ *
+ * @return speed of the QEMU USB controller.
+ */
+ public int getSpeed()
+ {
+ return this.speed;
+ }
+
+ /**
+ * Returns QEMU hardware model of the USB controller.
+ *
+ * @return hardware model of the QEMU USB controller.
+ */
+ public ControllerUsb.Model getModel()
+ {
+ return this.model;
+ }
+}
+
+/**
+ * Virtual machine configuration (managed by Libvirt) for the QEMU hypervisor.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class QemuMetaData extends
+ VmMetaData<QemuSoundCardMeta, QemuDDAccelMeta, QemuHWVersionMeta, QemuEthernetDevTypeMeta, QemuUsbSpeedMeta>
+{
+ /**
+ * Default bridge name of the network bridge connected to the LAN.
+ */
+ public static final String NETWORK_DEFAULT_BRIDGE = "brBwLehrpool";
+
+ /**
+ * Default network name of the isolated host network (host only).
+ */
+ public static final String NETWORK_DEFAULT_HOST_ONLY = "host";
+
+ /**
+ * Default network name of the NAT network.
+ */
+ public static final String NETWORK_DEFAULT_NAT = "nat";
+
+ /**
+ * Default physical CDROM drive of the hypervisor host.
+ */
+ public static final String CDROM_DEFAULT_PHYSICAL_DRIVE = "/dev/sr0";
+
+ /**
+ * List of supported image formats by the QEMU hypervisor.
+ */
+ private static final List<DiskImage.ImageFormat> SUPPORTED_IMAGE_FORMATS = Collections.unmodifiableList(
+ Arrays.asList( ImageFormat.QCOW2, ImageFormat.VMDK, ImageFormat.VDI ) );
+
+ /**
+ * Representation of a QEMU hypervisor (managed by Libvirt).
+ */
+ private static final Virtualizer VIRTUALIZER = new Virtualizer( TConst.VIRT_QEMU, "QEMU" );
+
+ /**
+ * Libvirt XML configuration file to modify configuration of virtual machine for QEMU.
+ */
+ private Domain vmConfig = null;
+
+ /**
+ * Stores current index of added HDD device to the Libvirt XML configuration file.
+ */
+ private int vmDeviceIndexHddAdd = 0;
+
+ /**
+ * Stores current index of added CDROM device to the Libvirt XML configuration file.
+ */
+ private int vmDeviceIndexCdromAdd = 0;
+
+ /**
+ * Stores current index of added ethernet device to the Libvirt XML configuration file.
+ */
+ private int vmDeviceIndexEthernetAdd = 0;
+
+ /**
+ * Creates new virtual machine configuration (managed by Libvirt) for the QEMU hypervisor.
+ *
+ * @param osList list of operating systems.
+ * @param file image file for the QEMU hypervisor.
+ * @throws UnsupportedVirtualizerFormatException Libvirt XML configuration cannot be processed.
+ */
+ public QemuMetaData( List<OperatingSystem> osList, File file ) throws UnsupportedVirtualizerFormatException
{
super( osList );
- DiskImage di;
+
try {
- di = new DiskImage( file );
- } catch ( UnknownImageFormatException e ) {
- di = null;
+ // read and parse Libvirt domain XML configuration document
+ this.vmConfig = new Domain( file );
+ } catch ( LibvirtXmlDocumentException e ) {
+ throw new UnsupportedVirtualizerFormatException( e.getLocalizedMessage() );
+ } catch ( LibvirtXmlSerializationException e ) {
+ throw new UnsupportedVirtualizerFormatException( e.getLocalizedMessage() );
+ } catch ( LibvirtXmlValidationException e ) {
+ throw new UnsupportedVirtualizerFormatException( e.getLocalizedMessage() );
}
- if ( di == null || di.format != ImageFormat.QCOW2 ) {
- throw new UnsupportedVirtualizerFormatException( "This is not a qcow2 disk image" );
+
+ // register virtual hardware models for graphical editing of virtual devices (GPU, sound, USB, ...)
+ this.registerVirtualHW();
+
+ // set display name of VM
+ this.displayName = vmConfig.getName();
+
+ // this property cannot be checked with the Libvirt domain XML configuration
+ // to check if machine is in a paused/suspended state, look in the QEMU qcow2 image for snapshots and machine states
+ this.isMachineSnapshot = false;
+
+ // add HDDs, SSDs to QEMU metadata
+ for ( DiskStorage storageDiskDevice : this.vmConfig.getDiskStorageDevices() ) {
+ this.addHddMetaData( storageDiskDevice );
}
- config = "qemu-system-i386 <args> <image> -enable-kvm\nqemu-system-x86_64 <args> <image> -enable-kvm";
- displayName = file.getName().substring( 0, file.getName().indexOf( "." ) );
- setOs( "anyOs" );
- hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, file.getAbsolutePath() ) );
- makeStartSequence();
}
- // initiates the arguments map with a default working sequence that will later be used in the definition array
- public void makeStartSequence()
+ /**
+ * Adds an existing and observed storage disk device to the HDD metadata.
+ *
+ * @param storageDiskDevice existing and observed storage disk that should be added to the
+ * metadata.
+ */
+ private void addHddMetaData( DiskStorage storageDiskDevice )
{
- arguments.put( "cpu", "host" );
- arguments.put( "smp", "2" );
- arguments.put( "m", "1024" );
- arguments.put( "vga", "std" );
+ String hddChipsetModel = null;
+ DriveBusType hddChipsetBus = QemuMetaDataUtils.convertBusType( storageDiskDevice.getBusType() );
+ String hddImagePath = storageDiskDevice.getStorageSource();
+
+ this.hdds.add( new HardDisk( hddChipsetModel, hddChipsetBus, hddImagePath ) );
}
- private String configWithArgs()
+ @Override
+ public byte[] getFilteredDefinitionArray()
{
- String tempString = "";
- for ( String key : arguments.keySet() ) {
- tempString += "-" + key + " " + arguments.get( key ) + " ";
- }
- return config.replaceAll( "<args>", tempString );
+ // remove UUID in Libvirt domain XML configuration
+ this.vmConfig.removeUuid();
+
+ // removes all specified boot order entries
+ this.vmConfig.removeBootOrder();
+
+ // removes all referenced storage files of all specified CDROMs, Floppy drives and HDDs
+ this.vmConfig.removeDiskDevicesStorage();
+
+ // removes all source networks of all specified network interfaces
+ this.vmConfig.removeInterfaceDevicesSource();
+
+ // output filtered Libvirt domain XML configuration
+ String configuration = this.vmConfig.toString();
+ return configuration.getBytes( StandardCharsets.UTF_8 );
}
@Override
- public byte[] getFilteredDefinitionArray()
+ public List<DiskImage.ImageFormat> getSupportedImageFormats()
{
- return configWithArgs().getBytes( StandardCharsets.UTF_8 );
+ return QemuMetaData.SUPPORTED_IMAGE_FORMATS;
}
@Override
public void applySettingsForLocalEdit()
{
+ // NOT implemented yet
}
@Override
public boolean addHddTemplate( File diskImage, String hddMode, String redoDir )
{
- String tempS = config.replaceAll( "<image>", diskImage.getAbsolutePath() );
- config = tempS;
- hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, diskImage.getAbsolutePath() ) );
- return true;
+ return this.addHddTemplate( diskImage.getAbsolutePath(), hddMode, redoDir );
}
@Override
public boolean addHddTemplate( String diskImagePath, String hddMode, String redoDir )
{
- String tempS = config.replaceAll( "<image>", diskImagePath );
- config = tempS;
- hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, diskImagePath ) );
- return true;
+ return this.addHddTemplate( this.vmDeviceIndexHddAdd++, diskImagePath, hddMode, redoDir );
+ }
+
+ /**
+ * Adds hard disk drive (HDD) to the QEMU virtual machine configuration.
+ *
+ * @param index current index of HDD to be added to the virtual machine configuration.
+ * @param diskImagePath path to the virtual disk image for the HDD.
+ * @param hddMode operation mode of the HDD.
+ * @param redoDir directory for the redo log if an independent non-persistent
+ * <code>hddMode</code> is set.
+ * @return result state of adding the HDD.
+ */
+ public boolean addHddTemplate( int index, String diskImagePath, String hddMode, String redoDir )
+ {
+ ArrayList<DiskStorage> storageDiskDevices = this.vmConfig.getDiskStorageDevices();
+ DiskStorage storageDiskDevice = QemuMetaDataUtils.getArrayIndex( storageDiskDevices, index );
+
+ if ( storageDiskDevice == null ) {
+ // HDD does not exist, so create new storage (HDD) device
+ storageDiskDevice = this.vmConfig.addDiskStorageDevice();
+ storageDiskDevice.setReadOnly( false );
+ storageDiskDevice.setBusType( BusType.VIRTIO );
+ String targetDevName = QemuMetaDataUtils.createAlphabeticalDeviceName( "vd", index );
+ storageDiskDevice.setTargetDevice( targetDevName );
+ storageDiskDevice.setStorage( StorageType.FILE, diskImagePath );
+
+ // add new created HDD to the metadata of the QemuMetaData object, too
+ this.addHddMetaData( storageDiskDevice );
+ } else {
+ // HDD exists, so update existing storage (HDD) device
+ storageDiskDevice.setStorage( StorageType.FILE, diskImagePath );
+ }
+
+ return false;
}
@Override
public boolean addDefaultNat()
{
- return true;
+ return this.addEthernet( EtherType.NAT );
}
@Override
public void setOs( String vendorOsId )
{
- setOs( TConst.VIRT_QEMU, vendorOsId );
+ this.setOs( vendorOsId );
}
@Override
public boolean addDisplayName( String name )
{
- // TODO Auto-generated method stub
- return false;
+ this.vmConfig.setName( name );
+
+ final boolean statusName = this.vmConfig.getName().equals( name );
+
+ return statusName;
}
@Override
public boolean addRam( int mem )
{
- this.arguments.put( "m", Integer.toString( mem ) );
- return true;
+ BigInteger memory = BigInteger.valueOf( mem );
+
+ this.vmConfig.setMemory( memory );
+ this.vmConfig.setCurrentMemory( memory );
+
+ final boolean isMemorySet = this.vmConfig.getMemory().toString().equals( memory.toString() );
+ final boolean isCurrentMemorySet = this.vmConfig.getCurrentMemory().toString().equals( memory.toString() );
+
+ return isMemorySet && isCurrentMemorySet;
}
@Override
public void addFloppy( int index, String image, boolean readOnly )
{
- // TODO Auto-generated method stub
-
+ ArrayList<DiskFloppy> floppyDiskDevices = this.vmConfig.getDiskFloppyDevices();
+ DiskFloppy floppyDiskDevice = QemuMetaDataUtils.getArrayIndex( floppyDiskDevices, index );
+
+ if ( floppyDiskDevice == null ) {
+ // floppy device does not exist, so create new floppy device
+ floppyDiskDevice = this.vmConfig.addDiskFloppyDevice();
+ floppyDiskDevice.setBusType( BusType.FDC );
+ String targetDevName = QemuMetaDataUtils.createAlphabeticalDeviceName( "fd", index );
+ floppyDiskDevice.setTargetDevice( targetDevName );
+ floppyDiskDevice.setReadOnly( readOnly );
+ floppyDiskDevice.setStorage( StorageType.FILE, image );
+ } else {
+ // floppy device exists, so update existing floppy device
+ floppyDiskDevice.setReadOnly( readOnly );
+ floppyDiskDevice.setStorage( StorageType.FILE, image );
+ }
}
@Override
public boolean addCdrom( String image )
{
- // TODO Auto-generated method stub
+ return this.addCdrom( this.vmDeviceIndexCdromAdd++, image );
+ }
+
+ /**
+ * Adds CDROM drive to the QEMU virtual machine configuration.
+ *
+ * @param index current index of CDROM drive to be added to the virtual machine configuration.
+ * @param image path to a virtual image that will be inserted as CDROM into the drive.
+ * @return result state of adding the CDROM drive.
+ */
+ public boolean addCdrom( int index, String image )
+ {
+ ArrayList<DiskCdrom> cdromDiskDevices = this.vmConfig.getDiskCdromDevices();
+ DiskCdrom cdromDiskDevice = QemuMetaDataUtils.getArrayIndex( cdromDiskDevices, index );
+
+ if ( cdromDiskDevice == null ) {
+ // CDROM device does not exist, so create new CDROM device
+ cdromDiskDevice = this.vmConfig.addDiskCdromDevice();
+ cdromDiskDevice.setBusType( BusType.SATA );
+ String targetDevName = QemuMetaDataUtils.createAlphabeticalDeviceName( "sd", index );
+ cdromDiskDevice.setTargetDevice( targetDevName );
+ cdromDiskDevice.setReadOnly( true );
+
+ if ( image == null ) {
+ cdromDiskDevice.setStorage( StorageType.BLOCK, CDROM_DEFAULT_PHYSICAL_DRIVE );
+ } else {
+ cdromDiskDevice.setStorage( StorageType.FILE, image );
+ }
+ } else {
+ // CDROM device exists, so update existing CDROM device
+ cdromDiskDevice.setReadOnly( true );
+
+ if ( image == null ) {
+ cdromDiskDevice.setStorage( StorageType.BLOCK, CDROM_DEFAULT_PHYSICAL_DRIVE );
+ } else {
+ cdromDiskDevice.setStorage( StorageType.FILE, image );
+ }
+ }
+
return false;
}
@Override
public boolean addCpuCoreCount( int nrOfCores )
{
- this.arguments.put( "smp", Integer.toString( nrOfCores ) );
- return true;
+ this.vmConfig.setVCpu( nrOfCores );
+
+ boolean isVCpuSet = this.vmConfig.getVCpu() == nrOfCores;
+
+ return isVCpuSet;
}
@Override
- public void setSoundCard( VmMetaData.SoundCardType type )
+ public void setSoundCard( SoundCardType type )
{
+ QemuSoundCardMeta soundDeviceConfig = this.soundCards.get( type );
+ ArrayList<Sound> soundDevices = this.vmConfig.getSoundDevices();
+ Sound.Model soundDeviceModel = soundDeviceConfig.getModel();
+
+ if ( soundDevices.isEmpty() ) {
+ // create new sound device with 'soundDeviceModel' hardware
+ Sound soundDevice = this.vmConfig.addSoundDevice();
+ soundDevice.setModel( soundDeviceModel );
+ } else {
+ // update sound device model type of existing sound devices
+ for ( Sound soundDevice : soundDevices ) {
+ soundDevice.setModel( soundDeviceModel );
+ }
+ }
}
@Override
- public VmMetaData.SoundCardType getSoundCard()
+ public SoundCardType getSoundCard()
{
- return null;
+ ArrayList<Sound> soundDevices = this.vmConfig.getSoundDevices();
+ SoundCardType soundDeviceType = SoundCardType.DEFAULT;
+
+ if ( soundDevices.isEmpty() ) {
+ // the VM configuration does not contain a sound card device
+ soundDeviceType = SoundCardType.NONE;
+ } else {
+ // the VM configuration at least one sound card device, so return the type of the first one
+ Sound.Model soundDeviceModel = soundDevices.get( 0 ).getModel();
+ soundDeviceType = QemuMetaDataUtils.convertSoundDeviceModel( soundDeviceModel );
+ }
+
+ return soundDeviceType;
}
@Override
- public void setDDAcceleration( VmMetaData.DDAcceleration type )
+ public void setDDAcceleration( DDAcceleration type )
{
+ QemuDDAccelMeta accelerationConfig = this.ddacc.get( type );
+ ArrayList<Graphics> graphicDevices = this.vmConfig.getGraphicDevices();
+ ArrayList<Video> videoDevices = this.vmConfig.getVideoDevices();
+ final boolean accelerationEnabled = accelerationConfig.isEnabled();
+
+ boolean acceleratedGraphicsAvailable = false;
+
+ if ( graphicDevices.isEmpty() ) {
+ // add new graphics device with enabled acceleration to VM configuration
+ GraphicsSpice graphicSpiceDevice = this.vmConfig.addGraphicsSpiceDevice();
+ graphicSpiceDevice.setOpenGl( true );
+ acceleratedGraphicsAvailable = true;
+ } else {
+ // enable graphic acceleration of existing graphics devices
+ for ( Graphics graphicDevice : graphicDevices ) {
+ // set hardware acceleration for SPICE based graphics output
+ // other graphic devices do not support hardware acceleration
+ if ( graphicDevice instanceof GraphicsSpice ) {
+ GraphicsSpice.class.cast( graphicDevice ).setOpenGl( true );
+ acceleratedGraphicsAvailable = true;
+ }
+ }
+ }
+
+ // only configure hardware acceleration of video card(s) if graphics with hardware acceleration is available
+ if ( acceleratedGraphicsAvailable ) {
+ if ( videoDevices.isEmpty() ) {
+ // add new video device with enabled acceleration to VM configuration
+ Video videoDevice = this.vmConfig.addVideoDevice();
+ videoDevice.setModel( Video.Model.VIRTIO );
+ videoDevice.set2DAcceleration( true );
+ videoDevice.set3DAcceleration( true );
+ } else {
+ // enable graphic acceleration of existing graphics and video devices
+ for ( Video videoDevice : videoDevices ) {
+ // set hardware acceleration for Virtio GPUs
+ // other GPUs do not support hardware acceleration
+ if ( videoDevice.getModel() == Video.Model.VIRTIO ) {
+ videoDevice.set2DAcceleration( accelerationEnabled );
+ videoDevice.set3DAcceleration( accelerationEnabled );
+ }
+ }
+ }
+ }
}
@Override
- public VmMetaData.DDAcceleration getDDAcceleration()
+ public DDAcceleration getDDAcceleration()
{
- return null;
+ ArrayList<Graphics> graphicsDevices = this.vmConfig.getGraphicDevices();
+ ArrayList<Video> videoDevices = this.vmConfig.getVideoDevices();
+ DDAcceleration accelerationState = DDAcceleration.OFF;
+
+ boolean acceleratedGraphicsAvailable = false;
+ boolean acceleratedVideoDevAvailable = false;
+
+ // search for hardware accelerated graphics
+ for ( Graphics graphicDevice : graphicsDevices ) {
+ // only SPICE based graphic devices support hardware acceleration
+ if ( graphicDevice instanceof GraphicsSpice ) {
+ acceleratedGraphicsAvailable = true;
+ break;
+ }
+ }
+
+ // search for hardware accelerated video devices
+ for ( Video videoDevice : videoDevices ) {
+ // only Virtio based video devices support hardware acceleration
+ if ( videoDevice.getModel() == Video.Model.VIRTIO ) {
+ acceleratedVideoDevAvailable = true;
+ break;
+ }
+ }
+
+ // hardware acceleration is available if at least one accelerated graphics and video device is available
+ if ( acceleratedGraphicsAvailable && acceleratedVideoDevAvailable ) {
+ accelerationState = DDAcceleration.ON;
+ } else {
+ accelerationState = DDAcceleration.OFF;
+ }
+
+ return accelerationState;
}
@Override
- public void setHWVersion( VmMetaData.HWVersion type )
+ public void setHWVersion( HWVersion type )
{
+ // NOT supported by the QEMU hypervisor
}
@Override
- public VmMetaData.HWVersion getHWVersion()
+ public HWVersion getHWVersion()
{
+ // NOT supported by the QEMU hypervisor
return null;
}
@Override
- public void setEthernetDevType( int cardIndex, VmMetaData.EthernetDevType type )
+ public void setEthernetDevType( int cardIndex, EthernetDevType type )
{
+ QemuEthernetDevTypeMeta networkDeviceConfig = this.networkCards.get( type );
+ ArrayList<Interface> networkDevices = this.vmConfig.getInterfaceDevices();
+ Interface networkDevice = QemuMetaDataUtils.getArrayIndex( networkDevices, cardIndex );
+ Interface.Model networkDeviceModel = networkDeviceConfig.getModel();
+
+ if ( networkDevice != null ) {
+ networkDevice.setModel( networkDeviceModel );
+ }
}
@Override
- public VmMetaData.EthernetDevType getEthernetDevType( int cardIndex )
+ public EthernetDevType getEthernetDevType( int cardIndex )
{
- return null;
+ ArrayList<Interface> networkDevices = this.vmConfig.getInterfaceDevices();
+ Interface networkDevice = QemuMetaDataUtils.getArrayIndex( networkDevices, cardIndex );
+ EthernetDevType networkDeviceType = EthernetDevType.NONE;
+
+ if ( networkDevice == null ) {
+ // network interface device is not present
+ networkDeviceType = EthernetDevType.NONE;
+ } else {
+ // get model of existing network interface device
+ Interface.Model networkDeviceModel = networkDevice.getModel();
+ networkDeviceType = QemuMetaDataUtils.convertNetworkDeviceModel( networkDeviceModel );
+ }
+
+ return networkDeviceType;
}
@Override
- public byte[] getDefinitionArray()
+ public void setMaxUsbSpeed( UsbSpeed speed )
{
- return configWithArgs().getBytes( StandardCharsets.UTF_8 );
+ QemuUsbSpeedMeta usbControllerConfig = this.usbSpeeds.get( speed );
+ ArrayList<ControllerUsb> usbControllerDevices = this.vmConfig.getUsbControllerDevices();
+ ControllerUsb.Model usbControllerModel = usbControllerConfig.getModel();
+
+ if ( usbControllerDevices.isEmpty() ) {
+ // add new USB controller with specified speed 'usbControllerModel'
+ ControllerUsb usbControllerDevice = this.vmConfig.addControllerUsbDevice();
+ usbControllerDevice.setModel( usbControllerModel );
+ } else {
+ // update model of all USB controller devices to support the maximum speed
+ for ( ControllerUsb usbControllerDevice : usbControllerDevices ) {
+ usbControllerDevice.setModel( usbControllerModel );
+ }
+ }
}
@Override
- public boolean addEthernet( VmMetaData.EtherType type )
+ public UsbSpeed getMaxUsbSpeed()
{
- return false;
+ ArrayList<ControllerUsb> usbControllerDevices = this.vmConfig.getUsbControllerDevices();
+ UsbSpeed maxUsbSpeed = VmMetaData.UsbSpeed.NONE;
+ int maxUsbSpeedNumeric = 0;
+
+ for ( ControllerUsb usbControllerDevice : usbControllerDevices ) {
+ ControllerUsb.Model usbControllerModel = usbControllerDevice.getModel();
+
+ for ( Entry<UsbSpeed, QemuUsbSpeedMeta> usbSpeedEntry : this.usbSpeeds.entrySet() ) {
+ QemuUsbSpeedMeta usbSpeed = usbSpeedEntry.getValue();
+ if ( usbSpeed.getSpeed() > maxUsbSpeedNumeric && usbSpeed.getModel() == usbControllerModel ) {
+ maxUsbSpeed = usbSpeedEntry.getKey();
+ maxUsbSpeedNumeric = usbSpeed.getSpeed();
+ }
+ }
+ }
+
+ return maxUsbSpeed;
}
@Override
- public Virtualizer getVirtualizer()
+ public byte[] getDefinitionArray()
{
- return virtualizer;
+ String configuration = this.vmConfig.toString();
+
+ if ( configuration == null ) {
+ return null;
+ } else {
+ // append newline at the end of the XML content to match the structure of an original Libvirt XML file
+ configuration += System.lineSeparator();
+ return configuration.getBytes( StandardCharsets.UTF_8 );
+ }
}
@Override
- public boolean tweakForNonPersistent()
+ public boolean addEthernet( EtherType type )
+ {
+ return this.addEthernet( this.vmDeviceIndexEthernetAdd++, type );
+ }
+
+ /**
+ * Adds an ethernet card to the QEMU virtual machine configuration.
+ *
+ * @param index current index of the ethernet card to be added to the virtual machine
+ * configuration.
+ * @param type card model of the ethernet card.
+ * @return result state of adding the ethernet card.
+ */
+ public boolean addEthernet( int index, EtherType type )
{
+ QemuEthernetDevTypeMeta defaultNetworkDeviceConfig = this.networkCards.get( EthernetDevType.AUTO );
+ ArrayList<Interface> interfaceDevices = this.vmConfig.getInterfaceDevices();
+ Interface interfaceDevice = QemuMetaDataUtils.getArrayIndex( interfaceDevices, index );
+
+ final Interface.Model defaultNetworkDeviceModel = defaultNetworkDeviceConfig.getModel();
+
+ if ( interfaceDevice == null ) {
+ // network interface device does not exist, so create new network interface device
+ switch ( type ) {
+ case BRIDGED:
+ // add network bridge interface device
+ interfaceDevice = this.vmConfig.addInterfaceBridgeDevice();
+ interfaceDevice.setModel( defaultNetworkDeviceModel );
+ interfaceDevice.setSource( QemuMetaData.NETWORK_DEFAULT_BRIDGE );
+ break;
+ case HOST_ONLY:
+ // add network interface device with link to the isolated host network
+ interfaceDevice = this.vmConfig.addInterfaceNetworkDevice();
+ interfaceDevice.setModel( defaultNetworkDeviceModel );
+ interfaceDevice.setSource( QemuMetaData.NETWORK_DEFAULT_HOST_ONLY );
+ break;
+ case NAT:
+ // add network interface device with link to the NAT network
+ interfaceDevice = this.vmConfig.addInterfaceNetworkDevice();
+ interfaceDevice.setModel( defaultNetworkDeviceModel );
+ interfaceDevice.setSource( QemuMetaData.NETWORK_DEFAULT_NAT );
+ break;
+ }
+ } else {
+ // network interface device exists, so update existing network interface device
+ switch ( type ) {
+ case BRIDGED:
+ interfaceDevice.setType( Interface.Type.BRIDGE );
+ interfaceDevice.setSource( QemuMetaData.NETWORK_DEFAULT_BRIDGE );
+ break;
+ case HOST_ONLY:
+ interfaceDevice.setType( Interface.Type.NETWORK );
+ interfaceDevice.setSource( QemuMetaData.NETWORK_DEFAULT_HOST_ONLY );
+ break;
+ case NAT:
+ interfaceDevice.setType( Interface.Type.NETWORK );
+ interfaceDevice.setSource( QemuMetaData.NETWORK_DEFAULT_NAT );
+ break;
+ }
+ }
+
return false;
}
@Override
- public void registerVirtualHW()
+ public Virtualizer getVirtualizer()
{
+ return QemuMetaData.VIRTUALIZER;
}
@Override
- public void setMaxUsbSpeed( VmMetaData.UsbSpeed speed )
+ public boolean tweakForNonPersistent()
{
- // TODO: Actual speed setting?
- if ( speed == null || speed == VmMetaData.UsbSpeed.NONE ) {
- arguments.remove( "usb" );
- } else {
- arguments.put( "usb", "" );
- }
+ // NOT implemented yet
+ return false;
}
@Override
- public VmMetaData.UsbSpeed getMaxUsbSpeed()
+ public void registerVirtualHW()
{
- if ( arguments.containsKey( "usb" ) )
- return VmMetaData.UsbSpeed.USB2_0; // TODO
- return VmMetaData.UsbSpeed.NONE;
+ // @formatter:off
+ soundCards.put( VmMetaData.SoundCardType.NONE, new QemuSoundCardMeta( null ) );
+ soundCards.put( VmMetaData.SoundCardType.DEFAULT, new QemuSoundCardMeta( Sound.Model.ICH9 ) );
+ soundCards.put( VmMetaData.SoundCardType.SOUND_BLASTER, new QemuSoundCardMeta( Sound.Model.SB16 ) );
+ soundCards.put( VmMetaData.SoundCardType.ES, new QemuSoundCardMeta( Sound.Model.ES1370 ) );
+ soundCards.put( VmMetaData.SoundCardType.AC, new QemuSoundCardMeta( Sound.Model.AC97 ) );
+ soundCards.put( VmMetaData.SoundCardType.HD_AUDIO, new QemuSoundCardMeta( Sound.Model.ICH9 ) );
+
+ ddacc.put( VmMetaData.DDAcceleration.OFF, new QemuDDAccelMeta( false ) );
+ ddacc.put( VmMetaData.DDAcceleration.ON, new QemuDDAccelMeta( true ) );
+
+ hwversion.put( VmMetaData.HWVersion.DEFAULT, new QemuHWVersionMeta( 0 ) );
+
+ networkCards.put( VmMetaData.EthernetDevType.NONE, new QemuEthernetDevTypeMeta( null ) );
+ networkCards.put( VmMetaData.EthernetDevType.AUTO, new QemuEthernetDevTypeMeta( Interface.Model.VIRTIO_NET_PCI ) );
+ networkCards.put( VmMetaData.EthernetDevType.PCNETPCI2, new QemuEthernetDevTypeMeta( Interface.Model.PCNET ) );
+ networkCards.put( VmMetaData.EthernetDevType.E1000, new QemuEthernetDevTypeMeta( Interface.Model.E1000 ) );
+ networkCards.put( VmMetaData.EthernetDevType.E1000E, new QemuEthernetDevTypeMeta( Interface.Model.E1000E ) );
+ networkCards.put( VmMetaData.EthernetDevType.VMXNET3, new QemuEthernetDevTypeMeta( Interface.Model.VMXNET3 ) );
+ networkCards.put( VmMetaData.EthernetDevType.PARAVIRT, new QemuEthernetDevTypeMeta( Interface.Model.VIRTIO_NET_PCI ) );
+
+ usbSpeeds.put( VmMetaData.UsbSpeed.NONE, new QemuUsbSpeedMeta( 0, ControllerUsb.Model.NONE ) );
+ usbSpeeds.put( VmMetaData.UsbSpeed.USB1_1, new QemuUsbSpeedMeta( 1, ControllerUsb.Model.ICH9_UHCI1 ) );
+ usbSpeeds.put( VmMetaData.UsbSpeed.USB2_0, new QemuUsbSpeedMeta( 2, ControllerUsb.Model.ICH9_EHCI1 ) );
+ usbSpeeds.put( VmMetaData.UsbSpeed.USB3_0, new QemuUsbSpeedMeta( 3, ControllerUsb.Model.QEMU_XHCI ) );
+ // @formatter:on
}
-
}
diff --git a/src/main/java/org/openslx/util/vm/QemuMetaDataUtils.java b/src/main/java/org/openslx/util/vm/QemuMetaDataUtils.java
new file mode 100644
index 0000000..42c3fb6
--- /dev/null
+++ b/src/main/java/org/openslx/util/vm/QemuMetaDataUtils.java
@@ -0,0 +1,188 @@
+package org.openslx.util.vm;
+
+import java.util.ArrayList;
+
+import org.openslx.libvirt.domain.device.Disk;
+import org.openslx.libvirt.domain.device.Interface;
+import org.openslx.libvirt.domain.device.Disk.BusType;
+import org.openslx.libvirt.domain.device.Sound;
+import org.openslx.util.vm.VmMetaData.DriveBusType;
+import org.openslx.util.vm.VmMetaData.EthernetDevType;
+import org.openslx.util.vm.VmMetaData.SoundCardType;
+
+/**
+ * Collection of utils to convert data types from bwLehrpool to Libvirt and vice versa.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class QemuMetaDataUtils
+{
+ /**
+ * Converts a Libvirt disk device bus type to a VM metadata driver bus type.
+ *
+ * @param busType Libvirt disk device bus type.
+ * @return VM metadata bus type of the disk drive.
+ */
+ public static DriveBusType convertBusType( Disk.BusType busType )
+ {
+ DriveBusType type = null;
+
+ switch ( busType ) {
+ case IDE:
+ type = DriveBusType.IDE;
+ break;
+ case SATA:
+ type = DriveBusType.SATA;
+ break;
+ case SCSI:
+ type = DriveBusType.SCSI;
+ break;
+ default:
+ type = null;
+ break;
+ }
+
+ return type;
+ }
+
+ /**
+ * Converts a VM metadata driver bus type to a Libvirt disk device bus type.
+ *
+ * @param busType VM metadata bus type of the disk drive.
+ * @return Libvirt disk device bus type.
+ */
+ public static Disk.BusType convertBusType( DriveBusType busType )
+ {
+ Disk.BusType type = null;
+
+ switch ( busType ) {
+ case IDE:
+ type = BusType.IDE;
+ break;
+ case SATA:
+ type = BusType.SATA;
+ break;
+ case SCSI:
+ type = BusType.SCSI;
+ break;
+ }
+
+ return type;
+ }
+
+ /**
+ * Converts a Libvirt sound device model to a VM metadata sound card type.
+ *
+ * @param soundDeviceModel Libvirt sound device model.
+ * @return VM metadata sound card type.
+ */
+ public static SoundCardType convertSoundDeviceModel( Sound.Model soundDeviceModel )
+ {
+ SoundCardType type = SoundCardType.NONE;
+
+ switch ( soundDeviceModel ) {
+ case AC97:
+ type = SoundCardType.AC;
+ break;
+ case ES1370:
+ type = SoundCardType.ES;
+ break;
+ case ICH6:
+ type = SoundCardType.HD_AUDIO;
+ break;
+ case ICH9:
+ type = SoundCardType.HD_AUDIO;
+ break;
+ case SB16:
+ type = SoundCardType.SOUND_BLASTER;
+ break;
+ }
+
+ return type;
+ }
+
+ /**
+ * Converts a Libvirt network device model to a VM metadata ethernet device type.
+ *
+ * @param soundDeviceModel Libvirt network device model.
+ * @return VM metadata ethernet device type.
+ */
+ public static EthernetDevType convertNetworkDeviceModel( Interface.Model networkDeviceModel )
+ {
+ EthernetDevType type = EthernetDevType.NONE;
+
+ switch ( networkDeviceModel ) {
+ case E1000:
+ type = EthernetDevType.E1000;
+ break;
+ case E1000E:
+ type = EthernetDevType.E1000E;
+ break;
+ case PCNET:
+ type = EthernetDevType.PCNETPCI2;
+ break;
+ case VIRTIO:
+ type = EthernetDevType.PARAVIRT;
+ break;
+ case VIRTIO_NET_PCI:
+ type = EthernetDevType.PARAVIRT;
+ break;
+ case VIRTIO_NET_PCI_NON_TRANSITIONAL:
+ type = EthernetDevType.PARAVIRT;
+ break;
+ case VIRTIO_NET_PCI_TRANSITIONAL:
+ type = EthernetDevType.PARAVIRT;
+ break;
+ case VMXNET3:
+ type = EthernetDevType.VMXNET3;
+ break;
+ default:
+ type = EthernetDevType.AUTO;
+ break;
+ }
+
+ return type;
+ }
+
+ /**
+ * Returns an item from a given {@link ArrayList}.
+ *
+ * The item is selected by a given index. If the item is not available within the
+ * {@link ArrayList}, <code>null</code> is returned.
+ *
+ * @param <T> type of the {@link ArrayList}.
+ * @param array {@link ArrayList} of type <code>T</code>.
+ * @param index selects the item from the {@link ArrayList}.
+ * @return selected item of the {@link ArrayList}.
+ */
+ public static <T> T getArrayIndex( ArrayList<T> array, int index )
+ {
+ T ret;
+
+ try {
+ ret = array.get( index );
+ } catch ( IndexOutOfBoundsException e ) {
+ ret = null;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Creates an alphabetical device name constructed from a device prefix and a device number.
+ *
+ * @param devicePrefix prefix of the constructed device name.
+ * @param deviceNumber number of the device.
+ * @return alphabetical device name.
+ */
+ public static String createAlphabeticalDeviceName( String devicePrefix, int deviceNumber )
+ {
+ if ( deviceNumber < 0 || deviceNumber >= ( 'z' - 'a' ) ) {
+ String errorMsg = new String( "Device number is out of range to be able to create a valid device name." );
+ throw new IllegalArgumentException( errorMsg );
+ }
+
+ return devicePrefix + ( 'a' + deviceNumber );
+ }
+}
diff --git a/src/main/java/org/openslx/util/vm/VboxMetaData.java b/src/main/java/org/openslx/util/vm/VboxMetaData.java
index da5189e..82936a7 100644
--- a/src/main/java/org/openslx/util/vm/VboxMetaData.java
+++ b/src/main/java/org/openslx/util/vm/VboxMetaData.java
@@ -6,6 +6,7 @@ import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.UUID;
@@ -14,6 +15,7 @@ import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.OperatingSystem;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.thrifthelper.TConst;
+import org.openslx.util.vm.DiskImage.ImageFormat;
import org.openslx.util.vm.VboxConfig.PlaceHolder;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -77,6 +79,12 @@ class VBoxUsbSpeedMeta
public class VboxMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta, VBoxHWVersionMeta, VBoxEthernetDevTypeMeta, VBoxUsbSpeedMeta>
{
+ /**
+ * List of supported image formats by the VirtualBox hypervisor.
+ */
+ private static final List<DiskImage.ImageFormat> SUPPORTED_IMAGE_FORMATS = Collections.unmodifiableList(
+ Arrays.asList( ImageFormat.VDI ) );
+
private static final Logger LOGGER = Logger.getLogger( VboxMetaData.class );
private static final Virtualizer virtualizer = new Virtualizer( TConst.VIRT_VIRTUALBOX, "VirtualBox" );
@@ -125,6 +133,12 @@ public class VboxMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta,
{
return virtualizer;
}
+
+ @Override
+ public List<DiskImage.ImageFormat> getSupportedImageFormats()
+ {
+ return VboxMetaData.SUPPORTED_IMAGE_FORMATS;
+ }
@Override
public void applySettingsForLocalEdit()
diff --git a/src/main/java/org/openslx/util/vm/VmMetaData.java b/src/main/java/org/openslx/util/vm/VmMetaData.java
index dca9e27..616d971 100644
--- a/src/main/java/org/openslx/util/vm/VmMetaData.java
+++ b/src/main/java/org/openslx/util/vm/VmMetaData.java
@@ -282,6 +282,13 @@ public abstract class VmMetaData<T, U, V, W, X>
}
/**
+ * Returns list of image formats supported by the VM's hypervisor.
+ *
+ * @return list of image formats.
+ */
+ public abstract List<DiskImage.ImageFormat> getSupportedImageFormats();
+
+ /**
* Apply config options that are desired when locally editing a VM. for vmware,
* this disables automatic DPI scaling of the guest.
*/
@@ -310,7 +317,7 @@ public abstract class VmMetaData<T, U, V, W, X>
try {
return new QemuMetaData( osList, file );
} catch ( Exception e ) {
- LOGGER.info( "Not a QEmu file", e );
+ LOGGER.info( "Not a Qemu file", e );
}
try {
return new DockerMetaDataDummy(osList, file);
diff --git a/src/main/java/org/openslx/util/vm/VmwareMetaData.java b/src/main/java/org/openslx/util/vm/VmwareMetaData.java
index 2835e22..1793655 100644
--- a/src/main/java/org/openslx/util/vm/VmwareMetaData.java
+++ b/src/main/java/org/openslx/util/vm/VmwareMetaData.java
@@ -3,6 +3,8 @@ package org.openslx.util.vm;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -15,6 +17,7 @@ import org.openslx.bwlp.thrift.iface.OperatingSystem;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.thrifthelper.TConst;
import org.openslx.util.Util;
+import org.openslx.util.vm.DiskImage.ImageFormat;
import org.openslx.util.vm.VmwareConfig.ConfigEntry;
class VmWareSoundCardMeta
@@ -73,7 +76,12 @@ class VmwareUsbSpeed
public class VmwareMetaData extends VmMetaData<VmWareSoundCardMeta, VmWareDDAccelMeta, VmWareHWVersionMeta, VmWareEthernetDevTypeMeta, VmwareUsbSpeed>
{
-
+ /**
+ * List of supported image formats by the VMware hypervisor.
+ */
+ private static final List<DiskImage.ImageFormat> SUPPORTED_IMAGE_FORMATS = Collections.unmodifiableList(
+ Arrays.asList( ImageFormat.VMDK ) );
+
private static final Logger LOGGER = Logger.getLogger( VmwareMetaData.class );
private static final Virtualizer virtualizer = new Virtualizer( TConst.VIRT_VMWARE, "VMware" );
@@ -246,6 +254,12 @@ public class VmwareMetaData extends VmMetaData<VmWareSoundCardMeta, VmWareDDAcce
}
@Override
+ public List<DiskImage.ImageFormat> getSupportedImageFormats()
+ {
+ return VmwareMetaData.SUPPORTED_IMAGE_FORMATS;
+ }
+
+ @Override
public boolean addHddTemplate( File diskImage, String hddMode, String redoDir )
{
return addHddTemplate( diskImage.getName(), hddMode, redoDir );
diff --git a/src/main/resources/libvirt/rng/basictypes.rng b/src/main/resources/libvirt/rng/basictypes.rng
new file mode 100644
index 0000000..a221ff6
--- /dev/null
+++ b/src/main/resources/libvirt/rng/basictypes.rng
@@ -0,0 +1,637 @@
+<?xml version="1.0"?>
+<!-- network-related definitions used in multiple grammars -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <!-- Our unsignedInt doesn't allow a leading "+" in its lexical form -->
+ <define name="unsignedInt">
+ <data type="unsignedInt">
+ <param name="pattern">[0-9]+</param>
+ </data>
+ </define>
+ <define name="unsignedLong">
+ <data type="unsignedLong">
+ <param name="pattern">[0-9]+</param>
+ </data>
+ </define>
+
+ <define name="hexuint">
+ <data type="string">
+ <param name="pattern">(0x)?[0-9a-f]+</param>
+ </data>
+ </define>
+
+ <define name="positiveInteger">
+ <data type="positiveInteger">
+ <param name="pattern">[0-9]+</param>
+ </data>
+ </define>
+
+ <define name="octalMode">
+ <data type="unsignedInt">
+ <param name="pattern">[0-7]+</param>
+ </data>
+ </define>
+
+ <define name="uint8">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,2}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">255</param>
+ </data>
+ </choice>
+ </define>
+ <define name="uint16">
+ <choice>
+ <data type="string">
+ <param name="pattern">(0x)?[0-9a-fA-F]{1,4}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+ </choice>
+ </define>
+ <define name="uint24">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,6}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">16777215</param>
+ </data>
+ </choice>
+ </define>
+ <define name="uint32">
+ <choice>
+ <data type="string">
+ <param name="pattern">(0x)?[0-9a-fA-F]{1,8}</param>
+ </data>
+ <data type="unsignedInt">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">4294967295</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="UUID">
+ <choice>
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9]{32}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9]{8}\-([a-fA-F0-9]{4}\-){3}[a-fA-F0-9]{12}</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="numaDistanceValue">
+ <data type="unsignedInt">
+ <param name="minInclusive">10</param>
+ <param name="maxInclusive">255</param>
+ </data>
+ </define>
+
+ <define name="pciaddress">
+ <optional>
+ <attribute name="domain">
+ <ref name="pciDomain"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="bus">
+ <ref name="pciBus"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="slot">
+ <ref name="pciSlot"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="function">
+ <ref name="pciFunc"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="multifunction">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </define>
+ <define name="zpciaddress">
+ <optional>
+ <element name="zpci">
+ <optional>
+ <attribute name="uid">
+ <ref name="uint16"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="fid">
+ <ref name="uint32"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </define>
+
+ <!-- a 6 byte MAC address in ASCII-hex format, eg "12:34:56:78:9A:BC" -->
+ <!-- The lowest bit of the 1st byte is the "multicast" bit. a -->
+ <!-- uniMacAddr requires that bit to be 0, and a multiMacAddr -->
+ <!-- requires it to be 1. Plain macAddr will accept either. -->
+ <!-- Currently there is no use of multiMacAddr in libvirt, it -->
+ <!-- is included here for documentation/comparison purposes. -->
+ <define name="uniMacAddr">
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9][02468aAcCeE](:[a-fA-F0-9]{2}){5}</param>
+ </data>
+ </define>
+ <define name="multiMacAddr">
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9][13579bBdDfF](:[a-fA-F0-9]{2}){5}</param>
+ </data>
+ </define>
+ <define name="macAddr">
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}</param>
+ </data>
+ </define>
+
+ <!--====================================================================-->
+ <!--The duid is a unique identifier used in DHCPv6 to identity an -->
+ <!--interface on a device (system). The duid is often used by servers -->
+ <!--such as dnsmasq to assign a specific IP address (and optionally a -->
+ <!--name to an interface. The applicable standards are RFC3315 and -->
+ <!--RFC6355. These standards actually require the duid to be fixed for -->
+ <!--the hardware device and applicable to all network interfaces on -->
+ <!--that device. It is not clear that any software currently enforces -->
+ <!--this requirement although it could be implemented manually. -->
+ <!--====================================================================-->
+ <!--There are currently four types of duids defined: -->
+ <!-- type 1, duid-LLT, link-layer (MAC) plus 32 bit time when the -->
+ <!-- duid-LLT was created in seconds from January 1, 2000 -->
+ <!-- type 2, duid-EN, 32 bit "enterprise number" followed by a -->
+ <!-- variable length unique identifier. -->
+ <!-- type 3, duid-LL, link-layer (MAC) -->
+ <!-- type 4, duid-UUID, a 128 bit UUID (16 bytes) -->
+ <!--RFC3315 states that the maximum length of a duid is 128 bytes plus -->
+ <!--the 16 bit type field. Often, the machine type is "1" which is the -->
+ <!--number assigned to ethernet. -->
+
+ <define name="duidLLT">
+ <data type="string">
+ <!-- 0======| type======| 0======| machine type======| time================| link-layer============| -->
+ <param name="pattern">[0]{1,2}:[0]{0,1}[1]:[0]{1,2}:[0]{0,1}[a-fA-F1-9](:[a-fA-F0-9]{1,2}){4}(:[a-fA-F0-9]{1,2}){6,8}</param>
+ </data>
+ </define>
+
+ <define name="duidEN">
+ <data type="string">
+ <!-- 0======| type======| Enterprise number===| unique id ==============| -->
+ <param name="pattern">[0]{1,2}:[0]{0,1}[2](:[a-fA-F0-9]{1,2}){4}(:[a-fA-F0-9]{1,2}){1,124}</param>
+ </data>
+ </define>
+
+ <define name="duidLL">
+ <data type="string">
+ <!-- 0======| type======| 0======| machine type======| link-layer============| -->
+ <param name="pattern">[0]{1,2}:[0]{0,1}[3]:[0]{1,2}:[0]{0,1}[a-fA-F1-9](:[a-fA-F0-9]{1,2}){6,8}</param>
+ </data>
+ </define>
+
+ <define name="duidUUID">
+ <data type="string">
+ <!-- 0======| type======| UUID=================| -->
+ <param name="pattern">[0]{1,2}:[0]{0,1}[4](:[a-fA-F0-9]{1,2}){16}</param>
+ </data>
+ </define>
+
+ <define name="DUID">
+ <choice>
+ <ref name="duidLLT"/>
+ <ref name="duidEN"/>
+ <ref name="duidLL"/>
+ <ref name="duidUUID"/>
+ </choice>
+ </define>
+ <!--======================================================================-->
+
+ <!-- An ipv4 "dotted quad" address -->
+ <define name="ipv4Addr">
+ <data type="string">
+ <param name="pattern">(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9]))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9]))</param>
+ </data>
+ </define>
+
+ <!-- Based on http://blog.mes-stats.fr/2008/10/09/regex-ipv4-et-ipv6 -->
+ <define name="ipv6Addr">
+ <data type="string">
+ <!-- To understand this better, take apart the toplevel "|"s -->
+<param name="pattern">(([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9]))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9])))|(([0-9A-Fa-f]{1,4}:){0,5}:(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9]))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9])))|(::([0-9A-Fa-f]{1,4}:){0,5}(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9]))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9])))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:)|(::)</param>
+ </data>
+ </define>
+
+ <define name="ipAddr">
+ <choice>
+ <ref name="ipv4Addr"/>
+ <ref name="ipv6Addr"/>
+ </choice>
+ </define>
+
+ <define name="ipv4Prefix">
+ <data type="unsignedInt">
+ <param name="maxInclusive">32</param>
+ </data>
+ </define>
+
+ <define name="ipv6Prefix">
+ <data type="unsignedInt">
+ <param name="maxInclusive">128</param>
+ </data>
+ </define>
+
+ <define name="ipPrefix">
+ <choice>
+ <ref name="ipv4Prefix"/>
+ <ref name="ipv6Prefix"/>
+ </choice>
+ </define>
+
+ <!-- objectName represents any generic string for naming objects like domain -->
+ <define name="objectNameWithSlash">
+ <data type="string">
+ <param name="pattern">[^\n]+</param>
+ </data>
+ </define>
+
+ <define name="objectName">
+ <data type="string">
+ <param name="pattern">[^/\n]+</param>
+ </data>
+ </define>
+
+ <define name="genericName">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\+\-]+</param>
+ </data>
+ </define>
+
+ <define name="dnsName">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9\.\-]+</param>
+ </data>
+ </define>
+
+ <define name="deviceName">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\.\-\\:/]+</param>
+ </data>
+ </define>
+
+ <define name="zoneName">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\-]+</param>
+ </data>
+ </define>
+
+ <define name="filePath">
+ <data type="string">
+ <param name="pattern">.+</param>
+ </data>
+ </define>
+
+ <define name="dirPath">
+ <data type="string">
+ <param name="pattern">.+</param>
+ </data>
+ </define>
+
+ <define name="absFilePath">
+ <data type="string">
+ <param name="pattern">(/|[a-zA-Z]:\\).+</param>
+ </data>
+ </define>
+
+ <define name="vmwarePath">
+ <data type="string">
+ <param name="pattern">\[[^\]]+\] .+</param>
+ </data>
+ </define>
+
+ <define name="absDirPath">
+ <data type="string">
+ <param name="pattern">/.*</param>
+ </data>
+ </define>
+
+ <define name="unit">
+ <data type="string">
+ <param name="pattern">([bB]([yY][tT][eE][sS]?)?)|([kKmMgGtTpPeE]([iI]?[bB])?)</param>
+ </data>
+ </define>
+ <define name="scaledInteger">
+ <optional>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </optional>
+ <ref name="unsignedLong"/>
+ </define>
+
+ <define name="pciDomain">
+ <ref name="uint32"/>
+ </define>
+ <define name="pciBus">
+ <ref name="uint8"/>
+ </define>
+ <define name="pciSlot">
+ <choice>
+ <data type="string">
+ <param name="pattern">(0x)?[0-1]?[0-9a-fA-F]</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">31</param>
+ </data>
+ </choice>
+ </define>
+ <define name="pciFunc">
+ <choice>
+ <data type="string">
+ <param name="pattern">(0x)?[0-7]</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">7</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="wwn">
+ <data type="string">
+ <param name="pattern">(0x)?[0-9a-fA-F]{16}</param>
+ </data>
+ </define>
+
+ <define name="ccwCssidRange">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-eA-E][0-9a-fA-F]?</param>
+ </data>
+ <data type="string">
+ <param name="pattern">0x[fF][0-9a-eA-E]?</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">254</param>
+ </data>
+ </choice>
+ </define>
+ <define name="ccwSsidRange">
+ <data type="string">
+ <param name="pattern">(0x)?[0-3]</param>
+ </data>
+ </define>
+ <define name="ccwDevnoRange">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,4}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="cpuset">
+ <data type="string">
+ <param name="pattern">([0-9]+(-[0-9]+)?|\^[0-9]+)(,([0-9]+(-[0-9]+)?|\^[0-9]+))*</param>
+ </data>
+ </define>
+
+ <define name="volName">
+ <!-- directory pools allow almost any file name as a volume name -->
+ <data type="string">
+ <param name="pattern">[^/]+</param>
+ <except>
+ <choice>
+ <value>.</value>
+ <value>..</value>
+ </choice>
+ </except>
+ </data>
+ </define>
+
+ <define name="archnames">
+ <choice>
+ <value>aarch64</value>
+ <value>alpha</value>
+ <value>armv6l</value>
+ <value>armv7l</value>
+ <value>cris</value>
+ <value>i686</value>
+ <value>ia64</value>
+ <value>lm32</value>
+ <value>m68k</value>
+ <value>microblaze</value>
+ <value>microblazeel</value>
+ <value>mips</value>
+ <value>mipsel</value>
+ <value>mips64</value>
+ <value>mips64el</value>
+ <value>openrisc</value>
+ <value>parisc</value>
+ <value>parisc64</value>
+ <value>ppc</value>
+ <value>ppc64</value>
+ <value>ppc64le</value>
+ <value>ppcemb</value>
+ <value>riscv32</value>
+ <value>riscv64</value>
+ <value>s390</value>
+ <value>s390x</value>
+ <value>sh4</value>
+ <value>sh4eb</value>
+ <value>sparc</value>
+ <value>sparc64</value>
+ <value>unicore32</value>
+ <value>x86_64</value>
+ <value>xtensa</value>
+ <value>xtensaeb</value>
+ </choice>
+ </define>
+
+ <define name="PortNumber">
+ <data type="int">
+ <param name="minInclusive">-1</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+ </define>
+
+ <define name="sourceinfoadapter">
+ <element name="adapter">
+ <choice>
+ <group>
+ <!-- To keep back-compat, "type" is not mandatory for
+ scsi_host adapter -->
+ <optional>
+ <attribute name="type">
+ <value>scsi_host</value>
+ </attribute>
+ </optional>
+ <choice>
+ <group>
+ <attribute name="name">
+ <text/>
+ </attribute>
+ </group>
+ <group>
+ <interleave>
+ <element name="parentaddr">
+ <optional>
+ <attribute name="unique_id">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <element name="address">
+ <ref name="pciaddress"/>
+ </element>
+ </element>
+ </interleave>
+ </group>
+ </choice>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>fc_host</value>
+ </attribute>
+ <optional>
+ <attribute name="parent">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="managed">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="parent_wwnn">
+ <ref name="wwn"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="parent_wwpn">
+ <ref name="wwn"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="parent_fabric_wwn">
+ <ref name="wwn"/>
+ </attribute>
+ </optional>
+ <attribute name="wwnn">
+ <ref name="wwn"/>
+ </attribute>
+ <attribute name="wwpn">
+ <ref name="wwn"/>
+ </attribute>
+ </group>
+ </choice>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="isaaddress">
+ <optional>
+ <attribute name="iobase">
+ <data type="string">
+ <param name="pattern">0x[a-fA-F0-9]{1,4}</param>
+ </data>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="irq">
+ <data type="string">
+ <param name="pattern">0x[a-fA-F0-9]</param>
+ </data>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="link-speed-state">
+ <optional>
+ <element name="link">
+ <optional>
+ <attribute name="speed">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="state">
+ <choice>
+ <value>unknown</value>
+ <value>notpresent</value>
+ <value>down</value>
+ <value>lowerlayerdown</value>
+ <value>testing</value>
+ <value>dormant</value>
+ <value>up</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </define>
+
+ <define name="virYesNo">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </define>
+
+ <define name="virOnOff">
+ <choice>
+ <value>on</value>
+ <value>off</value>
+ </choice>
+ </define>
+
+ <define name="metadata">
+ <element name="metadata">
+ <zeroOrMore>
+ <ref name="customElement"/>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="customElement">
+ <element>
+ <anyName/>
+ <zeroOrMore>
+ <choice>
+ <attribute>
+ <anyName/>
+ </attribute>
+ <text/>
+ <ref name="customElement"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="leaseUnit">
+ <choice>
+ <value>seconds</value>
+ <value>minutes</value>
+ <value>hours</value>
+ </choice>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/capability.rng b/src/main/resources/libvirt/rng/capability.rng
new file mode 100644
index 0000000..91a046e
--- /dev/null
+++ b/src/main/resources/libvirt/rng/capability.rng
@@ -0,0 +1,519 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt capabilities XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <include href="cputypes.rng"/>
+ <start>
+ <ref name="capabilities"/>
+ </start>
+
+
+ <define name="capabilities">
+ <element name="capabilities">
+ <ref name="hostcaps"/>
+
+ <zeroOrMore>
+ <ref name="guestcaps"/>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="hostcaps">
+ <element name="host">
+ <optional>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </optional>
+ <ref name="hostcpu"/>
+ <optional>
+ <ref name="power_management"/>
+ </optional>
+ <optional>
+ <ref name="iommu_support"/>
+ </optional>
+ <optional>
+ <ref name="migration"/>
+ </optional>
+ <optional>
+ <ref name="topology"/>
+ </optional>
+ <optional>
+ <ref name="cache"/>
+ </optional>
+ <optional>
+ <ref name="memory_bandwidth"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="secmodel"/>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="secmodel">
+ <element name="secmodel">
+ <interleave>
+ <element name="model">
+ <text/>
+ </element>
+ <element name="doi">
+ <text/>
+ </element>
+ <zeroOrMore>
+ <element name="baselabel">
+ <attribute name="type">
+ <text/>
+ </attribute>
+ <text/>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="power_management">
+ <element name="power_management">
+ <interleave>
+ <optional>
+ <element name="suspend_mem">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="suspend_disk">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="suspend_hybrid">
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="iommu_support">
+ <element name="iommu">
+ <optional>
+ <attribute name="support">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="migration">
+ <element name="migration_features">
+ <optional>
+ <element name="live">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="uri_transports">
+ <oneOrMore>
+ <element name="uri_transport">
+ <choice>
+ <value>vpxmigr</value>
+ <value>tcp</value>
+ <value>rdma</value>
+ <value>vzmigr</value>
+ </choice>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="topology">
+ <element name="topology">
+ <element name="cells">
+ <attribute name="num">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <oneOrMore>
+ <ref name="cell"/>
+ </oneOrMore>
+ </element>
+ </element>
+ </define>
+
+ <define name="cell">
+ <element name="cell">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+
+ <optional>
+ <ref name="memory"/>
+ </optional>
+
+ <zeroOrMore>
+ <ref name="pagesNuma"/>
+ </zeroOrMore>
+
+ <optional>
+ <element name="distances">
+ <zeroOrMore>
+ <element name="sibling">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="value">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="cpus">
+ <attribute name="num">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <oneOrMore>
+ <ref name="cpu"/>
+ </oneOrMore>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="memory">
+ <element name="memory">
+ <ref name="scaledInteger"/>
+ </element>
+ </define>
+
+ <define name="cpu">
+ <element name="cpu">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <optional>
+ <attribute name="socket_id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="die_id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="core_id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="siblings">
+ <ref name="cpuset"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="cache">
+ <element name="cache">
+ <oneOrMore>
+ <element name="bank">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="level">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <ref name="cacheType"/>
+ <attribute name="size">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ <attribute name="cpus">
+ <ref name="cpuset"/>
+ </attribute>
+ <zeroOrMore>
+ <element name="control">
+ <attribute name="granularity">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <optional>
+ <attribute name="min">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ <ref name="cacheType"/>
+ <attribute name="maxAllocs">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </oneOrMore>
+ <optional>
+ <ref name="cpuMonitor"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="cacheType">
+ <attribute name="type">
+ <choice>
+ <value>both</value>
+ <value>code</value>
+ <value>data</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="memory_bandwidth">
+ <element name="memory_bandwidth">
+ <oneOrMore>
+ <element name="node">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="cpus">
+ <ref name="cpuset"/>
+ </attribute>
+ <zeroOrMore>
+ <element name="control">
+ <attribute name="granularity">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <optional>
+ <attribute name="min">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <attribute name="maxAllocs">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </oneOrMore>
+ <optional>
+ <ref name="cpuMonitor"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="cpuMonitor">
+ <element name="monitor">
+ <optional>
+ <attribute name="level">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="reuseThreshold">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <attribute name="maxMonitors">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <oneOrMore>
+ <element name="feature">
+ <attribute name="name">
+ <ref name="monitorFeature"/>
+ </attribute>
+ </element>
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="monitorFeature">
+ <data type="string">
+ <param name="pattern">(llc_|mbm_)[a-zA-Z0-9\-_]+</param>
+ </data>
+ </define>
+
+ <define name="guestcaps">
+ <element name="guest">
+ <ref name="ostype"/>
+ <ref name="arch"/>
+ <optional>
+ <ref name="features"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="ostype">
+ <element name="os_type">
+ <choice>
+ <value>xen</value> <!-- Xen 3.0 pv -->
+ <value>linux</value> <!-- same as "xen" - meant to be legacy -->
+ <value>hvm</value> <!-- unmodified OS -->
+ <value>exe</value> <!-- For container based virt -->
+ <value>uml</value> <!-- user mode linux; NOT USED ANYMORE -->
+ </choice>
+ </element>
+ </define>
+
+ <define name="arch">
+ <element name="arch">
+ <attribute name="name">
+ <ref name="archnames"/>
+ </attribute>
+ <ref name="wordsize"/>
+ <optional>
+ <ref name="emulator"/>
+ </optional>
+ <optional>
+ <ref name="loader"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="machine"/>
+ </zeroOrMore>
+ <oneOrMore>
+ <ref name="domain"/>
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="emulator">
+ <element name="emulator">
+ <ref name="absFilePath"/>
+ </element>
+ </define>
+
+ <define name="loader">
+ <element name="loader">
+ <ref name="absFilePath"/>
+ </element>
+ </define>
+
+ <define name="wordsize">
+ <element name="wordsize">
+ <choice>
+ <value>31</value>
+ <value>32</value>
+ <value>64</value>
+ </choice>
+ </element>
+ </define>
+
+ <define name="machine">
+ <element name="machine">
+ <optional>
+ <attribute name="canonical">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="maxCpus">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <text/>
+ </element>
+ </define>
+
+ <define name="domain">
+ <element name="domain">
+ <attribute name="type">
+ <choice>
+ <value>qemu</value>
+ <value>kqemu</value>
+ <value>kvm</value>
+ <value>xen</value>
+ <value>uml</value> <!-- NOT USED ANYMORE -->
+ <value>lxc</value>
+ <value>openvz</value>
+ <value>test</value>
+ </choice>
+ </attribute>
+
+ <optional>
+ <ref name="emulator"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="machine"/>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="features">
+ <element name="features">
+ <interleave>
+ <optional>
+ <element name="pae">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="nonpae">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="ia64_be">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="acpi">
+ <ref name="featuretoggle"/>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="apic">
+ <ref name="featuretoggle"/>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="cpuselection">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="deviceboot">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="disksnapshot">
+ <ref name="featuretoggle"/>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="hap">
+ <ref name="featuretoggle"/>
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="featuretoggle">
+ <attribute name="toggle">
+ <ref name="virYesNo"/>
+ </attribute>
+ <attribute name="default">
+ <ref name="virOnOff"/>
+ </attribute>
+ </define>
+
+ <define name="pagesNuma">
+ <element name="pages">
+ <ref name="pagesElem"/>
+ <ref name="unsignedInt"/>
+ </element>
+ </define>
+ <define name="pagesElem">
+ <optional>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </optional>
+ <attribute name="size">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </define>
+</grammar>
diff --git a/src/main/resources/libvirt/rng/cpu.rng b/src/main/resources/libvirt/rng/cpu.rng
new file mode 100644
index 0000000..d1eb674
--- /dev/null
+++ b/src/main/resources/libvirt/rng/cpu.rng
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <include href="cputypes.rng"/>
+
+ <start>
+ <choice>
+ <ref name="guestcpu"/>
+ <ref name="hostcpu"/>
+ </choice>
+ </start>
+</grammar>
diff --git a/src/main/resources/libvirt/rng/cputypes.rng b/src/main/resources/libvirt/rng/cputypes.rng
new file mode 100644
index 0000000..f66bc62
--- /dev/null
+++ b/src/main/resources/libvirt/rng/cputypes.rng
@@ -0,0 +1,420 @@
+<?xml version="1.0"?>
+<!-- CPU-related definitions used in multiple grammars -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <define name="cpuMode">
+ <attribute name="mode">
+ <choice>
+ <value>custom</value>
+ <value>host-model</value>
+ <value>host-passthrough</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="cpuMatch">
+ <attribute name="match">
+ <choice>
+ <value>minimum</value>
+ <value>exact</value>
+ <value>strict</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="cpuCheck">
+ <attribute name="check">
+ <choice>
+ <value>none</value>
+ <value>partial</value>
+ <value>full</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="cpuModel">
+ <element name="model">
+ <optional>
+ <attribute name="fallback">
+ <choice>
+ <value>allow</value>
+ <value>forbid</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="vendor_id">
+ <data type="string">
+ <param name="pattern">[^,]{12}</param>
+ </data>
+ </attribute>
+ </optional>
+ <choice>
+ <text/>
+ <empty/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="cpuVendor">
+ <element name="vendor">
+ <text/>
+ </element>
+ </define>
+
+ <define name="cpuFeature">
+ <element name="feature">
+ <attribute name="policy">
+ <choice>
+ <value>force</value>
+ <value>require</value>
+ <value>optional</value>
+ <value>disable</value>
+ <value>forbid</value>
+ </choice>
+ </attribute>
+ <attribute name="name">
+ <ref name="featureName"/>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="cpuTopology">
+ <element name="topology">
+ <attribute name="sockets">
+ <ref name="positiveInteger"/>
+ </attribute>
+ <optional>
+ <attribute name="dies">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <attribute name="cores">
+ <ref name="positiveInteger"/>
+ </attribute>
+ <attribute name="threads">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="cpuNuma">
+ <element name="numa">
+ <interleave>
+ <oneOrMore>
+ <ref name="numaCell"/>
+ </oneOrMore>
+ <optional>
+ <ref name="numaInterconnects"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="numaCell">
+ <element name="cell">
+ <optional>
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="cpus">
+ <ref name="cpuset"/>
+ </attribute>
+ </optional>
+ <attribute name="memory">
+ <ref name="memoryKB"/>
+ </attribute>
+ <optional>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="memAccess">
+ <choice>
+ <value>shared</value>
+ <value>private</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="discard">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="distances">
+ <oneOrMore>
+ <ref name="numaDistance"/>
+ </oneOrMore>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <ref name="numaCache"/>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="numaDistance">
+ <element name="sibling">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="value">
+ <ref name="numaDistanceValue"/>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="numaCache">
+ <element name="cache">
+ <attribute name="level">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="associativity">
+ <choice>
+ <value>none</value>
+ <value>direct</value>
+ <value>full</value>
+ </choice>
+ </attribute>
+ <attribute name="policy">
+ <choice>
+ <value>none</value>
+ <value>writeback</value>
+ <value>writethrough</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <element name="size">
+ <attribute name="value">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </element>
+ <element name="line">
+ <attribute name="value">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </element>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="numaInterconnects">
+ <element name="interconnects">
+ <interleave>
+ <zeroOrMore>
+ <element name="latency">
+ <attribute name="initiator">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="target">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <optional>
+ <attribute name="cache">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <attribute name="type">
+ <choice>
+ <value>access</value>
+ <value>read</value>
+ <value>write</value>
+ </choice>
+ </attribute>
+ <attribute name="value">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="bandwidth">
+ <attribute name="initiator">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="target">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="type">
+ <choice>
+ <value>access</value>
+ <value>read</value>
+ <value>write</value>
+ </choice>
+ </attribute>
+ <attribute name="value">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <!-- Memory as an attribute is in KiB, no way to express a unit -->
+ <define name="memoryKB">
+ <data type="unsignedLong"/>
+ </define>
+ <define name="featureName">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9\-_\.]+</param>
+ </data>
+ </define>
+
+ <define name="cpuCache">
+ <element name="cache">
+ <optional>
+ <attribute name="level">
+ <choice>
+ <value>1</value>
+ <value>2</value>
+ <value>3</value>
+ </choice>
+ </attribute>
+ </optional>
+ <attribute name="mode">
+ <choice>
+ <value>emulate</value>
+ <value>passthrough</value>
+ <value>disable</value>
+ </choice>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="hostcpu">
+ <element name="cpu">
+ <element name="arch">
+ <ref name="archnames"/>
+ </element>
+ <optional>
+ <element name="features">
+ <optional>
+ <element name="pae"><empty/></element>
+ </optional>
+ <optional>
+ <element name="nonpae"><empty/></element>
+ </optional>
+ <optional>
+ <element name="vmx"><empty/></element>
+ </optional>
+ <optional>
+ <element name="svm"><empty/></element>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="model">
+ <text/>
+ </element>
+ <optional>
+ <element name="vendor">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="microcode">
+ <attribute name="version">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="counter">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <attribute name="frequency">
+ <ref name="positiveInteger"/>
+ </attribute>
+ <attribute name="scaling">
+ <ref name="virYesNo"/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <ref name="cpuTopology"/>
+ </optional>
+ <zeroOrMore>
+ <element name="feature">
+ <attribute name="name">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9\-_]+</param>
+ </data>
+ </attribute>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="pages">
+ <optional>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </optional>
+ <attribute name="size">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </optional>
+ </element>
+ </define>
+
+ <define name="guestcpu">
+ <element name="cpu">
+ <optional>
+ <ref name="cpuMode"/>
+ </optional>
+ <optional>
+ <ref name="cpuMatch"/>
+ </optional>
+ <optional>
+ <ref name="cpuCheck"/>
+ </optional>
+ <optional>
+ <attribute name="migratable">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <ref name="cpuModel"/>
+ </optional>
+ <optional>
+ <ref name="cpuVendor"/>
+ </optional>
+ <optional>
+ <ref name="cpuTopology"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="cpuFeature"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="cpuNuma"/>
+ </optional>
+ <optional>
+ <ref name="cpuCache"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/domain.rng b/src/main/resources/libvirt/rng/domain.rng
new file mode 100644
index 0000000..b93bbed
--- /dev/null
+++ b/src/main/resources/libvirt/rng/domain.rng
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <!-- Grammar for accepting a domain element, both as top level, and
+ also suitable for inclusion in domainsnapshot.rng -->
+ <start>
+ <ref name="domain"/>
+ </start>
+
+ <include href="domaincommon.rng"/>
+
+ <define name="storageStartupPolicy" combine="choice">
+ <!-- overrides the no-op version in storagecommon.rng -->
+ <ref name="startupPolicy"/>
+ </define>
+
+ <define name="storageSourceExtra" combine="choice">
+ <!-- overrides the no-op version in storagecommon.rng -->
+ <ref name="diskspec"/>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/domainbackup.rng b/src/main/resources/libvirt/rng/domainbackup.rng
new file mode 100644
index 0000000..c03455a
--- /dev/null
+++ b/src/main/resources/libvirt/rng/domainbackup.rng
@@ -0,0 +1,300 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt domain backup properties XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <ref name="domainbackup"/>
+ </start>
+
+ <include href="domaincommon.rng"/>
+
+ <define name="backupEncryption">
+ <element name="encryption">
+ <attribute name="format">
+ <choice>
+ <value>luks</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <ref name="secret"/>
+ <optional>
+ <element name="cipher">
+ <ref name="keycipher"/>
+ </element>
+ <element name="ivgen">
+ <ref name="keyivgen"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="domainbackup">
+ <element name="domainbackup">
+ <interleave>
+ <optional>
+ <element name="incremental">
+ <text/>
+ </element>
+ </optional>
+ <choice>
+ <group>
+ <optional>
+ <attribute name="mode">
+ <value>push</value>
+ </attribute>
+ </optional>
+ <ref name="backupDisksPush"/>
+ </group>
+ <group>
+ <attribute name="mode">
+ <value>pull</value>
+ </attribute>
+ <interleave>
+ <element name="server">
+ <optional>
+ <attribute name="tls">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <choice>
+ <group>
+ <optional>
+ <attribute name="transport">
+ <value>tcp</value>
+ </attribute>
+ </optional>
+ <attribute name="name">
+ <choice>
+ <ref name="dnsName"/>
+ <ref name="ipAddr"/>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="port">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="transport">
+ <value>unix</value>
+ </attribute>
+ <attribute name="socket">
+ <ref name="absFilePath"/>
+ </attribute>
+ </group>
+ </choice>
+ </element>
+ <ref name="backupDisksPull"/>
+ </interleave>
+ </group>
+ </choice>
+ </interleave>
+ </element>
+ </define>
+
+
+ <define name="backupDiskMode">
+ <optional>
+ <choice>
+ <attribute name="backupmode">
+ <value>full</value>
+ </attribute>
+ <group>
+ <optional>
+ <attribute name="backupmode">
+ <value>incremental</value>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="incremental"/>
+ </optional>
+ </group>
+ </choice>
+ </optional>
+ </define>
+
+ <define name="backupPushDriver">
+ <optional>
+ <element name="driver">
+ <attribute name="type">
+ <ref name="storageFormat"/>
+ </attribute>
+ </element>
+ </optional>
+ </define>
+
+ <define name="backupPullDriver">
+ <optional>
+ <element name="driver">
+ <attribute name="type">
+ <value>qcow2</value>
+ </attribute>
+ </element>
+ </optional>
+ </define>
+
+ <define name="backupAttr">
+ <optional>
+ <attribute name="backup">
+ <choice>
+ <value>yes</value>
+ </choice>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="backupDisksPush">
+ <optional>
+ <element name="disks">
+ <oneOrMore>
+ <element name="disk">
+ <attribute name="name">
+ <ref name="diskTarget"/>
+ </attribute>
+ <ref name="backupDiskMode"/>
+ <choice>
+ <group>
+ <attribute name="backup">
+ <value>no</value>
+ </attribute>
+ </group>
+ <group>
+ <ref name="backupAttr"/>
+ <attribute name="type">
+ <value>file</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="target">
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ <interleave>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="backupEncryption"/>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ <ref name="backupPushDriver"/>
+ </interleave>
+ </group>
+ <group>
+ <ref name="backupAttr"/>
+ <attribute name="type">
+ <value>block</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="target">
+ <attribute name="dev">
+ <ref name="absFilePath"/>
+ </attribute>
+ <interleave>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="backupEncryption"/>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ <ref name="backupPushDriver"/>
+ </interleave>
+ </group>
+ </choice>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
+ </define>
+
+ <define name="backupDisksPull">
+ <optional>
+ <element name="disks">
+ <oneOrMore>
+ <element name="disk">
+ <attribute name="name">
+ <ref name="diskTarget"/>
+ </attribute>
+ <ref name="backupDiskMode"/>
+ <optional>
+ <attribute name="exportname">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="exportbitmap">
+ <text/>
+ </attribute>
+ </optional>
+ <choice>
+ <group>
+ <attribute name="backup">
+ <value>no</value>
+ </attribute>
+ </group>
+ <group>
+ <optional>
+ <ref name="backupAttr"/>
+ <attribute name="type">
+ <value>file</value>
+ </attribute>
+ </optional>
+ <optional>
+ <interleave>
+ <element name="scratch">
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ <interleave>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="backupEncryption"/>
+ </optional>
+ </interleave>
+ </element>
+ <ref name="backupPullDriver"/>
+ </interleave>
+ </optional>
+ </group>
+ <group>
+ <ref name="backupAttr"/>
+ <attribute name="type">
+ <value>block</value>
+ </attribute>
+ <interleave>
+ <element name="scratch">
+ <attribute name="dev">
+ <ref name="absFilePath"/>
+ </attribute>
+ <interleave>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="backupEncryption"/>
+ </optional>
+ </interleave>
+ </element>
+ <ref name="backupPullDriver"/>
+ </interleave>
+ </group>
+ </choice>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/domaincaps.rng b/src/main/resources/libvirt/rng/domaincaps.rng
new file mode 100644
index 0000000..0dbffb2
--- /dev/null
+++ b/src/main/resources/libvirt/rng/domaincaps.rng
@@ -0,0 +1,294 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt domain capabilities XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <include href="cputypes.rng"/>
+ <start>
+ <ref name="domainCapabilities"/>
+ </start>
+
+
+ <define name="domainCapabilities">
+ <element name="domainCapabilities">
+ <interleave>
+ <element name="path">
+ <ref name="absFilePath"/>
+ </element>
+ <element name="domain">
+ <text/>
+ </element>
+ <optional>
+ <element name="machine">
+ <text/>
+ </element>
+ </optional>
+ <element name="arch">
+ <text/>
+ </element>
+ <optional>
+ <ref name="vcpu"/>
+ </optional>
+ <optional>
+ <ref name="iothreads"/>
+ </optional>
+ <optional>
+ <ref name="os"/>
+ </optional>
+ <optional>
+ <ref name="cpu"/>
+ </optional>
+ <optional>
+ <ref name="devices"/>
+ </optional>
+ <optional>
+ <ref name="features"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="vcpu">
+ <element name="vcpu">
+ <attribute name="max">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="iothreads">
+ <element name="iothreads">
+ <ref name="supported"/>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="loader">
+ <element name="loader">
+ <ref name="supported"/>
+ <optional>
+ <ref name="value"/>
+ </optional>
+ <ref name="enum"/>
+ </element>
+ </define>
+
+ <define name="os">
+ <element name="os">
+ <interleave>
+ <ref name="supported"/>
+ <ref name="enum"/>
+ <optional>
+ <ref name="loader"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="cpu">
+ <element name="cpu">
+ <ref name="cpuHost"/>
+ <ref name="cpuHostModel"/>
+ <ref name="cpuCustom"/>
+ </element>
+ </define>
+
+ <define name="cpuHost">
+ <element name="mode">
+ <attribute name="name">
+ <value>host-passthrough</value>
+ </attribute>
+ <ref name="supported"/>
+ <optional>
+ <ref name="enum"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="cpuHostModel">
+ <element name="mode">
+ <attribute name="name">
+ <value>host-model</value>
+ </attribute>
+ <ref name="supported"/>
+ <optional>
+ <ref name="cpuModel"/>
+ <optional>
+ <ref name="cpuVendor"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="cpuFeature"/>
+ </zeroOrMore>
+ </optional>
+ </element>
+ </define>
+
+ <define name="cpuCustom">
+ <element name="mode">
+ <attribute name="name">
+ <value>custom</value>
+ </attribute>
+ <ref name="supported"/>
+ <zeroOrMore>
+ <element name="model">
+ <attribute name="usable">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ <value>unknown</value>
+ </choice>
+ </attribute>
+ <text/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="devices">
+ <element name="devices">
+ <optional>
+ <ref name="disk"/>
+ </optional>
+ <optional>
+ <ref name="graphics"/>
+ </optional>
+ <optional>
+ <ref name="video"/>
+ </optional>
+ <optional>
+ <ref name="hostdev"/>
+ </optional>
+ <optional>
+ <ref name="rng"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="disk">
+ <element name="disk">
+ <ref name="supported"/>
+ <ref name="enum"/>
+ </element>
+ </define>
+
+ <define name="graphics">
+ <element name="graphics">
+ <ref name="supported"/>
+ <ref name="enum"/>
+ </element>
+ </define>
+
+ <define name="video">
+ <element name="video">
+ <ref name="supported"/>
+ <ref name="enum"/>
+ </element>
+ </define>
+
+ <define name="hostdev">
+ <element name="hostdev">
+ <ref name="supported"/>
+ <ref name="enum"/>
+ </element>
+ </define>
+
+ <define name="rng">
+ <element name="rng">
+ <ref name="supported"/>
+ <ref name="enum"/>
+ </element>
+ </define>
+
+ <define name="features">
+ <element name="features">
+ <optional>
+ <ref name="gic"/>
+ </optional>
+ <optional>
+ <ref name="vmcoreinfo"/>
+ </optional>
+ <optional>
+ <ref name="vmgenid"/>
+ </optional>
+ <optional>
+ <ref name="backingStoreInput"/>
+ </optional>
+ <optional>
+ <ref name="backup"/>
+ </optional>
+ <optional>
+ <ref name="sev"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="gic">
+ <element name="gic">
+ <ref name="supported"/>
+ <ref name="enum"/>
+ </element>
+ </define>
+
+ <define name="vmcoreinfo">
+ <element name="vmcoreinfo">
+ <ref name="supported"/>
+ </element>
+ </define>
+
+ <define name="vmgenid">
+ <element name="genid">
+ <ref name="supported"/>
+ </element>
+ </define>
+
+ <define name="backingStoreInput">
+ <element name="backingStoreInput">
+ <ref name="supported"/>
+ </element>
+ </define>
+
+ <define name="backup">
+ <element name="backup">
+ <ref name="supported"/>
+ </element>
+ </define>
+
+ <define name="sev">
+ <element name="sev">
+ <ref name="supported"/>
+ <optional>
+ <element name="cbitpos">
+ <data type="unsignedInt"/>
+ </element>
+ <element name="reducedPhysBits">
+ <data type="unsignedInt"/>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="value">
+ <zeroOrMore>
+ <element name="value">
+ <text/>
+ </element>
+ </zeroOrMore>
+ </define>
+
+ <define name="supported">
+ <attribute name="supported">
+ <ref name="virYesNo"/>
+ </attribute>
+ </define>
+
+ <define name="enum">
+ <zeroOrMore>
+ <element name="enum">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <ref name="value"/>
+ </element>
+ </zeroOrMore>
+ </define>
+</grammar>
diff --git a/src/main/resources/libvirt/rng/domaincheckpoint.rng b/src/main/resources/libvirt/rng/domaincheckpoint.rng
new file mode 100644
index 0000000..a1c8b0b
--- /dev/null
+++ b/src/main/resources/libvirt/rng/domaincheckpoint.rng
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt domain checkpoint properties XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="domaincheckpoint"/>
+ </start>
+
+ <include href="domaincommon.rng"/>
+
+ <define name="domaincheckpoint">
+ <element name="domaincheckpoint">
+ <interleave>
+ <optional>
+ <element name="name">
+ <ref name="checkpointName"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="description">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="creationTime">
+ <ref name="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="disks">
+ <oneOrMore>
+ <ref name="diskcheckpoint"/>
+ </oneOrMore>
+ </element>
+ </optional>
+ <optional>
+ <!-- Nested grammar ensures that any of our overrides of
+ storagecommon/domaincommon defines do not conflict
+ with any domain.rng overrides. -->
+ <grammar>
+ <include href="domain.rng"/>
+ </grammar>
+ </optional>
+ <optional>
+ <element name="parent">
+ <element name="name">
+ <ref name="checkpointName"/>
+ </element>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskcheckpoint">
+ <element name="disk">
+ <attribute name="name">
+ <choice>
+ <ref name="diskTarget"/>
+ <ref name="absFilePath"/>
+ </choice>
+ </attribute>
+ <choice>
+ <attribute name="checkpoint">
+ <value>no</value>
+ </attribute>
+ <group>
+ <optional>
+ <attribute name="checkpoint">
+ <value>bitmap</value>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="bitmap">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="size">
+ <ref name="unsignedLong"/>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="checkpointName">
+ <data type="string">
+ <!-- Notably: no leading "." and no embedded "/" or newline -->
+ <param name="pattern">[a-zA-Z0-9_\-][a-zA-Z0-9_\-.]*</param>
+ </data>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/domaincommon.rng b/src/main/resources/libvirt/rng/domaincommon.rng
new file mode 100644
index 0000000..7dc419b
--- /dev/null
+++ b/src/main/resources/libvirt/rng/domaincommon.rng
@@ -0,0 +1,7270 @@
+<?xml version="1.0"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <!-- domain-related definitions used in multiple grammars -->
+ <include href="basictypes.rng"/>
+ <include href="storagecommon.rng"/>
+ <include href="networkcommon.rng"/>
+ <include href="cputypes.rng"/>
+ <include href="nwfilter_params.rng"/>
+
+ <!--
+ description and title element, may be placed anywhere under the root
+ -->
+ <define name="description">
+ <element name="description">
+ <text/>
+ </element>
+ </define>
+
+ <define name="title">
+ <element name="title">
+ <ref name="objectNameWithSlash"/>
+ </element>
+ </define>
+
+ <define name="createMode">
+ <data type="unsignedInt">
+ <param name="pattern">0[0-7]{3}|[0-7]{1,3}</param>
+ </data>
+ </define>
+
+ <!--
+ We handle only document defining a domain
+ -->
+ <define name="domain">
+ <element name="domain">
+ <ref name="hvs"/>
+ <interleave>
+ <ref name="ids"/>
+ <optional>
+ <ref name="title"/>
+ </optional>
+ <optional>
+ <ref name="description"/>
+ </optional>
+ <optional>
+ <ref name="metadata"/>
+ </optional>
+ <optional>
+ <ref name="guestcpu"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="sysinfo"/>
+ </zeroOrMore>
+ <ref name="os"/>
+ <ref name="clock"/>
+ <ref name="resources"/>
+ <ref name="features"/>
+ <ref name="events"/>
+ <optional>
+ <ref name="pm"/>
+ </optional>
+ <optional>
+ <ref name="perf"/>
+ </optional>
+ <optional>
+ <ref name="idmap"/>
+ </optional>
+ <optional>
+ <ref name="devices"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="seclabel"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="qemucmdline"/>
+ </optional>
+ <optional>
+ <ref name="qemucapabilities"/>
+ </optional>
+ <optional>
+ <ref name="lxcsharens"/>
+ </optional>
+ <optional>
+ <ref name="keywrap"/>
+ </optional>
+ <optional>
+ <ref name="launchSecurity"/>
+ </optional>
+ <optional>
+ <ref name="bhyvecmdline"/>
+ </optional>
+ <optional>
+ <ref name="xencmdline"/>
+ </optional>
+ <optional>
+ <ref name="vmwaredatacenterpath"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="seclabel">
+ <element name="seclabel">
+ <optional>
+ <attribute name="model">
+ <text/>
+ </attribute>
+ </optional>
+ <choice>
+ <group>
+ <!-- with dynamic label (default), relabel must be yes, baselabel
+ is optional, and label and imagelabel are output-only -->
+ <optional>
+ <attribute name="type">
+ <value>dynamic</value>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="relabel">
+ <value>yes</value>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="label">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="imagelabel">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="baselabel">
+ <text/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ <group>
+ <!-- with static label, relabel can be either format (default
+ no), label is required, imagelabel is output-only, and no
+ baselabel is present -->
+ <attribute name="type">
+ <value>static</value>
+ </attribute>
+ <optional>
+ <attribute name="relabel">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <element name="label">
+ <text/>
+ </element>
+ <optional>
+ <element name="imagelabel">
+ <text/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ <group>
+ <!-- with none, relabel must be no if present -->
+ <attribute name="type">
+ <value>none</value>
+ </attribute>
+ <optional>
+ <attribute name="relabel">
+ <value>no</value>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+ <define name="devSeclabel">
+ <element name="seclabel">
+ <!-- A per-device seclabel override is more limited, either
+ relabel=no or a <label> must be present on input;
+ output also can include labelskip=yes. -->
+ <optional>
+ <attribute name="model">
+ <text/>
+ </attribute>
+ </optional>
+ <choice>
+ <group>
+ <attribute name="relabel">
+ <value>no</value>
+ </attribute>
+ </group>
+ <group>
+ <attribute name="labelskip">
+ <value>yes</value>
+ </attribute>
+ </group>
+ <group>
+ <optional>
+ <attribute name="relabel">
+ <value>yes</value>
+ </attribute>
+ </optional>
+ <oneOrMore>
+ <element name="label">
+ <text/>
+ </element>
+ </oneOrMore>
+ </group>
+ </choice>
+ </element>
+ </define>
+ <define name="hvs">
+ <attribute name="type">
+ <choice>
+ <value>qemu</value>
+ <value>kqemu</value>
+ <value>kvm</value>
+ <value>xen</value>
+ <value>lxc</value>
+ <value>uml</value> <!-- NOT USED ANYMORE -->
+ <value>openvz</value>
+ <value>test</value>
+ <value>vmware</value>
+ <value>hyperv</value>
+ <value>vbox</value>
+ <value>phyp</value> <!-- NOT USED ANYMORE -->
+ <value>vz</value>
+ <value>bhyve</value>
+ </choice>
+ </attribute>
+ </define>
+ <define name="os">
+ <choice>
+ <ref name="osxen"/>
+ <ref name="oshvm"/>
+ <ref name="osexe"/>
+ </choice>
+ </define>
+ <define name="osxen">
+ <choice>
+ <group>
+ <optional>
+ <ref name="bootloader"/>
+ </optional>
+ <element name="os">
+ <ref name="ostypexen"/>
+ <ref name="osbootkernel"/>
+ </element>
+ </group>
+ <group>
+ <ref name="bootloader"/>
+ <optional>
+ <element name="os">
+ <ref name="ostypexen"/>
+ <optional>
+ <ref name="osbootkernel"/>
+ </optional>
+ </element>
+ </optional>
+ </group>
+ </choice>
+ </define>
+ <define name="oshvm">
+ <optional>
+ <ref name="bootloader"/>
+ </optional>
+ <element name="os">
+ <interleave>
+ <optional>
+ <attribute name="firmware">
+ <choice>
+ <value>bios</value>
+ <value>efi</value>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="ostypehvm"/>
+ <optional>
+ <element name="loader">
+ <optional>
+ <attribute name="readonly">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="secure">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="type">
+ <choice>
+ <value>rom</value>
+ <value>pflash</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="absFilePath"/>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="nvram">
+ <optional>
+ <attribute name="template">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="absFilePath"/>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <ref name="osbootkernel"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="osbootdev"/>
+ </zeroOrMore>
+ <optional>
+ <element name="bootmenu">
+ <attribute name="enable">
+ <ref name="virYesNo"/>
+ </attribute>
+ <optional>
+ <attribute name="timeout">
+ <ref name="unsignedShort"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <ref name="smbios"/>
+ </optional>
+ <optional>
+ <ref name="bios"/>
+ </optional>
+ <optional>
+ <ref name="acpiTable"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="ostypexen">
+ <element name="type">
+ <optional>
+ <attribute name="arch">
+ <choice>
+ <value>i686</value>
+ <value>x86_64</value>
+ <value>ia64</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="machine">
+ <choice>
+ <value>xenpv</value>
+ <value>xenfv</value>
+ <value>xenpvh</value>
+ </choice>
+ </attribute>
+ </optional>
+ <choice>
+ <value>xen</value>
+ <value>linux</value>
+ <value>xenpvh</value>
+ </choice>
+ </element>
+ </define>
+ <define name="ostypehvm">
+ <element name="type">
+ <optional>
+ <attribute name="arch">
+ <ref name="archnames"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="machine">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\.\-]+</param>
+ </data>
+ </attribute>
+ </optional>
+ <value>hvm</value>
+ </element>
+ </define>
+
+ <define name="osexe">
+ <element name="os">
+ <element name="type">
+ <optional>
+ <attribute name="arch">
+ <ref name="archnames"/>
+ </attribute>
+ </optional>
+ <value>exe</value>
+ </element>
+ <interleave>
+ <optional>
+ <element name="init">
+ <ref name="absFilePath"/>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="initarg">
+ <text/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="initenv">
+ <attribute name="name">
+ <data type="string">
+ <param name="pattern">[a-zA-Z_]+[a-zA-Z0-9_]*</param>
+ </data>
+ </attribute>
+ <text/>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="initdir">
+ <ref name="absFilePath"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="inituser">
+ <ref name="genericName"/>
+ </element>
+ <element name="initgroup">
+ <ref name="genericName"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="keywrap">
+ <element name="keywrap">
+ <oneOrMore>
+ <element name="cipher">
+ <attribute name="name">
+ <choice>
+ <value>aes</value>
+ <value>dea</value>
+ </choice>
+ </attribute>
+ <attribute name="state">
+ <ref name="virOnOff"/>
+ </attribute>
+ </element>
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="launchSecurity">
+ <element name="launchSecurity">
+ <attribute name="type">
+ <value>sev</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="cbitpos">
+ <data type="unsignedInt"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="reducedPhysBits">
+ <data type="unsignedInt"/>
+ </element>
+ </optional>
+ <element name="policy">
+ <ref name="hexuint"/>
+ </element>
+ <optional>
+ <element name="handle">
+ <ref name="unsignedInt"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="dhCert">
+ <data type="string"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="session">
+ <data type="string"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <!--
+ Enable or disable perf events for the domain. For each
+ of the events the following rules apply:
+ on: the event will be forcefully enabled
+ off: the event will be forcefully disabled
+ not specified: the event will be disabled by default
+ -->
+ <define name="perf">
+ <element name="perf">
+ <oneOrMore>
+ <element name="event">
+ <attribute name="name">
+ <choice>
+ <value>cmt</value>
+ <value>mbmt</value>
+ <value>mbml</value>
+ <value>cpu_cycles</value>
+ <value>instructions</value>
+ <value>cache_references</value>
+ <value>cache_misses</value>
+ <value>branch_instructions</value>
+ <value>branch_misses</value>
+ <value>bus_cycles</value>
+ <value>stalled_cycles_frontend</value>
+ <value>stalled_cycles_backend</value>
+ <value>ref_cpu_cycles</value>
+ <value>cpu_clock</value>
+ <value>task_clock</value>
+ <value>page_faults</value>
+ <value>context_switches</value>
+ <value>cpu_migrations</value>
+ <value>page_faults_min</value>
+ <value>page_faults_maj</value>
+ <value>alignment_faults</value>
+ <value>emulation_faults</value>
+ </choice>
+ </attribute>
+ <attribute name="enabled">
+ <ref name="virYesNo"/>
+ </attribute>
+ </element>
+ </oneOrMore>
+ </element>
+ </define>
+
+ <!--
+ The Identifiers can be:
+ - an optional id attribute with a number on the domain element
+ - a mandatory name
+ - an optional uuid
+ -->
+ <define name="ids">
+ <optional>
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <element name="name">
+ <ref name="objectNameWithSlash"/>
+ </element>
+ <optional>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="genid">
+ <choice>
+ <ref name="UUID"/>
+ <empty/>
+ </choice>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+ <define name="idmap">
+ <element name="idmap">
+ <interleave>
+ <zeroOrMore>
+ <element name="uid">
+ <attribute name="start">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="target">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="count">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="gid">
+ <attribute name="start">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="target">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="count">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+ <!--
+ Resources usage defines the amount of memory (maximum and possibly
+ current usage) and number of virtual CPUs used by that domain.
+ We can't check here the rule that currentMemory <= memory
+ -->
+ <define name="resources">
+ <interleave>
+ <optional>
+ <element name="memory">
+ <ref name="scaledInteger"/>
+ <optional>
+ <attribute name="dumpCore">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="maxMemory">
+ <ref name="scaledInteger"/>
+ <attribute name="slots">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="currentMemory">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="memoryBacking">
+ <interleave>
+ <optional>
+ <element name="hugepages">
+ <zeroOrMore>
+ <element name="page">
+ <attribute name="size">
+ <ref name="unsignedLong"/>
+ </attribute>
+ <optional>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="nodeset">
+ <ref name="cpuset"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ <optional>
+ <element name="nosharepages">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="locked">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="source">
+ <attribute name="type">
+ <choice>
+ <value>file</value>
+ <value>anonymous</value>
+ <value>memfd</value>
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="access">
+ <attribute name="mode">
+ <choice>
+ <value>shared</value>
+ <value>private</value>
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="allocation">
+ <attribute name="mode">
+ <choice>
+ <value>immediate</value>
+ <value>ondemand</value>
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="discard">
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="vcpu">
+ <optional>
+ <attribute name="placement">
+ <choice>
+ <value>static</value>
+ <value>auto</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="cpuset">
+ <ref name="cpuset"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="current">
+ <ref name="countCPU"/>
+ </attribute>
+ </optional>
+ <ref name="countCPU"/>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="vcpus">
+ <zeroOrMore>
+ <element name="vcpu">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="enabled">
+ <ref name="virYesNo"/>
+ </attribute>
+ <optional>
+ <attribute name="hotpluggable">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="order">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="iothreads">
+ <ref name="unsignedInt"/>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="iothreadids">
+ <zeroOrMore>
+ <element name="iothread">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+
+ <optional>
+ <ref name="blkiotune"/>
+ </optional>
+
+ <optional>
+ <ref name="memtune"/>
+ </optional>
+
+ <optional>
+ <ref name="cputune"/>
+ </optional>
+
+ <optional>
+ <ref name="numatune"/>
+ </optional>
+
+ <optional>
+ <ref name="respartition"/>
+ </optional>
+ </interleave>
+ </define>
+
+ <!-- The Blkio cgroup related tunables would go in the blkiotune -->
+ <define name="blkiotune">
+ <element name="blkiotune">
+ <interleave>
+ <!-- I/O weight the VM can use -->
+ <optional>
+ <element name="weight">
+ <ref name="weight"/>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="device">
+ <interleave>
+ <element name="path">
+ <ref name="absFilePath"/>
+ </element>
+ <optional>
+ <element name="weight">
+ <ref name="weight"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="read_iops_sec">
+ <data type="unsignedInt"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_iops_sec">
+ <data type="unsignedInt"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="read_bytes_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_bytes_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <!-- All the memory/swap related tunables would go in the memtune -->
+ <define name="memtune">
+ <element name="memtune">
+ <!-- Maximum memory the VM can use -->
+ <optional>
+ <element name="hard_limit">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <!-- Minimum memory ascertained for the VM during contention -->
+ <optional>
+ <element name="soft_limit">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <!-- Minimum amount of memory required to start the VM -->
+ <optional>
+ <element name="min_guarantee">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <!-- Maximum swap area the VM can use -->
+ <optional>
+ <element name="swap_hard_limit">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <!-- All the cpu related tunables would go in the cputune -->
+ <define name="cputune">
+ <element name="cputune">
+ <interleave>
+ <optional>
+ <element name="shares">
+ <ref name="cpushares"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="global_period">
+ <ref name="cpuperiod"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="global_quota">
+ <ref name="cpuquota"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="period">
+ <ref name="cpuperiod"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="quota">
+ <ref name="cpuquota"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="emulator_period">
+ <ref name="cpuperiod"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="emulator_quota">
+ <ref name="cpuquota"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="iothread_period">
+ <ref name="cpuperiod"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="iothread_quota">
+ <ref name="cpuquota"/>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="vcpupin">
+ <attribute name="vcpu">
+ <ref name="vcpuid"/>
+ </attribute>
+ <attribute name="cpuset">
+ <ref name="cpuset"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="emulatorpin">
+ <attribute name="cpuset">
+ <ref name="cpuset"/>
+ </attribute>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="iothreadpin">
+ <attribute name="iothread">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="cpuset">
+ <ref name="cpuset"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="vcpusched">
+ <optional>
+ <attribute name="vcpus">
+ <ref name="cpuset"/>
+ </attribute>
+ </optional>
+ <ref name="schedparam"/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="iothreadsched">
+ <optional>
+ <attribute name="iothreads">
+ <ref name="cpuset"/>
+ </attribute>
+ </optional>
+ <ref name="schedparam"/>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="emulatorsched">
+ <ref name="schedparam"/>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="cachetune">
+ <attribute name="vcpus">
+ <ref name="cpuset"/>
+ </attribute>
+ <oneOrMore>
+ <choice>
+ <element name="cache">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="level">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="type">
+ <choice>
+ <value>both</value>
+ <value>code</value>
+ <value>data</value>
+ </choice>
+ </attribute>
+ <attribute name="size">
+ <ref name="unsignedLong"/>
+ </attribute>
+ <optional>
+ <attribute name="unit">
+ <ref name="unit"/>
+ </attribute>
+ </optional>
+ </element>
+ <element name="monitor">
+ <attribute name="level">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="vcpus">
+ <ref name="cpuset"/>
+ </attribute>
+ </element>
+ </choice>
+ </oneOrMore>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="memorytune">
+ <attribute name="vcpus">
+ <ref name="cpuset"/>
+ </attribute>
+ <oneOrMore>
+ <choice>
+ <element name="node">
+ <attribute name="id">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="bandwidth">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ <element name="monitor">
+ <attribute name="vcpus">
+ <ref name="cpuset"/>
+ </attribute>
+ </element>
+ </choice>
+ </oneOrMore>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="schedparam">
+ <choice>
+ <group>
+ <attribute name="scheduler">
+ <choice>
+ <value>batch</value>
+ <value>idle</value>
+ </choice>
+ </attribute>
+ </group>
+ <group>
+ <attribute name="scheduler">
+ <choice>
+ <value>fifo</value>
+ <value>rr</value>
+ </choice>
+ </attribute>
+ <attribute name="priority">
+ <ref name="unsignedShort"/>
+ </attribute>
+ </group>
+ </choice>
+ </define>
+
+ <!-- All the NUMA related tunables would go in the numatune -->
+ <define name="numatune">
+ <element name="numatune">
+ <interleave>
+ <optional>
+ <element name="memory">
+ <optional>
+ <attribute name="mode">
+ <choice>
+ <value>strict</value>
+ <value>preferred</value>
+ <value>interleave</value>
+ </choice>
+ </attribute>
+ </optional>
+ <choice>
+ <group>
+ <optional>
+ <attribute name="placement">
+ <value>static</value>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="nodeset">
+ <ref name="cpuset"/>
+ </attribute>
+ </optional>
+ </group>
+ <attribute name="placement">
+ <value>auto</value>
+ </attribute>
+ </choice>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="memnode">
+ <attribute name="cellid">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="mode">
+ <choice>
+ <value>strict</value>
+ <value>preferred</value>
+ <value>interleave</value>
+ </choice>
+ </attribute>
+ <attribute name="nodeset">
+ <ref name="cpuset"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="respartition">
+ <element name="resource">
+ <element name="partition">
+ <ref name="absFilePath"/>
+ </element>
+ </element>
+ </define>
+
+ <define name="clock">
+ <optional>
+ <element name="clock">
+ <choice>
+ <group>
+ <attribute name="offset">
+ <choice>
+ <value>localtime</value>
+ <value>utc</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="adjustment">
+ <choice>
+ <ref name="timeDelta"/>
+ <value>reset</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="offset">
+ <value>timezone</value>
+ </attribute>
+ <optional>
+ <attribute name="timezone">
+ <ref name="timeZone"/>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="offset">
+ <value>variable</value>
+ </attribute>
+ <optional>
+ <attribute name="adjustment">
+ <ref name="timeDelta"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="basis">
+ <choice>
+ <value>utc</value>
+ <value>localtime</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ <zeroOrMore>
+ <ref name="timer"/>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </define>
+ <define name="timer">
+ <element name="timer">
+ <choice>
+ <group>
+ <attribute name="name">
+ <choice>
+ <value>platform</value>
+ <value>rtc</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="track">
+ <choice>
+ <value>boot</value>
+ <value>guest</value>
+ <value>wall</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="tickpolicy"/>
+ </optional>
+ </group>
+ <group>
+ <attribute name="name">
+ <value>tsc</value>
+ </attribute>
+ <optional>
+ <ref name="tickpolicy"/>
+ </optional>
+ <optional>
+ <attribute name="frequency">
+ <ref name="unsignedLong"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="mode">
+ <choice>
+ <value>auto</value>
+ <value>native</value>
+ <value>emulate</value>
+ <value>paravirt</value>
+ <value>smpsafe</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="name">
+ <choice>
+ <value>hpet</value>
+ <value>pit</value>
+ <value>armvtimer</value>
+ </choice>
+ </attribute>
+ <optional>
+ <ref name="tickpolicy"/>
+ </optional>
+ </group>
+ <group>
+ <attribute name="name">
+ <choice>
+ <value>kvmclock</value>
+ <value>hypervclock</value>
+ </choice>
+ </attribute>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="present">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="tickpolicy">
+ <choice>
+ <group>
+ <attribute name="tickpolicy">
+ <choice>
+ <value>delay</value>
+ <value>merge</value>
+ <value>discard</value>
+ </choice>
+ </attribute>
+ </group>
+ <group>
+ <attribute name="tickpolicy">
+ <value>catchup</value>
+ </attribute>
+ <optional>
+ <element name="catchup">
+ <optional>
+ <attribute name="threshold">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="slew">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="limit">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </group>
+ </choice>
+ </define>
+<!--
+ A bootloader may be used to extract the OS information instead of
+ defining the OS parameter in the instance. It points just to the
+ binary or script used to extract the data from the first disk device.
+ -->
+ <define name="bootloader">
+ <interleave>
+ <optional>
+ <element name="bootloader">
+ <choice>
+ <text/>
+ <empty/>
+ </choice>
+ </element>
+ </optional>
+ <optional>
+ <element name="bootloader_args">
+ <text/>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+ <define name="osbootkernel">
+ <interleave>
+ <optional>
+ <element name="kernel">
+ <ref name="absFilePath"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="initrd">
+ <ref name="absFilePath"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="root">
+ <ref name="absFilePath"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="cmdline">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="dtb">
+ <ref name="absFilePath"/>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+ <define name="osbootdev">
+ <element name="boot">
+ <attribute name="dev">
+ <choice>
+ <value>hd</value>
+ <value>fd</value>
+ <value>cdrom</value>
+ <value>network</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+ <define name="diskspec">
+ <interleave>
+ <optional>
+ <ref name="diskDriver"/>
+ </optional>
+ <optional>
+ <ref name="diskMirror"/>
+ </optional>
+ <optional>
+ <ref name="diskAuth"/>
+ </optional>
+ <ref name="target"/>
+ <optional>
+ <ref name="deviceBoot"/>
+ </optional>
+ <optional>
+ <element name="backenddomain">
+ <attribute name="name">
+ <ref name="objectNameWithSlash"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="readonly">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="shareable">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="transient">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="serial">
+ <ref name="diskSerial"/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <optional>
+ <ref name="diskIoTune"/>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <optional>
+ <ref name="geometry"/>
+ </optional>
+ <optional>
+ <ref name="diskBlockIo"/>
+ </optional>
+ <optional>
+ <element name="wwn">
+ <ref name="wwn"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="vendor">
+ <data type="string">
+ <!-- All printable characters -->
+ <param name="pattern">[&#x20;-&#x7E;]{0,8}</param>
+ </data>
+ </element>
+ </optional>
+ <optional>
+ <element name="product">
+ <data type="string">
+ <!-- All printable characters -->
+ <param name="pattern">[&#x20;-&#x7E;]{0,16}</param>
+ </data>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+ <define name="snapshot">
+ <attribute name="snapshot">
+ <choice>
+ <value>no</value>
+ <value>internal</value>
+ <value>external</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="lease">
+ <element name="lease">
+ <interleave>
+ <element name="lockspace">
+ <text/>
+ </element>
+ <element name="key">
+ <text/>
+ </element>
+ <element name="target">
+ <attribute name="path">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="offset">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="startupPolicy">
+ <attribute name="startupPolicy">
+ <choice>
+ <value>mandatory</value>
+ <value>requisite</value>
+ <value>optional</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <!--
+ A disk description can be either of type file or block
+ The name of the attribute on the source element depends on the type
+
+ -->
+ <define name="disk">
+ <element name="disk">
+ <choice>
+ <group>
+ <optional>
+ <attribute name="device">
+ <choice>
+ <value>floppy</value>
+ <value>disk</value>
+ <value>cdrom</value>
+ </choice>
+ </attribute>
+ </optional>
+ <interleave>
+ <ref name="diskSource"/>
+ <ref name="diskSpecsExtra"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="device">
+ <value>lun</value>
+ </attribute>
+ <optional>
+ <ref name="rawIO"/>
+ </optional>
+ <optional>
+ <ref name="sgIO"/>
+ </optional>
+ <interleave>
+ <choice>
+ <ref name="diskSourceNetwork"/>
+ <ref name="diskSourceBlock"/>
+ <ref name="diskSourceVolume"/>
+ </choice>
+ <ref name="diskSpecsExtra"/>
+ </interleave>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>virtio</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="snapshot"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="diskSpecsExtra">
+ <interleave>
+ <ref name="storageSourceExtra"/>
+ <ref name="diskBackingChain"/>
+ </interleave>
+ </define>
+
+ <define name="diskBackingChain">
+ <choice>
+ <ref name="diskBackingStore"/>
+ <ref name="diskBackingStoreLast"/>
+ </choice>
+ </define>
+
+ <define name="diskBackingStore">
+ <element name="backingStore">
+ <optional>
+ <attribute name="index">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <ref name="diskSource"/>
+ <ref name="diskBackingChain"/>
+ <ref name="diskFormat"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskFormat">
+ <element name="format">
+ <attribute name="type">
+ <ref name="storageFormat"/>
+ </attribute>
+ <optional>
+ <element name="metadata_cache">
+ <optional>
+ <element name="max_size">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="diskBackingStoreLast">
+ <optional>
+ <element name="backingStore">
+ <empty/>
+ </element>
+ </optional>
+ </define>
+
+ <define name="diskSourceSlice">
+ <attribute name="offset">
+ <ref name="positiveInteger"/>
+ </attribute>
+ <attribute name="size">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </define>
+
+ <define name="diskSourceCommon">
+ <optional>
+ <attribute name="index">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="slices">
+ <element name="slice">
+ <attribute name="type">
+ <value>storage</value>
+ </attribute>
+ <ref name="diskSourceSlice"/>
+ </element>
+ </element>
+ </optional>
+ </define>
+
+ <define name="diskSource">
+ <choice>
+ <ref name="diskSourceFile"/>
+ <ref name="diskSourceBlock"/>
+ <ref name="diskSourceDir"/>
+ <ref name="diskSourceNetwork"/>
+ <ref name="diskSourceVolume"/>
+ <ref name="diskSourceNvme"/>
+ </choice>
+ </define>
+
+ <define name="diskSourceFile">
+ <optional>
+ <attribute name="type">
+ <value>file</value>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="source">
+ <interleave>
+ <optional>
+ <attribute name="file">
+ <choice>
+ <ref name="absFilePath"/>
+ <ref name="vmwarePath"/>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <ref name="storageStartupPolicy"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="diskSourceBlock">
+ <attribute name="type">
+ <value>block</value>
+ </attribute>
+ <optional>
+ <element name="source">
+ <interleave>
+ <optional>
+ <attribute name="dev">
+ <choice>
+ <ref name="absFilePath"/>
+ <ref name="deviceName"/>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <ref name="storageStartupPolicy"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <optional>
+ <ref name="reservations"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="diskSourceDir">
+ <attribute name="type">
+ <value>dir</value>
+ </attribute>
+ <optional>
+ <element name="source">
+ <interleave>
+ <attribute name="dir">
+ <ref name="absFilePath"/>
+ </attribute>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <ref name="storageStartupPolicy"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <empty/>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="diskSourceNetworkHost">
+ <element name="host">
+ <choice>
+ <group>
+ <optional>
+ <attribute name="transport">
+ <choice>
+ <value>tcp</value>
+ <value>rdma</value>
+ </choice>
+ </attribute>
+ </optional>
+ <attribute name="name">
+ <choice>
+ <ref name="dnsName"/>
+ <ref name="ipAddr"/>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="port">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="transport">
+ <value>unix</value>
+ </attribute>
+ <attribute name="socket">
+ <ref name="absFilePath"/>
+ </attribute>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkNFS">
+ <element name="identity">
+ <optional>
+ <attribute name="user">
+ <ref name="genericName"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="group">
+ <ref name="genericName"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolRBD">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <value>rbd</value>
+ </attribute>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <attribute name="name"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="diskSourceNetworkHost"/>
+ </zeroOrMore>
+ <optional>
+ <element name="snapshot">
+ <attribute name="name">
+ <ref name="genericName"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="config">
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="diskAuth"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <empty/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolISCSI">
+ <element name="source">
+ <attribute name="protocol">
+ <value>iscsi</value>
+ </attribute>
+ <attribute name="name"/>
+ <interleave>
+ <ref name="diskSourceCommon"/>
+ <ref name="diskSourceNetworkHost"/>
+ <optional>
+ <ref name="diskAuth"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <optional>
+ <ref name="initiatorinfo"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolPropsCommon">
+ <optional>
+ <element name="readahead">
+ <attribute name="size">
+ <ref name="positiveInteger"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="timeout">
+ <attribute name="seconds">
+ <ref name="positiveInteger"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ </define>
+
+ <define name="diskSourceNetworkProtocolSSLVerify">
+ <element name="ssl">
+ <attribute name="verify">
+ <ref name="virYesNo"/>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolHTTPCookies">
+ <element name="cookies">
+ <oneOrMore>
+ <element name="cookie">
+ <attribute name="name">
+ <data type="string">
+ <param name="pattern">[!#$%&amp;'*+\-.0-9A-Z\^_`a-z|~]+</param>
+ </data>
+ </attribute>
+ <data type="string">
+ <param name="pattern">"?[!#$%&amp;'()*+\-./0-9:&gt;=&lt;?@A-Z\^_`\[\]a-z|~]+"?</param>
+ </data>
+ </element>
+ </oneOrMore>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolHTTPS">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <choice>
+ <value>https</value>
+ </choice>
+ </attribute>
+ <attribute name="name"/>
+ <optional>
+ <attribute name="query"/>
+ </optional>
+ <ref name="diskSourceCommon"/>
+ <ref name="diskSourceNetworkHost"/>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <optional>
+ <ref name="diskSourceNetworkProtocolSSLVerify"/>
+ </optional>
+ <optional>
+ <ref name="diskSourceNetworkProtocolHTTPCookies"/>
+ </optional>
+ <ref name="diskSourceNetworkProtocolPropsCommon"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolHTTP">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <choice>
+ <value>http</value>
+ </choice>
+ </attribute>
+ <attribute name="name"/>
+ <optional>
+ <attribute name="query"/>
+ </optional>
+ <ref name="diskSourceCommon"/>
+ <ref name="diskSourceNetworkHost"/>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <optional>
+ <ref name="diskSourceNetworkProtocolHTTPCookies"/>
+ </optional>
+ <ref name="diskSourceNetworkProtocolPropsCommon"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolFTPS">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <choice>
+ <value>ftps</value>
+ </choice>
+ </attribute>
+ <attribute name="name"/>
+ <ref name="diskSourceCommon"/>
+ <ref name="diskSourceNetworkHost"/>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <optional>
+ <ref name="diskSourceNetworkProtocolSSLVerify"/>
+ </optional>
+ <ref name="diskSourceNetworkProtocolPropsCommon"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolSimple">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <choice>
+ <value>sheepdog</value>
+ <value>ftp</value>
+ <value>tftp</value>
+ </choice>
+ </attribute>
+ <attribute name="name"/>
+ <ref name="diskSourceCommon"/>
+ <ref name="diskSourceNetworkHost"/>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <ref name="diskSourceNetworkProtocolPropsCommon"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolNBD">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <value>nbd</value>
+ </attribute>
+ <optional>
+ <attribute name="name"/>
+ </optional>
+ <optional>
+ <attribute name="tls">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <ref name="diskSourceCommon"/>
+ <ref name="diskSourceNetworkHost"/>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolGluster">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <value>gluster</value>
+ </attribute>
+ <attribute name="name"/>
+ <ref name="diskSourceCommon"/>
+ <oneOrMore>
+ <ref name="diskSourceNetworkHost"/>
+ </oneOrMore>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolVxHS">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <choice>
+ <value>vxhs</value>
+ </choice>
+ </attribute>
+ <attribute name="name"/>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <attribute name="tls">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <ref name="diskSourceNetworkHost"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetworkProtocolNFS">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <choice>
+ <value>nfs</value>
+ </choice>
+ </attribute>
+ <attribute name="name"/>
+ <ref name="diskSourceCommon"/>
+ <ref name="diskSourceNetworkHost"/>
+ <ref name="diskSourceNetworkNFS"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="diskSourceNetwork">
+ <attribute name="type">
+ <value>network</value>
+ </attribute>
+ <choice>
+ <ref name="diskSourceNetworkProtocolNBD"/>
+ <ref name="diskSourceNetworkProtocolGluster"/>
+ <ref name="diskSourceNetworkProtocolRBD"/>
+ <ref name="diskSourceNetworkProtocolISCSI"/>
+ <ref name="diskSourceNetworkProtocolHTTP"/>
+ <ref name="diskSourceNetworkProtocolHTTPS"/>
+ <ref name="diskSourceNetworkProtocolFTPS"/>
+ <ref name="diskSourceNetworkProtocolSimple"/>
+ <ref name="diskSourceNetworkProtocolVxHS"/>
+ <ref name="diskSourceNetworkProtocolNFS"/>
+ </choice>
+ </define>
+
+ <define name="diskSourceVolume">
+ <attribute name="type">
+ <value>volume</value>
+ </attribute>
+ <optional>
+ <element name="source">
+ <interleave>
+ <attribute name="pool">
+ <ref name="objectName"/>
+ </attribute>
+ <attribute name="volume">
+ <ref name="volName"/>
+ </attribute>
+ <optional>
+ <attribute name="mode">
+ <choice>
+ <value>host</value>
+ <value>direct</value>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <ref name="storageStartupPolicy"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="diskSourceNvme">
+ <attribute name="type">
+ <value>nvme</value>
+ </attribute>
+ <optional>
+ <element name="source">
+ <interleave>
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+ <attribute name="namespace">
+ <ref name="uint32"/>
+ </attribute>
+ <optional>
+ <attribute name="managed">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <element name="address">
+ <ref name="pciaddress"/>
+ </element>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <ref name="storageStartupPolicy"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="diskTarget">
+ <data type="string">
+ <param name="pattern">(ioemu:)?(fd|hd|sd|vd|xvd|ubd)[a-zA-Z0-9_]+</param>
+ </data>
+ </define>
+ <define name="target">
+ <element name="target">
+ <attribute name="dev">
+ <ref name="diskTarget"/>
+ </attribute>
+ <optional>
+ <attribute name="bus">
+ <choice>
+ <value>ide</value>
+ <value>fdc</value>
+ <value>scsi</value>
+ <value>virtio</value>
+ <value>xen</value>
+ <value>usb</value>
+ <value>uml</value> <!-- NOT USED ANYMORE -->
+ <value>sata</value>
+ <value>sd</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="tray">
+ <choice>
+ <value>open</value>
+ <value>closed</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="removable">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+ <define name="geometry">
+ <element name="geometry">
+ <attribute name="cyls">
+ <data type="integer"/>
+ </attribute>
+ <attribute name="heads">
+ <data type="integer"/>
+ </attribute>
+ <attribute name="secs">
+ <data type="integer"/>
+ </attribute>
+ <optional>
+ <attribute name="trans">
+ <choice>
+ <value>auto</value>
+ <value>none</value>
+ <value>lba</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+ <define name="diskBlockIo">
+ <element name="blockio">
+ <optional>
+ <attribute name="logical_block_size">
+ <data type="integer"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="physical_block_size">
+ <data type="integer"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+ <!--
+ Disk may use a special driver for access.
+ -->
+ <define name="diskDriver">
+ <element name="driver">
+ <optional>
+ <ref name="driverFormat"/>
+ </optional>
+ <optional>
+ <ref name="driverCache"/>
+ </optional>
+ <optional>
+ <ref name="driverErrorPolicy"/>
+ </optional>
+ <optional>
+ <ref name="driverRerrorPolicy"/>
+ </optional>
+ <optional>
+ <ref name="driverIO"/>
+ </optional>
+ <optional>
+ <ref name="ioeventfd"/>
+ </optional>
+ <optional>
+ <ref name="event_idx"/>
+ </optional>
+ <optional>
+ <ref name="copy_on_read"/>
+ </optional>
+ <optional>
+ <ref name="discard"/>
+ </optional>
+ <optional>
+ <ref name="driverIOThread"/>
+ </optional>
+ <optional>
+ <ref name="detect_zeroes"/>
+ </optional>
+ <optional>
+ <attribute name="queues">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <ref name="virtioOptions"/>
+ <optional>
+ <element name="metadata_cache">
+ <optional>
+ <element name="max_size">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </define>
+ <define name="driverFormat">
+ <optional>
+ <attribute name="name">
+ <ref name="genericName"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="type">
+ <choice>
+ <ref name="storageFormat"/>
+ <value>aio</value> <!-- back-compat for "raw" -->
+ </choice>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="driverCache">
+ <attribute name="cache">
+ <choice>
+ <value>default</value>
+ <value>none</value>
+ <value>writeback</value>
+ <value>writethrough</value>
+ <value>directsync</value>
+ <value>unsafe</value>
+ </choice>
+ </attribute>
+ </define>
+ <define name="driverErrorPolicy">
+ <attribute name="error_policy">
+ <choice>
+ <value>stop</value>
+ <value>report</value>
+ <value>ignore</value>
+ <value>enospace</value>
+ </choice>
+ </attribute>
+ </define>
+ <define name="driverRerrorPolicy">
+ <attribute name="rerror_policy">
+ <choice>
+ <value>stop</value>
+ <value>report</value>
+ <value>ignore</value>
+ </choice>
+ </attribute>
+ </define>
+ <define name="driverIO">
+ <attribute name="io">
+ <choice>
+ <value>threads</value>
+ <value>native</value>
+ <value>io_uring</value>
+ </choice>
+ </attribute>
+ </define>
+ <define name="ioeventfd">
+ <attribute name="ioeventfd">
+ <ref name="virOnOff"/>
+ </attribute>
+ </define>
+ <define name="event_idx">
+ <attribute name="event_idx">
+ <ref name="virOnOff"/>
+ </attribute>
+ </define>
+ <define name="copy_on_read">
+ <attribute name="copy_on_read">
+ <ref name="virOnOff"/>
+ </attribute>
+ </define>
+ <define name="discard">
+ <attribute name="discard">
+ <choice>
+ <value>unmap</value>
+ <value>ignore</value>
+ </choice>
+ </attribute>
+ </define>
+ <define name="driverIOThread">
+ <attribute name="iothread">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </define>
+ <define name="detect_zeroes">
+ <attribute name="detect_zeroes">
+ <choice>
+ <value>off</value>
+ <value>on</value>
+ <value>unmap</value>
+ </choice>
+ </attribute>
+ </define>
+ <define name="controller">
+ <element name="controller">
+ <optional>
+ <attribute name="index">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <choice>
+ <!-- fdc/sata/ccid have only the common attributes -->
+ <group>
+ <attribute name="type">
+ <choice>
+ <value>fdc</value>
+ <value>sata</value>
+ <value>ccid</value>
+ </choice>
+ </attribute>
+ </group>
+ <!-- scsi has an optional attribute "model" -->
+ <group>
+ <attribute name="type">
+ <value>scsi</value>
+ </attribute>
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>auto</value>
+ <value>buslogic</value>
+ <value>lsilogic</value>
+ <value>lsisas1068</value>
+ <value>vmpvscsi</value>
+ <value>ibmvscsi</value>
+ <value>virtio-scsi</value>
+ <value>lsisas1078</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ <value>ncr53c90</value>
+ <value>dc390</value>
+ <value>am53c974</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ <!-- usb has an optional attribute "model",
+ and optional subelements "master" and "ports" -->
+ <group>
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>piix3-uhci</value>
+ <value>piix4-uhci</value>
+ <value>ehci</value>
+ <value>ich9-ehci1</value>
+ <value>ich9-uhci1</value>
+ <value>ich9-uhci2</value>
+ <value>ich9-uhci3</value>
+ <value>vt82c686b-uhci</value>
+ <value>pci-ohci</value>
+ <value>nec-xhci</value>
+ <value>none</value>
+ <value>qusb1</value>
+ <value>qusb2</value>
+ <value>qemu-xhci</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="usbmaster"/>
+ </optional>
+ <optional>
+ <attribute name="ports">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </group>
+ <!-- ide has an optional attribute "model" -->
+ <group>
+ <attribute name="type">
+ <value>ide</value>
+ </attribute>
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>piix3</value>
+ <value>piix4</value>
+ <value>ich6</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ <!-- isa -->
+ <group>
+ <attribute name="type">
+ <value>isa</value>
+ </attribute>
+ </group>
+ <!-- pci has an optional attribute "model" -->
+ <group>
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+ <optional>
+ <element name="model">
+ <attribute name="name">
+ <choice>
+ <!-- implementations of "pci-root" -->
+ <value>spapr-pci-host-bridge</value>
+ <!-- implementations of "pci-bridge" -->
+ <value>pci-bridge</value>
+ <!-- implementations of "dmi-to-pci-bridge" -->
+ <value>i82801b11-bridge</value>
+ <!-- implementations of "pcie-to-pci-bridge" -->
+ <value>pcie-pci-bridge</value>
+ <!-- implementations of "pcie-root-port" -->
+ <value>ioh3420</value>
+ <value>pcie-root-port</value>
+ <!-- implementations of "pcie-switch-upstream-port" -->
+ <value>x3130-upstream</value>
+ <!-- implementations of "pcie-switch-downstream-port" -->
+ <value>xio3130-downstream</value>
+ <!-- implementations of "pci-expander-bus" -->
+ <value>pxb</value>
+ <!-- implementations of "pcie-expander-bus" -->
+ <value>pxb-pcie</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="target">
+ <optional>
+ <attribute name="chassisNr">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="chassis">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="port">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="busNr">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="index">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="hotplug">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="node">
+ <ref name="unsignedInt"/>
+ </element>
+ </optional>
+ </element>
+ </optional>
+ <!-- *-root controllers have an optional element "pcihole64"-->
+ <choice>
+ <group>
+ <attribute name="model">
+ <choice>
+ <value>pci-root</value>
+ <value>pcie-root</value>
+ </choice>
+ </attribute>
+ <optional>
+ <element name="pcihole64">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </group>
+ <group>
+ <attribute name="model">
+ <choice>
+ <value>pci-bridge</value>
+ <value>dmi-to-pci-bridge</value>
+ <value>pcie-to-pci-bridge</value>
+ <value>pcie-root-port</value>
+ <value>pcie-switch-upstream-port</value>
+ <value>pcie-switch-downstream-port</value>
+ <value>pci-expander-bus</value>
+ <value>pcie-expander-bus</value>
+ </choice>
+ </attribute>
+ </group>
+ </choice>
+ </group>
+ <!-- virtio-serial has optional "ports" and "vectors" -->
+ <group>
+ <attribute name="type">
+ <value>virtio-serial</value>
+ </attribute>
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>virtio</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ports">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="vectors">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </group>
+ <!-- xenbus has an optional attribute "maxGrantFrames" -->
+ <group>
+ <attribute name="type">
+ <value>xenbus</value>
+ </attribute>
+ <optional>
+ <attribute name="maxGrantFrames">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="maxEventChannels">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ <optional>
+ <element name="driver">
+ <optional>
+ <attribute name="queues">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="cmd_per_lun">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="max_sectors">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="ioeventfd"/>
+ </optional>
+ <optional>
+ <ref name="driverIOThread"/>
+ </optional>
+ <ref name="virtioOptions"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="filesystem">
+ <element name="filesystem">
+ <interleave>
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>file</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <ref name="fsDriver"/>
+ </optional>
+ <element name="source">
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>block</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <ref name="fsDriver"/>
+ </optional>
+ <element name="source">
+ <attribute name="dev">
+ <ref name="absFilePath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </interleave>
+ </group>
+ <group>
+ <!-- type="mount" is default -->
+ <optional>
+ <attribute name="type">
+ <value>mount</value>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <ref name="fsDriver"/>
+ </optional>
+ <optional>
+ <ref name="fsBinary"/>
+ </optional>
+ <element name="source">
+ <attribute name="dir">
+ <ref name="absDirPath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </interleave>
+ </group>
+ <group>
+ <optional>
+ <attribute name="type">
+ <value>bind</value>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <ref name="fsDriver"/>
+ </optional>
+ <element name="source">
+ <attribute name="dir">
+ <ref name="absDirPath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>template</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <ref name="fsDriver"/>
+ </optional>
+ <element name="source">
+ <attribute name="name">
+ <ref name="genericName"/>
+ </attribute>
+ <empty/>
+ </element>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>ram</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <ref name="fsDriver"/>
+ </optional>
+ <element name="source">
+ <attribute name="usage">
+ <ref name="unsignedLong"/>
+ </attribute>
+ <optional>
+ <attribute name="units">
+ <ref name="unit"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </interleave>
+ </group>
+ </choice>
+ <interleave>
+ <element name="target">
+ <attribute name="dir"/>
+ <empty/>
+ </element>
+ <optional>
+ <attribute name="accessmode">
+ <choice>
+ <value>passthrough</value>
+ <value>mapped</value>
+ <value>squash</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="multidevs">
+ <choice>
+ <value>default</value>
+ <value>remap</value>
+ <value>forbid</value>
+ <value>warn</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="fmode">
+ <ref name="createMode"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dmode">
+ <ref name="createMode"/>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="readonly">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
+ <interleave>
+ <optional>
+ <element name="space_hard_limit">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="space_soft_limit">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </interleave>
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>virtio</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ </choice>
+ </attribute>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="fsDriver">
+ <element name="driver">
+ <!-- Annoying inconsistency. "disk" uses "name"
+ for this kind of info, and "type" for the
+ storage format. We need the latter too, so
+ had to invent a new attribute name -->
+ <choice>
+ <group>
+ <optional>
+ <attribute name="type">
+ <choice>
+ <value>path</value>
+ <value>handle</value>
+ <value>loop</value>
+ <value>nbd</value>
+ <value>ploop</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="format">
+ <ref name="storageFormat"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="wrpolicy">
+ <value>immediate</value>
+ </attribute>
+ </optional>
+ <ref name="virtioOptions"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>virtiofs</value>
+ </attribute>
+ <optional>
+ <attribute name="queue">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <ref name="virtioOptions"/>
+ </group>
+ <empty/>
+ </choice>
+ </element>
+ </define>
+ <define name="fsBinary">
+ <element name="binary">
+ <optional>
+ <attribute name="path">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="xattr">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="cache">
+ <optional>
+ <attribute name="mode">
+ <choice>
+ <value>none</value>
+ <value>always</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="lock">
+ <optional>
+ <attribute name="posix">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="flock">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="interface-network-attributes">
+ <attribute name="network">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="portgroup">
+ <ref name="deviceName"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="portid">
+ <ref name="UUID"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="interface-bridge-attributes">
+ <attribute name="bridge">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\.\-\\:/ ]*</param>
+ </data>
+ </attribute>
+ <optional>
+ <attribute name="macTableManager">
+ <ref name="macTableManager"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <!--
+ An interface description can either be of type bridge in which case
+ it will use a bridging source, or of type ethernet which uses a device
+ source and a device target instead. They both share a set of interface
+ options. FIXME
+ -->
+ <define name="interface">
+ <element name="interface">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>bridge</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="source">
+ <ref name="interface-bridge-attributes"/>
+ <optional>
+ <ref name="interface-network-attributes"/>
+ </optional>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>ethernet</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="source">
+ <ref name="interface-ip-info"/>
+ </element>
+ </optional>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>vhostuser</value>
+ </attribute>
+ <interleave>
+ <ref name="unixSocketSource"/>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>network</value>
+ </attribute>
+ <interleave>
+ <element name="source">
+ <ref name="interface-network-attributes"/>
+ <optional>
+ <ref name="interface-bridge-attributes"/>
+ </optional>
+ <empty/>
+ </element>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>direct</value>
+ </attribute>
+ <interleave>
+ <element name="source">
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ <optional>
+ <attribute name="mode">
+ <ref name="bridgeMode"/>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="interface-network-attributes"/>
+ </optional>
+ <empty/>
+ </element>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>user</value>
+ </attribute>
+ <interleave>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>internal</value>
+ </attribute>
+ <interleave>
+ <element name="source">
+ <attribute name="name">
+ <ref name="deviceName"/>
+ </attribute>
+ <empty/>
+ </element>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <choice>
+ <value>mcast</value>
+ <value>client</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <element name="source">
+ <attribute name="address">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ <empty/>
+ </element>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>udp</value>
+ </attribute>
+ <interleave>
+ <element name="source">
+ <attribute name="address">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ <element name="local">
+ <attribute name="address">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ <empty/>
+ </element>
+ </element>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>server</value>
+ </attribute>
+ <interleave>
+ <element name="source">
+ <optional>
+ <attribute name="address">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ </optional>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ <empty/>
+ </element>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>hostdev</value>
+ </attribute>
+ <optional>
+ <attribute name="managed">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <element name="source">
+ <optional>
+ <attribute name="missing">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <choice>
+ <group>
+ <ref name="usbproduct"/>
+ <optional>
+ <ref name="usbaddress"/>
+ </optional>
+ </group>
+ <element name="address">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+ <ref name="pciaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+ <attribute name="bus">
+ <ref name="usbAddr"/>
+ </attribute>
+ <attribute name="device">
+ <ref name="usbAddr"/>
+ </attribute>
+ </group>
+ </choice>
+ </element>
+ </choice>
+ </element>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+
+ <group>
+ <attribute name="type">
+ <value>vdpa</value>
+ </attribute>
+ <interleave>
+ <element name="source">
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ </element>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
+
+ </choice>
+ <optional>
+ <attribute name="trustGuestRxFilters">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+ <!--
+ The interface options possible are:
+ - the MAC address
+ - the IP address bound to the interface
+ - the name of the script used to set up the binding
+ - the target device used
+ - boot order
+ - link state
+ -->
+ <define name="interface-options">
+ <interleave>
+ <optional>
+ <element name="link">
+ <attribute name="state">
+ <choice>
+ <value>up</value>
+ <value>down</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="mtu"/>
+ </optional>
+ <optional>
+ <ref name="coalesce"/>
+ </optional>
+ <optional>
+ <element name="target">
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ <optional>
+ <attribute name="managed">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="guest">
+ <interleave>
+ <optional>
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="actual">
+ <ref name="deviceName"/>
+ </attribute>
+ </optional>
+ </interleave>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="mac">
+ <attribute name="address">
+ <ref name="uniMacAddr"/>
+ </attribute>
+ <optional>
+ <attribute name="type">
+ <choice>
+ <value>generated</value>
+ <value>static</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="check">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </optional>
+ <ref name="interface-ip-info"/>
+ <optional>
+ <element name="script">
+ <attribute name="path">
+ <ref name="filePath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="downscript">
+ <attribute name="path">
+ <ref name="filePath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="backenddomain">
+ <attribute name="name">
+ <ref name="objectNameWithSlash"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="model">
+ <attribute name="type">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9\-_]+</param>
+ </data>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="backend">
+ <optional>
+ <attribute name="tap">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="vhost">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="driver">
+ <choice>
+ <group>
+ <attribute name="name">
+ <choice>
+ <value>kvm</value>
+ <value>vfio</value>
+ <value>xen</value>
+ </choice>
+ </attribute>
+ </group>
+ <group>
+ <optional>
+ <attribute name="name">
+ <choice>
+ <value>qemu</value>
+ <value>vhost</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="queues">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="rx_queue_size">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="tx_queue_size">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="txmode">
+ <choice>
+ <value>iothread</value>
+ <value>timer</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="ioeventfd"/>
+ </optional>
+ <optional>
+ <ref name="event_idx"/>
+ </optional>
+ </group>
+ </choice>
+ <ref name="virtioOptions"/>
+ <interleave>
+ <optional>
+ <element name="host">
+ <optional>
+ <attribute name="csum">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="gso">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="tso4">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="tso6">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ecn">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ufo">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="mrg_rxbuf">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="guest">
+ <optional>
+ <attribute name="csum">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="tso4">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="tso6">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ecn">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ufo">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <optional>
+ <element name="filterref">
+ <ref name="filterref-node-attributes"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="tune">
+ <optional>
+ <!-- size of send buffer for network tap devices -->
+ <element name="sndbuf">
+ <ref name="unsignedInt"/>
+ </element>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <ref name="deviceBoot"/>
+ </optional>
+ <optional>
+ <ref name="rom"/>
+ </optional>
+ <optional>
+ <ref name="bandwidth"/>
+ </optional>
+ <optional>
+ <ref name="vlan"/>
+ </optional>
+ <optional>
+ <ref name="portOptions"/>
+ </optional>
+ <optional>
+ <element name="teaming">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>persistent</value>
+ </attribute>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>transient</value>
+ </attribute>
+ <attribute name="persistent">
+ <ref name="aliasName"/>
+ </attribute>
+ </group>
+ </choice>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+ <!--
+ All ip-related info for either the host or guest side of an interface
+ -->
+ <define name="interface-ip-info">
+ <zeroOrMore>
+ <element name="ip">
+ <attribute name="address">
+ <ref name="ipAddr"/>
+ </attribute>
+ <optional>
+ <attribute name="family">
+ <ref name="addr-family"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="prefix">
+ <ref name="ipPrefix"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="peer">
+ <ref name="ipAddr"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <ref name="route"/>
+ </zeroOrMore>
+ </define>
+ <!--
+ An emulator description is just a path to the binary used for the task
+ -->
+ <define name="emulator">
+ <element name="emulator">
+ <ref name="absFilePath"/>
+ </element>
+ </define>
+ <!--
+ A graphic description (SPICE, VNC, SDL, ...)
+ -->
+ <define name="graphic">
+ <element name="graphics">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>sdl</value>
+ </attribute>
+ <optional>
+ <attribute name="display">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="xauth">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="fullscreen">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="gl">
+ <attribute name="enable">
+ <ref name="virYesNo"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>vnc</value>
+ </attribute>
+ <choice>
+ <group>
+ <optional>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="autoport">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="websocket">
+ <ref name="PortNumber"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="listen">
+ <ref name="addrIPorName"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="sharePolicy">
+ <choice>
+ <value>allow-exclusive</value>
+ <value>force-shared</value>
+ <value>ignore</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <optional>
+ <attribute name="socket">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="passwd">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="keymap">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="passwdValidTo">
+ <data type="dateTime"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="connected">
+ <value>keep</value>
+ </attribute>
+ </optional>
+ <ref name="listenElements"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>spice</value>
+ </attribute>
+ <optional>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="tlsPort">
+ <ref name="PortNumber"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="autoport">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="listen">
+ <ref name="addrIPorName"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="passwd">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="keymap">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="passwdValidTo">
+ <data type="dateTime"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="connected">
+ <choice>
+ <value>fail</value>
+ <value>disconnect</value>
+ <value>keep</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="defaultMode">
+ <choice>
+ <value>any</value>
+ <value>secure</value>
+ <value>insecure</value>
+ </choice>
+ </attribute>
+ </optional>
+ <interleave>
+ <ref name="listenElements"/>
+ <zeroOrMore>
+ <element name="channel">
+ <attribute name="name">
+ <choice>
+ <value>main</value>
+ <value>display</value>
+ <value>inputs</value>
+ <value>cursor</value>
+ <value>playback</value>
+ <value>record</value>
+ <value>smartcard</value>
+ <value>usbredir</value>
+ </choice>
+ </attribute>
+ <attribute name="mode">
+ <choice>
+ <value>any</value>
+ <value>secure</value>
+ <value>insecure</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="image">
+ <attribute name="compression">
+ <choice>
+ <value>auto_glz</value>
+ <value>auto_lz</value>
+ <value>quic</value>
+ <value>glz</value>
+ <value>lz</value>
+ <value>off</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="jpeg">
+ <attribute name="compression">
+ <choice>
+ <value>auto</value>
+ <value>never</value>
+ <value>always</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="zlib">
+ <attribute name="compression">
+ <choice>
+ <value>auto</value>
+ <value>never</value>
+ <value>always</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="playback">
+ <attribute name="compression">
+ <ref name="virOnOff"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="streaming">
+ <attribute name="mode">
+ <choice>
+ <value>filter</value>
+ <value>all</value>
+ <value>off</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="clipboard">
+ <attribute name="copypaste">
+ <ref name="virYesNo"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="mouse">
+ <attribute name="mode">
+ <choice>
+ <value>server</value>
+ <value>client</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="filetransfer">
+ <attribute name="enable">
+ <ref name="virYesNo"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="gl">
+ <attribute name="enable">
+ <ref name="virYesNo"/>
+ </attribute>
+ <optional>
+ <attribute name="rendernode">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>rdp</value>
+ </attribute>
+ <optional>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="autoport">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="replaceUser">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="multiUser">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="listen">
+ <ref name="addrIPorName"/>
+ </attribute>
+ </optional>
+ <ref name="listenElements"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>desktop</value>
+ </attribute>
+ <optional>
+ <attribute name="display">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="fullscreen">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>egl-headless</value>
+ </attribute>
+ <optional>
+ <element name="gl">
+ <optional>
+ <attribute name="rendernode">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="listenElements">
+ <zeroOrMore>
+ <element name="listen">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>address</value>
+ </attribute>
+ <optional>
+ <attribute name="address">
+ <ref name="addrIPorName"/>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>network</value>
+ </attribute>
+ <attribute name="network">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="address">
+ <ref name="addrIPorName"/>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>socket</value>
+ </attribute>
+ <optional>
+ <attribute name="socket">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>none</value>
+ </attribute>
+ </group>
+ </choice>
+ </element>
+ </zeroOrMore>
+ </define>
+ <!--
+ A video adapter description, allowing configuration of device
+ model, number of virtual heads, video ram size, and for qxl
+ both ram bar sizes.
+ -->
+ <define name="video">
+ <element name="video">
+ <optional>
+ <element name="driver">
+ <optional>
+ <ref name="virtioOptions"/>
+ </optional>
+ <optional>
+ <attribute name="name">
+ <choice>
+ <value>qemu</value>
+ <value>vhostuser</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="vgaconf">
+ <choice>
+ <value>io</value>
+ <value>on</value>
+ <value>off</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="model">
+ <choice>
+ <attribute name="type">
+ <choice>
+ <value>vga</value>
+ <value>cirrus</value>
+ <value>vmvga</value>
+ <value>xen</value>
+ <value>vbox</value>
+ <value>virtio</value>
+ <value>gop</value>
+ <value>none</value>
+ <value>bochs</value>
+ <value>ramfb</value>
+ </choice>
+ </attribute>
+ <group>
+ <attribute name="type">
+ <value>qxl</value>
+ </attribute>
+ <optional>
+ <attribute name="ram">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="vgamem">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="vram64">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="vram">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="heads">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="primary">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="acceleration">
+ <optional>
+ <attribute name="accel3d">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="accel2d">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="rendernode">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="resolution">
+ <attribute name="x">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <attribute name="y">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </element>
+ </define>
+ <!--
+ When a certain event happens, multiple policies can be applied
+ depends on what happened:
+ -->
+ <define name="events">
+ <interleave>
+ <optional>
+ <element name="on_reboot">
+ <ref name="offOptions"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="on_poweroff">
+ <ref name="offOptions"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="on_crash">
+ <ref name="crashOptions"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="on_lockfailure">
+ <ref name="lockfailureOptions"/>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+ <!--
+ Options when a domain terminates:
+ destroy: The domain is cleaned up
+ restart: A new domain is started in place of the old one
+ preserve: The domain will remain in memory until it is destroyed manually
+ rename-restart: a variant of the previous one but where the old domain is
+ renamed before being saved to allow a restart
+ -->
+ <define name="offOptions">
+ <choice>
+ <value>destroy</value>
+ <value>restart</value>
+ <value>preserve</value>
+ <value>rename-restart</value>
+ </choice>
+ </define>
+ <!--
+ Options when a domain crashes:
+ destroy: The domain is cleaned up
+ restart: A new domain is started in place of the old one
+ preserve: The domain will remain in memory until it is destroyed manually
+ rename-restart: a variant of the previous one but where the old domain is
+ renamed before being saved to allow a restart
+ coredump-destroy: The crashed domain's core will be dumped, and then the
+ domain will be terminated completely and all resources
+ released
+ coredump-restart: The crashed domain's core will be dumped, and then the
+ domain will be restarted with the same configuration
+ -->
+ <define name="crashOptions">
+ <choice>
+ <value>destroy</value>
+ <value>restart</value>
+ <value>preserve</value>
+ <value>rename-restart</value>
+ <value>coredump-destroy</value>
+ <value>coredump-restart</value>
+ </choice>
+ </define>
+ <!--
+ Options when resource locks are lost:
+ poweroff: power off the domain
+ restart: power off the domain and start it up again to reacquire the
+ locks
+ pause: pause the execution of the domain so that it can be manually
+ resumed when lock issues are solved
+ ignore: keep the domain running
+ -->
+ <define name="lockfailureOptions">
+ <choice>
+ <value>poweroff</value>
+ <value>restart</value>
+ <value>pause</value>
+ <value>ignore</value>
+ </choice>
+ </define>
+ <!--
+ Control ACPI sleep states (dis)allowed for the domain
+ For each of the states the following rules apply:
+ on: the state will be forcefully enabled
+ off: the state will be forcefully disabled
+ not specified: hypervisor will be left to decide its defaults
+ -->
+ <define name="pm">
+ <element name="pm">
+ <interleave>
+ <optional>
+ <element name="suspend-to-mem">
+ <ref name="suspendChoices"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="suspend-to-disk">
+ <ref name="suspendChoices"/>
+ </element>
+ </optional>
+ </interleave>
+ <empty/>
+ </element>
+ </define>
+ <define name="suspendChoices">
+ <optional>
+ <attribute name="enabled">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ </define>
+ <!--
+ Specific setup for a qemu emulated character device. Note: this
+ definition doesn't fully specify the constraints on this node.
+ -->
+ <define name="qemucdev">
+ <ref name="qemucdevSrcType"/>
+ <optional>
+ <attribute name="tty">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <ref name="qemucdevSrcDef"/>
+ <optional>
+ <ref name="qemucdevTgtDef"/>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="qemucdevConsoleTgtType">
+ <attribute name="type">
+ <choice>
+ <value>xen</value>
+ <value>serial</value>
+ <value>uml</value> <!-- NOT USED ANYMORE -->
+ <value>virtio</value>
+ <value>lxc</value>
+ <value>openvz</value>
+ <value>sclp</value>
+ <value>sclplm</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="qemucdevSerialTgtType">
+ <attribute name="type">
+ <choice>
+ <value>isa-serial</value>
+ <value>usb-serial</value>
+ <value>pci-serial</value>
+ <value>spapr-vio-serial</value>
+ <value>system-serial</value>
+ <value>sclp-serial</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="qemucdevSerialTgtModel">
+ <element name="model">
+ <attribute name="name">
+ <choice>
+ <value>isa-serial</value>
+ <value>usb-serial</value>
+ <value>pci-serial</value>
+ <value>spapr-vty</value>
+ <value>pl011</value>
+ <value>16550a</value>
+ <value>sclpconsole</value>
+ <value>sclplmconsole</value>
+ </choice>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="qemucdevTgtDef">
+ <element name="target">
+ <interleave>
+ <choice>
+ <optional>
+ <ref name="qemucdevConsoleTgtType"/>
+ </optional>
+ <optional>
+ <ref name="qemucdevSerialTgtType"/>
+ </optional>
+ </choice>
+ <optional>
+ <attribute name="port"/>
+ </optional>
+ <optional>
+ <ref name="qemucdevSerialTgtModel"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="qemucdevSrcTypeChoice">
+ <choice>
+ <value>dev</value>
+ <value>file</value>
+ <value>pipe</value>
+ <value>unix</value>
+ <value>tcp</value>
+ <value>udp</value>
+ <value>null</value>
+ <value>stdio</value>
+ <value>vc</value>
+ <value>pty</value>
+ <value>spicevmc</value>
+ <value>spiceport</value>
+ <value>nmdm</value>
+ </choice>
+ </define>
+
+ <define name="usbdevfilter">
+ <element name="usbdev">
+ <attribute name="allow">
+ <ref name="virYesNo"/>
+ </attribute>
+ <optional>
+ <attribute name="class">
+ <choice>
+ <ref name="usbClass"/>
+ <ref name="usbIdDefault"/>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="vendor">
+ <choice>
+ <ref name="usbId"/>
+ <ref name="usbIdDefault"/>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="product">
+ <choice>
+ <ref name="usbId"/>
+ <ref name="usbIdDefault"/>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="version">
+ <choice>
+ <ref name="usbVersion"/>
+ <ref name="usbIdDefault"/>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="qemucdevSrcType">
+ <attribute name="type">
+ <ref name="qemucdevSrcTypeChoice"/>
+ </attribute>
+ </define>
+ <define name="qemucdevSrcDef">
+ <zeroOrMore>
+ <element name="source">
+ <optional>
+ <attribute name="mode"/>
+ </optional>
+ <optional>
+ <attribute name="path"/>
+ </optional>
+ <optional>
+ <attribute name="host"/>
+ </optional>
+ <optional>
+ <attribute name="service"/>
+ </optional>
+ <optional>
+ <attribute name="channel"/>
+ </optional>
+ <optional>
+ <attribute name="master"/>
+ </optional>
+ <optional>
+ <attribute name="slave"/>
+ </optional>
+ <optional>
+ <attribute name="append">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="tls">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="reconnect"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="protocol">
+ <optional>
+ <attribute name="type">
+ <choice>
+ <value>raw</value>
+ <value>telnet</value>
+ <value>telnets</value>
+ <value>tls</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="log">
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ <optional>
+ <attribute name="append">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </define>
+ <!--
+ The description for a console
+ just a tty device
+ -->
+ <define name="console">
+ <element name="console">
+ <choice>
+ <group>
+ <optional>
+ <attribute name="tty">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </group>
+ <ref name="qemucdev"/>
+ </choice>
+ </element>
+ </define>
+ <define name="codec">
+ <element name="codec">
+ <attribute name="type">
+ <choice>
+ <value>duplex</value>
+ <value>micro</value>
+ <value>output</value>
+ </choice>
+ </attribute>
+ </element>
+ </define>
+ <define name="sound">
+ <element name="sound">
+ <attribute name="model">
+ <choice>
+ <value>sb16</value>
+ <value>es1370</value>
+ <value>pcspk</value>
+ <value>ac97</value>
+ <value>ich6</value>
+ <value>ich7</value>
+ <value>ich9</value>
+ <value>usb</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <optional>
+ <element name="audio">
+ <attribute name="id">
+ <ref name="uint8"/>
+ </attribute>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <ref name="codec"/>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+ <define name="audio">
+ <element name="audio">
+ <attribute name="id">
+ <ref name="uint8"/>
+ </attribute>
+ <attribute name="type">
+ <choice>
+ <value>oss</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="input">
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="output">
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="watchdog">
+ <element name="watchdog">
+ <attribute name="model">
+ <choice>
+ <value>i6300esb</value>
+ <value>ib700</value>
+ <value>diag288</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="action">
+ <choice>
+ <value>reset</value>
+ <value>shutdown</value>
+ <value>poweroff</value>
+ <value>pause</value>
+ <value>none</value>
+ <value>dump</value>
+ <value>inject-nmi</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </element>
+ </define>
+ <define name="nvram">
+ <element name="nvram">
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="shmem">
+ <element name="shmem">
+ <attribute name="name">
+ <data type="string">
+ <param name="pattern">[^/]*</param>
+ </data>
+ </attribute>
+ <optional>
+ <attribute name="role">
+ <choice>
+ <value>master</value>
+ <value>peer</value>
+ </choice>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="model">
+ <attribute name="type">
+ <choice>
+ <value>ivshmem</value>
+ <value>ivshmem-plain</value>
+ <value>ivshmem-doorbell</value>
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="size">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="server">
+ <optional>
+ <attribute name="path">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="msi">
+ <optional>
+ <ref name="ioeventfd"/>
+ </optional>
+ <optional>
+ <attribute name="vectors">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="memballoon">
+ <element name="memballoon">
+ <attribute name="model">
+ <choice>
+ <value>virtio</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ <value>xen</value>
+ <value>none</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="autodeflate">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="freePageReporting">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <optional>
+ <element name="stats">
+ <attribute name="period">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="driver">
+ <ref name="virtioOptions"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="parallel">
+ <element name="parallel">
+ <ref name="qemucdev"/>
+ </element>
+ </define>
+ <define name="serial">
+ <element name="serial">
+ <ref name="qemucdev"/>
+ </element>
+ </define>
+ <define name="guestfwdTarget">
+ <element name="target">
+ <attribute name="type">
+ <value>guestfwd</value>
+ </attribute>
+ <attribute name="address"/>
+ <attribute name="port"/>
+ </element>
+ </define>
+ <define name="virtioTarget">
+ <element name="target">
+ <attribute name="type">
+ <value>virtio</value>
+ </attribute>
+ <optional>
+ <attribute name="name"/>
+ </optional>
+ <optional>
+ <attribute name="state">
+ <choice>
+ <value>connected</value>
+ <value>disconnected</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+ <define name="xenTarget">
+ <element name="target">
+ <attribute name="type">
+ <value>xen</value>
+ </attribute>
+ <optional>
+ <attribute name="name"/>
+ </optional>
+ </element>
+ </define>
+ <define name="channel">
+ <element name="channel">
+ <ref name="qemucdevSrcType"/>
+ <interleave>
+ <ref name="qemucdevSrcDef"/>
+ <choice>
+ <ref name="guestfwdTarget"/>
+ <ref name="virtioTarget"/>
+ <ref name="xenTarget"/>
+ </choice>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="smartcard">
+ <element name="smartcard">
+ <choice>
+ <group>
+ <attribute name="mode">
+ <value>host</value>
+ </attribute>
+ <!-- might need to add optional database element here later -->
+ </group>
+ <group>
+ <attribute name="mode">
+ <value>host-certificates</value>
+ </attribute>
+ <ref name="certificate"/>
+ <ref name="certificate"/>
+ <ref name="certificate"/>
+ <optional>
+ <element name="database">
+ <ref name="absDirPath"/>
+ </element>
+ </optional>
+ </group>
+ <group>
+ <attribute name="mode">
+ <value>passthrough</value>
+ </attribute>
+ <ref name="qemucdevSrcType"/>
+ <interleave>
+ <ref name="qemucdevSrcDef"/>
+ <optional>
+ <ref name="qemucdevTgtDef"/>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </element>
+ </define>
+ <define name="certificate">
+ <element name="certificate">
+ <text/>
+ </element>
+ </define>
+
+ <define name="tpm">
+ <element name="tpm">
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>tpm-tis</value>
+ <value>tpm-crb</value>
+ <value>tpm-spapr</value>
+ <value>spapr-tpm-proxy</value>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="tpm-backend"/>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="tpm-backend">
+ <element name="backend">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>passthrough</value>
+ </attribute>
+ <ref name="tpm-passthrough-device"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>emulator</value>
+ </attribute>
+ <ref name="tpm-backend-emulator-encryption"/>
+ <optional>
+ <attribute name="persistent_state">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="version">
+ <choice>
+ <value>1.2</value>
+ <value>2.0</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="tpm-passthrough-device">
+ <optional>
+ <element name="device">
+ <optional>
+ <attribute name="path">
+ <ref name="filePath"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </define>
+
+ <define name="tpm-backend-emulator-encryption">
+ <optional>
+ <element name="encryption">
+ <attribute name="secret">
+ <ref name="UUID"/>
+ </attribute>
+ </element>
+ </optional>
+ </define>
+
+ <define name="vsock">
+ <element name="vsock">
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>virtio</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ </choice>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="cid">
+ <optional>
+ <attribute name="auto">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="address">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="iommu">
+ <element name="iommu">
+ <attribute name="model">
+ <choice>
+ <value>intel</value>
+ <value>smmuv3</value>
+ </choice>
+ </attribute>
+ <optional>
+ <element name="driver">
+ <optional>
+ <attribute name="intremap">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="caching_mode">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="eim">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="iotlb">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="aw_bits">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="input">
+ <element name="input">
+ <interleave>
+ <optional>
+ <element name="driver">
+ <ref name="virtioOptions"/>
+ </element>
+ </optional>
+ <choice>
+ <group>
+ <attribute name="type">
+ <choice>
+ <value>tablet</value>
+ <value>mouse</value>
+ <value>keyboard</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="bus">
+ <choice>
+ <value>ps2</value>
+ <value>usb</value>
+ <value>xen</value>
+ <value>virtio</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>passthrough</value>
+ </attribute>
+ <attribute name="bus">
+ <value>virtio</value>
+ </attribute>
+ <element name="source">
+ <attribute name="evdev">
+ <ref name="absFilePath"/>
+ </attribute>
+ </element>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>virtio</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="hub">
+ <element name="hub">
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="redirdev">
+ <element name="redirdev">
+ <attribute name="bus">
+ <value>usb</value>
+ </attribute>
+ <attribute name="type">
+ <ref name="qemucdevSrcTypeChoice"/>
+ </attribute>
+ <interleave>
+ <ref name="qemucdevSrcDef"/>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <optional>
+ <ref name="deviceBoot"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="redirfilter">
+ <element name="redirfilter">
+ <zeroOrMore>
+ <ref name="usbdevfilter"/>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="hostdev">
+ <element name="hostdev">
+ <interleave>
+ <choice>
+ <group>
+ <ref name="hostdevsubsys"/>
+ </group>
+ <group>
+ <ref name="hostdevcaps"/>
+ </group>
+ </choice>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="deviceBoot"/>
+ </optional>
+ <optional>
+ <ref name="rom"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <optional>
+ <element name="readonly">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="shareable">
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="hostdevsubsys">
+ <optional>
+ <attribute name="mode">
+ <value>subsystem</value>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="managed">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <choice>
+ <ref name="hostdevsubsyspci"/>
+ <ref name="hostdevsubsysusb"/>
+ <ref name="hostdevsubsysscsi"/>
+ <ref name="hostdevsubsyshost"/>
+ <ref name="hostdevsubsysmdev"/>
+ </choice>
+ </define>
+
+ <define name="hostdevcaps">
+ <attribute name="mode">
+ <value>capabilities</value>
+ </attribute>
+ <choice>
+ <group>
+ <ref name="hostdevcapsstorage"/>
+ </group>
+ <group>
+ <ref name="hostdevcapsmisc"/>
+ </group>
+ <group>
+ <ref name="hostdevcapsnet"/>
+ </group>
+ </choice>
+ </define>
+
+
+ <define name="hostdevsubsyspci">
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="driver">
+ <attribute name="name">
+ <choice>
+ <value>kvm</value>
+ <value>vfio</value>
+ <value>xen</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <element name="source">
+ <optional>
+ <ref name="startupPolicy"/>
+ </optional>
+ <optional>
+ <attribute name="writeFiltering">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <element name="address">
+ <ref name="pciaddress"/>
+ </element>
+ </element>
+ </interleave>
+ </define>
+
+ <define name="hostdevsubsysusb">
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+ <element name="source">
+ <optional>
+ <ref name="startupPolicy"/>
+ </optional>
+ <choice>
+ <group>
+ <ref name="usbproduct"/>
+ <optional>
+ <ref name="usbaddress"/>
+ </optional>
+ </group>
+ <ref name="usbaddress"/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="hostdevsubsysscsi">
+ <attribute name="type">
+ <value>scsi</value>
+ </attribute>
+ <optional>
+ <ref name="sgIO"/>
+ </optional>
+ <optional>
+ <ref name="rawIO"/>
+ </optional>
+ <element name="source">
+ <choice>
+ <group> <!-- scsi_host adapter -->
+ <optional>
+ <attribute name="protocol">
+ <value>adapter</value> <!-- scsi_host, default, optional -->
+ </attribute>
+ </optional>
+ <interleave>
+ <ref name="sourceinfoadapter"/>
+ <element name="address">
+ <ref name="scsiaddress"/>
+ </element>
+ </interleave>
+ </group>
+ <group> <!-- iscsi adapter -->
+ <attribute name="protocol">
+ <value>iscsi</value> <!-- iscsi, required -->
+ </attribute>
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <interleave>
+ <oneOrMore>
+ <element name="host">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </oneOrMore>
+ <optional>
+ <ref name="diskAuth"/>
+ </optional>
+ <optional>
+ <ref name="initiatorinfo"/>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="hostdevsubsyshost">
+ <attribute name="type">
+ <value>scsi_host</value>
+ </attribute>
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>virtio</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ </choice>
+ </attribute>
+ </optional>
+ <element name="source">
+ <choice>
+ <group>
+ <attribute name="protocol">
+ <value>vhost</value> <!-- vhost, required -->
+ </attribute>
+ <attribute name="wwpn">
+ <data type="string">
+ <param name="pattern">(naa\.)[0-9a-fA-F]{16}</param>
+ </data>
+ </attribute>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="hostdevsubsysmdev">
+ <attribute name="type">
+ <value>mdev</value>
+ </attribute>
+ <attribute name="model">
+ <choice>
+ <value>vfio-pci</value>
+ <value>vfio-ccw</value>
+ <value>vfio-ap</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="ramfb">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="display">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <element name="source">
+ <ref name="mdevaddress"/>
+ </element>
+ </define>
+
+ <define name="hostdevcapsstorage">
+ <attribute name="type">
+ <value>storage</value>
+ </attribute>
+ <element name="source">
+ <element name="block">
+ <ref name="absFilePath"/>
+ </element>
+ </element>
+ </define>
+
+ <define name="hostdevcapsmisc">
+ <attribute name="type">
+ <value>misc</value>
+ </attribute>
+ <element name="source">
+ <element name="char">
+ <ref name="absFilePath"/>
+ </element>
+ </element>
+ </define>
+
+ <define name="hostdevcapsnet">
+ <attribute name="type">
+ <value>net</value>
+ </attribute>
+ <interleave>
+ <element name="source">
+ <element name="interface">
+ <ref name="deviceName"/>
+ </element>
+ </element>
+ <ref name="interface-ip-info"/>
+ </interleave>
+ </define>
+
+ <define name="usbproduct">
+ <element name="vendor">
+ <attribute name="id">
+ <ref name="usbId"/>
+ </attribute>
+ </element>
+ <element name="product">
+ <attribute name="id">
+ <ref name="usbId"/>
+ </attribute>
+ </element>
+ </define>
+ <define name="usbaddress">
+ <element name="address">
+ <attribute name="bus">
+ <ref name="usbAddr"/>
+ </attribute>
+ <attribute name="device">
+ <ref name="usbAddr"/>
+ </attribute>
+ </element>
+ </define>
+ <define name="scsiaddress">
+ <attribute name="bus">
+ <ref name="driveBus"/>
+ </attribute>
+ <attribute name="target">
+ <ref name="driveSCSITarget"/>
+ </attribute>
+ <attribute name="unit">
+ <ref name="driveSCSIUnit"/>
+ </attribute>
+ </define>
+ <define name="usbportaddress">
+ <attribute name="bus">
+ <ref name="usbAddr"/>
+ </attribute>
+ <optional>
+ <attribute name="port">
+ <ref name="usbPort"/>
+ </attribute>
+ </optional>
+ </define>
+ <define name="spaprvioaddress">
+ <optional>
+ <attribute name="reg">
+ <ref name="spaprvioReg"/>
+ </attribute>
+ </optional>
+ </define>
+ <define name="ccwaddress">
+ <optional>
+ <attribute name="cssid">
+ <ref name="ccwCssidRange"/>
+ </attribute>
+ <attribute name="ssid">
+ <ref name="ccwSsidRange"/>
+ </attribute>
+ <attribute name="devno">
+ <ref name="ccwDevnoRange"/>
+ </attribute>
+ </optional>
+ </define>
+ <define name="driveaddress">
+ <optional>
+ <attribute name="controller">
+ <ref name="driveController"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="bus">
+ <ref name="driveBus"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="target">
+ <ref name="driveTarget"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="unit">
+ <ref name="driveUnit"/>
+ </attribute>
+ </optional>
+ </define>
+ <define name="virtioserialaddress">
+ <attribute name="controller">
+ <ref name="driveController"/>
+ </attribute>
+ <optional>
+ <attribute name="bus">
+ <ref name="driveBus"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="port">
+ <ref name="virtioserialPort"/>
+ </attribute>
+ </optional>
+ </define>
+ <define name="ccidaddress">
+ <attribute name="controller">
+ <ref name="driveController"/>
+ </attribute>
+ <optional>
+ <attribute name="slot">
+ <ref name="ccidSlot"/>
+ </attribute>
+ </optional>
+ </define>
+ <define name="dimmaddress">
+ <optional>
+ <attribute name="slot">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="base">
+ <ref name="hexuint"/>
+ </attribute>
+ </optional>
+ </define>
+ <define name="mdevaddress">
+ <element name="address">
+ <attribute name="uuid">
+ <ref name="UUID"/>
+ </attribute>
+ </element>
+ </define>
+ <define name="devices">
+ <element name="devices">
+ <interleave>
+ <optional>
+ <ref name="emulator"/>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="disk"/>
+ <ref name="controller"/>
+ <ref name="lease"/>
+ <ref name="filesystem"/>
+ <ref name="interface"/>
+ <ref name="input"/>
+ <ref name="sound"/>
+ <ref name="audio"/>
+ <ref name="hostdev"/>
+ <ref name="graphic"/>
+ <ref name="video"/>
+ <ref name="console"/>
+ <ref name="parallel"/>
+ <ref name="serial"/>
+ <ref name="channel"/>
+ <ref name="smartcard"/>
+ <ref name="hub"/>
+ <ref name="redirdev"/>
+ <ref name="redirfilter"/>
+ <ref name="rng"/>
+ <ref name="tpm"/>
+ <ref name="shmem"/>
+ <ref name="memorydev"/>
+ </choice>
+ </zeroOrMore>
+ <optional>
+ <ref name="watchdog"/>
+ </optional>
+ <optional>
+ <ref name="memballoon"/>
+ </optional>
+ <optional>
+ <ref name="nvram"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="panic"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="iommu"/>
+ </optional>
+ <optional>
+ <ref name="vsock"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <!--
+ A set of optional features: PAE, APIC, ACPI, GIC,
+ HyperV Enlightenment, KVM features, paravirtual spinlocks and HAP support
+ -->
+ <define name="features">
+ <optional>
+ <element name="features">
+ <interleave>
+ <optional>
+ <element name="pae">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="apic">
+ <optional>
+ <attribute name="eoi">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="acpi">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="hap">
+ <optional>
+ <attribute name="state">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <ref name="hyperv"/>
+ </optional>
+ <optional>
+ <element name="viridian">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="kvm"/>
+ </optional>
+ <optional>
+ <ref name="xen"/>
+ </optional>
+ <optional>
+ <element name="privnet">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="pvspinlock">
+ <optional>
+ <ref name="featurestate"/>
+ </optional>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="capabilities"/>
+ </optional>
+ <optional>
+ <ref name="pmu"/>
+ </optional>
+ <optional>
+ <element name="vmport">
+ <optional>
+ <attribute name="state">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="gic">
+ <optional>
+ <attribute name="version">
+ <choice>
+ <value>host</value>
+ <value>2</value>
+ <value>3</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="smm">
+ <optional>
+ <attribute name="state">
+ <ref name="virOnOff"/>
+ </attribute>
+ <optional>
+ <element name="tseg">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <ref name="ioapic"/>
+ </optional>
+ <optional>
+ <ref name="hpt"/>
+ </optional>
+ <optional>
+ <ref name="vmcoreinfo"/>
+ </optional>
+ <optional>
+ <element name="htm">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="nested-hv">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="msrs"/>
+ </optional>
+ <optional>
+ <element name="ccf-assist">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="cfpc"/>
+ </optional>
+ <optional>
+ <ref name="sbbc"/>
+ </optional>
+ <optional>
+ <ref name="ibs"/>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <!--
+ System information specification:
+ Placeholder for system specific information likes the ones
+ contained in the SMBIOS area.
+ Only a limited subset of entries can be modified there, so we
+ fully enumerate each case here.
+ The DMTF spec doesn't specify any string subset, just 0 terminated
+ byte strings, but better be safe and restrict at least the names
+ to avoid problems with space normalization in attribute values,
+ the value is kept as the element body for maximum flexibility.
+ A priori we allow only type 0 and type 1 string updates
+ -->
+ <define name="sysinfo">
+ <element name="sysinfo">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>smbios</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="bios">
+ <oneOrMore>
+ <element name="entry">
+ <attribute name="name">
+ <ref name="sysinfo-bios-name"/>
+ </attribute>
+ <ref name="sysinfo-value"/>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
+ <optional>
+ <element name="system">
+ <oneOrMore>
+ <element name="entry">
+ <attribute name="name">
+ <ref name="sysinfo-system-name"/>
+ </attribute>
+ <ref name="sysinfo-value"/>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="baseBoard">
+ <oneOrMore>
+ <element name="entry">
+ <attribute name="name">
+ <ref name="sysinfo-baseBoard-name"/>
+ </attribute>
+ <ref name="sysinfo-value"/>
+ </element>
+ </oneOrMore>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="chassis">
+ <oneOrMore>
+ <element name="entry">
+ <attribute name="name">
+ <ref name="sysinfo-chassis-name"/>
+ </attribute>
+ <ref name="sysinfo-value"/>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
+ <optional>
+ <element name="oemStrings">
+ <oneOrMore>
+ <element name="entry">
+ <ref name="sysinfo-value"/>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>fwcfg</value>
+ </attribute>
+ <zeroOrMore>
+ <element name="entry">
+ <attribute name="name">
+ <data type="string"/>
+ </attribute>
+ <choice>
+ <group>
+ <attribute name="file">
+ <data type="string"/>
+ </attribute>
+ <empty/>
+ </group>
+ <group>
+ <ref name="sysinfo-value"/>
+ </group>
+ </choice>
+ </element>
+ </zeroOrMore>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="sysinfo-bios-name">
+ <choice>
+ <value>vendor</value>
+ <value>version</value>
+ <value>date</value>
+ <value>release</value>
+ </choice>
+ </define>
+
+ <define name="sysinfo-system-name">
+ <choice>
+ <value>manufacturer</value>
+ <value>product</value>
+ <value>version</value>
+ <value>serial</value>
+ <value>uuid</value>
+ <value>sku</value>
+ <value>family</value>
+ </choice>
+ </define>
+
+ <define name="sysinfo-baseBoard-name">
+ <choice>
+ <value>manufacturer</value>
+ <value>product</value>
+ <value>version</value>
+ <value>serial</value>
+ <value>asset</value>
+ <value>location</value>
+ </choice>
+ </define>
+
+ <define name="sysinfo-chassis-name">
+ <choice>
+ <value>manufacturer</value>
+ <value>version</value>
+ <value>serial</value>
+ <value>asset</value>
+ <value>sku</value>
+ </choice>
+ </define>
+
+ <define name="sysinfo-value">
+ <data type="string"/>
+ </define>
+
+ <define name="acpiTable">
+ <element name="acpi">
+ <zeroOrMore>
+ <element name="table">
+ <attribute name="type">
+ <value>slic</value>
+ </attribute>
+ <ref name="absFilePath"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="smbios">
+ <element name="smbios">
+ <attribute name="mode">
+ <choice>
+ <value>emulate</value>
+ <value>host</value>
+ <value>sysinfo</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="bios">
+ <element name="bios">
+ <optional>
+ <attribute name="useserial">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="rebootTimeout">
+ <ref name="rebootTimeoutDelay"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="ioapic">
+ <element name="ioapic">
+ <attribute name="driver">
+ <choice>
+ <value>qemu</value>
+ <value>kvm</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="hpt">
+ <element name="hpt">
+ <optional>
+ <attribute name="resizing">
+ <choice>
+ <value>enabled</value>
+ <value>disabled</value>
+ <value>required</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="maxpagesize">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="vmcoreinfo">
+ <element name="vmcoreinfo">
+ <optional>
+ <attribute name="state">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="msrs">
+ <element name="msrs">
+ <attribute name="unknown">
+ <choice>
+ <value>ignore</value>
+ <value>fault</value>
+ </choice>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="cfpc">
+ <element name="cfpc">
+ <attribute name="value">
+ <choice>
+ <value>broken</value>
+ <value>workaround</value>
+ <value>fixed</value>
+ </choice>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="sbbc">
+ <element name="sbbc">
+ <attribute name="value">
+ <choice>
+ <value>broken</value>
+ <value>workaround</value>
+ <value>fixed</value>
+ </choice>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="ibs">
+ <element name="ibs">
+ <attribute name="value">
+ <choice>
+ <value>broken</value>
+ <value>workaround</value>
+ <value>fixed-ibs</value>
+ <value>fixed-ccd</value>
+ <value>fixed-na</value>
+ </choice>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="address">
+ <element name="address">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+ <ref name="pciaddress"/>
+ <ref name="zpciaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>drive</value>
+ </attribute>
+ <ref name="driveaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>virtio-serial</value>
+ </attribute>
+ <ref name="virtioserialaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>ccid</value>
+ </attribute>
+ <ref name="ccidaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+ <ref name="usbportaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>spapr-vio</value>
+ </attribute>
+ <ref name="spaprvioaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>ccw</value>
+ </attribute>
+ <ref name="ccwaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>isa</value>
+ </attribute>
+ <ref name="isaaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>virtio-mmio</value>
+ </attribute>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>dimm</value>
+ </attribute>
+ <ref name="dimmaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>unassigned</value>
+ </attribute>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="rom">
+ <element name="rom">
+ <optional>
+ <attribute name="enabled">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="bar">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="memorydev">
+ <element name="memory">
+ <attribute name="model">
+ <choice>
+ <value>dimm</value>
+ <value>nvdimm</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="access">
+ <choice>
+ <value>shared</value>
+ <value>private</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="discard">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="memorydev-source"/>
+ </optional>
+ <ref name="memorydev-target"/>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="memorydev-source">
+ <element name="source">
+ <choice>
+ <group>
+ <interleave>
+ <optional>
+ <element name="pagesize">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="nodemask">
+ <ref name="cpuset"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ <group>
+ <interleave>
+ <element name="path">
+ <ref name="absFilePath"/>
+ </element>
+ <optional>
+ <element name="alignsize">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="pmem">
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="memorydev-target">
+ <element name="target">
+ <interleave>
+ <element name="size">
+ <ref name="scaledInteger"/>
+ </element>
+ <optional>
+ <element name="node">
+ <ref name="unsignedInt"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="label">
+ <element name="size">
+ <ref name="scaledInteger"/>
+ </element>
+ </element>
+ </optional>
+ <optional>
+ <element name="readonly">
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="rng">
+ <element name="rng">
+ <attribute name="model">
+ <choice>
+ <value>virtio</value>
+ <value>virtio-transitional</value>
+ <value>virtio-non-transitional</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <ref name="rng-backend"/>
+ <optional>
+ <element name="driver">
+ <ref name="virtioOptions"/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="rng-rate"/>
+ </optional>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="rng-backend">
+ <element name="backend">
+ <choice>
+ <group>
+ <attribute name="model">
+ <value>random</value>
+ </attribute>
+ <choice>
+ <ref name="absFilePath"/>
+ <empty/>
+ </choice>
+ </group>
+ <group>
+ <attribute name="model">
+ <value>egd</value>
+ </attribute>
+ <ref name="qemucdevSrcType"/>
+ <ref name="qemucdevSrcDef"/>
+ </group>
+ <group>
+ <attribute name="model">
+ <value>builtin</value>
+ </attribute>
+ <empty/>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="rng-rate">
+ <element name="rate">
+ <attribute name="bytes">
+ <ref name="positiveInteger"/>
+ </attribute>
+ <optional>
+ <attribute name="period">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="virtioOptions">
+ <optional>
+ <attribute name="iommu">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ats">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="packed">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="usbmaster">
+ <element name="master">
+ <attribute name="startport">
+ <ref name="usbPort"/>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="deviceBoot">
+ <element name="boot">
+ <attribute name="order">
+ <ref name="positiveInteger"/>
+ </attribute>
+ <optional>
+ <attribute name="loadparm">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9.\s]{1,8}</param>
+ </data>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="diskMirror">
+ <element name="mirror">
+ <choice>
+ <group> <!-- old format, for block copy back-compat -->
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ <optional>
+ <attribute name="format">
+ <ref name="storageFormat"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="job">
+ <value>copy</value>
+ </attribute>
+ </optional>
+ <optional>
+ <interleave>
+ <ref name="diskSourceFile"/>
+ <optional>
+ <ref name="diskFormat"/>
+ </optional>
+ </interleave>
+ </optional>
+ </group>
+ <group> <!-- preferred format -->
+ <attribute name="job">
+ <choice>
+ <value>copy</value>
+ <value>active-commit</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <ref name="diskSource"/>
+ <optional>
+ <ref name="diskFormat"/>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="ready">
+ <choice>
+ <value>yes</value>
+ <value>abort</value>
+ <value>pivot</value>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="diskBackingChain"/>
+ </element>
+ </define>
+ <define name="diskAuth">
+ <element name="auth">
+ <attribute name="username">
+ <ref name="genericName"/>
+ </attribute>
+ <ref name="diskAuthSecret"/>
+ </element>
+ </define>
+
+ <define name="diskAuthSecret">
+ <element name="secret">
+ <attribute name="type">
+ <choice>
+ <value>ceph</value>
+ <value>iscsi</value>
+ </choice>
+ </attribute>
+ <choice>
+ <attribute name="uuid">
+ <ref name="UUID"/>
+ </attribute>
+ <attribute name="usage">
+ <ref name="objectName"/>
+ </attribute>
+ </choice>
+ </element>
+ </define>
+
+ <define name="diskIoTune">
+ <element name="iotune">
+ <interleave>
+ <choice>
+ <element name="total_bytes_sec">
+ <data type="unsignedLong"/>
+ </element>
+ <group>
+ <interleave>
+ <optional>
+ <element name="read_bytes_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_bytes_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ <choice>
+ <element name="total_iops_sec">
+ <data type="unsignedLong"/>
+ </element>
+ <group>
+ <interleave>
+ <optional>
+ <element name="read_iops_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_iops_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ <choice>
+ <element name="total_bytes_sec_max">
+ <data type="unsignedLong"/>
+ </element>
+ <group>
+ <interleave>
+ <optional>
+ <element name="read_bytes_sec_max">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_bytes_sec_max">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ <choice>
+ <element name="total_iops_sec_max">
+ <data type="unsignedLong"/>
+ </element>
+ <group>
+ <interleave>
+ <optional>
+ <element name="read_iops_sec_max">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_iops_sec_max">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ <optional>
+ <element name="size_iops_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="group_name">
+ <text/>
+ </element>
+ </optional>
+ <choice>
+ <element name="total_bytes_sec_max_length">
+ <data type="unsignedLong"/>
+ </element>
+ <group>
+ <interleave>
+ <optional>
+ <element name="read_bytes_sec_max_length">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_bytes_sec_max_length">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ <choice>
+ <element name="total_iops_sec_max_length">
+ <data type="unsignedLong"/>
+ </element>
+ <group>
+ <interleave>
+ <optional>
+ <element name="read_iops_sec_max_length">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_iops_sec_max_length">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ </interleave>
+ </element>
+ </define>
+
+ <!-- Optional HyperV Enlightenment features -->
+ <define name="hyperv">
+ <element name="hyperv">
+ <interleave>
+ <optional>
+ <element name="relaxed">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="vapic">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="spinlocks">
+ <ref name="featurestate"/>
+ <optional>
+ <attribute name="retries">
+ <data type="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="vpindex">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="runtime">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="synic">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="stimer">
+ <ref name="stimer"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="reset">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="vendor_id">
+ <ref name="featurestate"/>
+ <optional>
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">[^,]{0,12}</param>
+ </data>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="frequencies">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="reenlightenment">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="tlbflush">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="ipi">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="evmcs">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <!-- Hyper-V stimer features -->
+ <define name="stimer">
+ <interleave>
+ <optional>
+ <ref name="featurestate"/>
+ </optional>
+ <optional>
+ <element name="direct">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+ <!-- Optional KVM features -->
+ <define name="kvm">
+ <element name="kvm">
+ <interleave>
+ <optional>
+ <element name="hidden">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="hint-dedicated">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="poll-control">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <!-- Optional Xen features -->
+ <define name="xen">
+ <element name="xen">
+ <interleave>
+ <optional>
+ <element name="e820_host">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="passthrough">
+ <ref name="featurestate"/>
+ <optional>
+ <attribute name="mode">
+ <data type="string">
+ <param name="pattern">(sync_pt|share_pt)</param>
+ </data>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <!-- Optional capabilities features -->
+ <define name="capabilities">
+ <element name="capabilities">
+ <ref name="capabilitiespolicy"/>
+ <interleave>
+ <optional>
+ <element name="audit_control">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="audit_write">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="block_suspend">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="chown">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="dac_override">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="dac_read_search">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="fowner">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="fsetid">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="ipc_lock">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="ipc_owner">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="kill">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="lease">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="linux_immutable">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="mac_admin">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="mac_override">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="mknod">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="net_admin">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="net_bind_service">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="net_broadcast">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="net_raw">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="setgid">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="setfcap">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="setpcap">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="setuid">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_admin">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_boot">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_chroot">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_module">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_nice">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_pacct">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_ptrace">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_rawio">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_resource">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_time">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="sys_tty_config">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="syslog">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="wake_alarm">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="pmu">
+ <element name="pmu">
+ <optional>
+ <ref name="featurestate"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="featurestate">
+ <attribute name="state">
+ <ref name="virOnOff"/>
+ </attribute>
+ </define>
+
+ <define name="capabilitiespolicy">
+ <attribute name="policy">
+ <choice>
+ <value>default</value>
+ <value>allow</value>
+ <value>deny</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <!--
+ Optional hypervisor extensions in their own namespace:
+ QEMU
+ -->
+ <define name="qemucmdline">
+ <element name="commandline" ns="http://libvirt.org/schemas/domain/qemu/1.0">
+ <interleave>
+ <zeroOrMore>
+ <element name="arg">
+ <attribute name="value"/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="env">
+ <attribute name="name">
+ <ref name="filter-param-name"/>
+ </attribute>
+ <optional>
+ <attribute name="value"/>
+ </optional>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="qemucapabilities">
+ <element name="capabilities" ns="http://libvirt.org/schemas/domain/qemu/1.0">
+ <interleave>
+ <zeroOrMore>
+ <element name="add">
+ <attribute name="capability"/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="del">
+ <attribute name="capability"/>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+
+ <!--
+ Optional hypervisor extensions in their own namespace:
+ LXC
+ -->
+ <define name="lxcsharens">
+ <element name="namespace" ns="http://libvirt.org/schemas/domain/lxc/1.0">
+ <interleave>
+ <optional>
+ <element name="sharenet">
+ <attribute name="type">
+ <choice>
+ <value>netns</value>
+ <value>name</value>
+ <value>pid</value>
+ </choice>
+ </attribute>
+ <attribute name="value"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="shareipc">
+ <attribute name="type">
+ <choice>
+ <value>name</value>
+ <value>pid</value>
+ </choice>
+ </attribute>
+ <attribute name="value"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="shareuts">
+ <attribute name="type">
+ <choice>
+ <value>name</value>
+ <value>pid</value>
+ </choice>
+ </attribute>
+ <attribute name="value"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <!--
+ Optional hypervisor extensions in their own namespace:
+ Bhyve
+ -->
+ <define name="bhyvecmdline">
+ <element name="commandline" ns="http://libvirt.org/schemas/domain/bhyve/1.0">
+ <zeroOrMore>
+ <element name="arg">
+ <attribute name="value"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <!--
+ Optional hypervisor extensions in their own namespace:
+ Xen
+ -->
+ <define name="xencmdline">
+ <element name="commandline" ns="http://libvirt.org/schemas/domain/xen/1.0">
+ <zeroOrMore>
+ <element name="arg">
+ <attribute name="value"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <!--
+ Optional hypervisor extensions in their own namespace:
+ vmware
+ -->
+ <define name="vmwaredatacenterpath">
+ <element name="datacenterpath" ns="http://libvirt.org/schemas/domain/vmware/1.0">
+ <text/>
+ </element>
+ </define>
+
+ <!--
+ Type library
+ -->
+ <define name="countCPU">
+ <data type="unsignedShort">
+ <param name="pattern">[0-9]+</param>
+ <param name="minInclusive">1</param>
+ </data>
+ </define>
+ <define name="vcpuid">
+ <data type="unsignedShort">
+ <param name="pattern">[0-9]+</param>
+ </data>
+ </define>
+ <define name="cpushares">
+ <data type="unsignedInt">
+ <param name="pattern">[0-9]+</param>
+ </data>
+ </define>
+ <define name="cpuperiod">
+ <data type="unsignedLong">
+ <param name="pattern">[0-9]+</param>
+ <param name="minInclusive">1000</param>
+ <param name="maxInclusive">1000000</param>
+ </data>
+ </define>
+ <define name="cpuquota">
+ <data type="long">
+ <param name="pattern">-?[0-9]+</param>
+ <param name="maxInclusive">18446744073709551</param>
+ <param name="minInclusive">-1</param>
+ </data>
+ </define>
+ <define name="rebootTimeoutDelay">
+ <data type="short">
+ <param name="minInclusive">-1</param>
+ </data>
+ </define>
+ <!-- weight currently is in range [100, 1000] -->
+ <define name="weight">
+ <data type="unsignedInt">
+ <param name="pattern">[0-9]+</param>
+ <param name="minInclusive">100</param>
+ <param name="maxInclusive">1000</param>
+ </data>
+ </define>
+ <define name="diskSerial">
+ <data type="string">
+ <param name="pattern">[A-Za-z0-9_\.\+\- ]+</param>
+ </data>
+ </define>
+ <define name="bridgeMode">
+ <data type="string">
+ <param name="pattern">(vepa|bridge|private|passthrough)</param>
+ </data>
+ </define>
+ <define name="addrIPorName">
+ <choice>
+ <ref name="ipAddr"/>
+ <ref name="dnsName"/>
+ </choice>
+ </define>
+ <define name="usbIdDefault">
+ <data type="string">
+ <param name="pattern">-1</param>
+ </data>
+ </define>
+ <define name="usbId">
+ <data type="string">
+ <param name="pattern">(0x)?[0-9a-fA-F]{1,4}</param>
+ </data>
+ </define>
+ <define name="usbVersion">
+ <data type="string">
+ <param name="pattern">[0-9]{1,2}.[0-9]{1,2}</param>
+ </data>
+ </define>
+ <define name="usbAddr">
+ <data type="string">
+ <param name="pattern">(0x)?[0-9a-fA-F]{1,3}</param>
+ </data>
+ </define>
+ <define name="usbClass">
+ <data type="string">
+ <param name="pattern">(0x)?[0-9a-fA-F]{1,2}</param>
+ </data>
+ </define>
+ <define name="usbPort">
+ <data type="string">
+ <param name="pattern">((0x)?[0-9a-fA-F]{1,3}\.){0,3}(0x)?[0-9a-fA-F]{1,3}</param>
+ </data>
+ </define>
+ <define name="driveController">
+ <data type="string">
+ <param name="pattern">[0-9]{1,2}</param>
+ </data>
+ </define>
+ <define name="driveBus">
+ <data type="string">
+ <param name="pattern">[0-9]{1,2}</param>
+ </data>
+ </define>
+ <define name="driveTarget">
+ <data type="string">
+ <param name="pattern">[0-9]{1,2}</param>
+ </data>
+ </define>
+ <define name="driveSCSITarget">
+ <data type="string">
+ <param name="pattern">[0-9]{1,10}</param>
+ </data>
+ </define>
+ <define name="driveUnit">
+ <data type="string">
+ <param name="pattern">[0-9]{1,5}</param>
+ </data>
+ </define>
+ <define name="driveSCSIUnit">
+ <data type="string">
+ <param name="pattern">[0-9]{1,20}</param>
+ </data>
+ </define>
+ <define name="timeDelta">
+ <data type="string">
+ <param name="pattern">(-|\+)?[0-9]+</param>
+ </data>
+ </define>
+ <define name="timeZone">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\.\+\-/]+</param>
+ </data>
+ </define>
+ <define name="spaprvioReg">
+ <data type="string">
+ <param name="pattern">(0x)?[0-9a-fA-F]{1,8}</param>
+ </data>
+ </define>
+ <define name="aliasName">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\-.]+</param>
+ </data>
+ </define>
+ <define name="virtioserialPort">
+ <data type="string">
+ <param name="pattern">[0-9]{1,2}</param>
+ </data>
+ </define>
+ <define name="ccidSlot">
+ <data type="string">
+ <param name="pattern">[0-9]{1,2}</param>
+ </data>
+ </define>
+ <define name="alias">
+ <element name="alias">
+ <attribute name="name">
+ <ref name="aliasName"/>
+ </attribute>
+ </element>
+ <empty/>
+ </define>
+ <define name="panic">
+ <element name="panic">
+ <optional>
+ <attribute name="model">
+ <choice>
+ <value>isa</value>
+ <value>pseries</value>
+ <value>hyperv</value>
+ <value>s390</value>
+ </choice>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <ref name="alias"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="rawIO">
+ <attribute name="rawio">
+ <ref name="virYesNo"/>
+ </attribute>
+ </define>
+ <define name="sgIO">
+ <attribute name="sgio">
+ <choice>
+ <value>filtered</value>
+ <value>unfiltered</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="coalesce">
+ <element name="coalesce">
+ <interleave>
+ <optional>
+ <element name="rx">
+ <optional>
+ <element name="frames">
+ <optional>
+ <attribute name="max">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <!--
+ This is how we'd add more Rx-related settings for
+ frames, like irq, high, and low
+
+ <optional>
+ <attribute name="irq">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="high">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="low">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+
+ -->
+ </element>
+ </optional>
+ <!--
+ This is how we'd add more Rx-related settings, like
+ usecs
+
+ <optional>
+ <element name="usecs">
+ <optional>
+ <attribute name="max">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="irq">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="high">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="low">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ -->
+ </element>
+ </optional>
+ <!--
+ This is how you would add more coalesce settings, like
+ Tx-related ones
+
+ <optional>
+ <element name="tx">
+ <optional>
+ <element name="frames">
+ <optional>
+ <attribute name="max">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="irq">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="high">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="low">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="usecs">
+ <optional>
+ <attribute name="max">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="irq">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="high">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="low">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </optional>
+ -->
+ </interleave>
+ </element>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/domainsnapshot.rng b/src/main/resources/libvirt/rng/domainsnapshot.rng
new file mode 100644
index 0000000..58c3708
--- /dev/null
+++ b/src/main/resources/libvirt/rng/domainsnapshot.rng
@@ -0,0 +1,228 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt domain snapshot properties XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <ref name="domainsnapshot"/>
+ </start>
+
+ <include href="domaincommon.rng"/>
+
+ <define name="domainsnapshot">
+ <element name="domainsnapshot">
+ <interleave>
+ <optional>
+ <element name="name">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="description">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="state">
+ <ref name="state"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="creationTime">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="memory">
+ <choice>
+ <attribute name="snapshot">
+ <choice>
+ <value>no</value>
+ <value>internal</value>
+ </choice>
+ </attribute>
+ <group>
+ <optional>
+ <attribute name="snapshot">
+ <value>external</value>
+ </attribute>
+ </optional>
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ </group>
+ </choice>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="disks">
+ <zeroOrMore>
+ <ref name="disksnapshot"/>
+ </zeroOrMore>
+ </element>
+ </optional>
+ <optional>
+ <element name="active">
+ <choice>
+ <value>0</value>
+ <value>1</value>
+ </choice>
+ </element>
+ </optional>
+ <optional>
+ <choice>
+ <element name="domain">
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </element>
+ <!-- Nested grammar ensures that any of our overrides of
+ storagecommon/domaincommon defines do not conflict
+ with any domain.rng overrides. -->
+ <grammar>
+ <include href="domain.rng"/>
+ </grammar>
+ </choice>
+ </optional>
+ <optional>
+ <element name="parent">
+ <element name="name">
+ <text/>
+ </element>
+ </element>
+ </optional>
+ <optional>
+ <element name="cookie">
+ <zeroOrMore>
+ <ref name="customElement"/>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="state">
+ <choice>
+ <value>running</value>
+ <value>blocked</value>
+ <value>paused</value>
+ <value>shutdown</value>
+ <value>shutoff</value>
+ <value>crashed</value>
+ <value>disk-snapshot</value>
+ </choice>
+ </define>
+
+ <define name="storageSourceExtra" combine="choice">
+ <!-- overrides the no-op version in storagecommon.rng -->
+ <ref name="disksnapshotdriver"/>
+ </define>
+
+ <define name="disksnapshot">
+ <element name="disk">
+ <attribute name="name">
+ <choice>
+ <ref name="diskTarget"/>
+ <ref name="absFilePath"/>
+ </choice>
+ </attribute>
+ <choice>
+ <attribute name="snapshot">
+ <value>no</value>
+ </attribute>
+ <attribute name="snapshot">
+ <value>internal</value>
+ </attribute>
+ <group>
+ <optional>
+ <attribute name="snapshot">
+ <value>external</value>
+ </attribute>
+ </optional>
+ <choice>
+ <group>
+ <optional>
+ <attribute name="type">
+ <value>file</value>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="source">
+ <optional>
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="storageStartupPolicy"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="diskSourceCommon"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <empty/>
+ </element>
+ </optional>
+ <ref name="storageSourceExtra"/>
+ </interleave>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>block</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="source">
+ <attribute name="dev">
+ <ref name="absFilePath"/>
+ </attribute>
+ <zeroOrMore>
+ <ref name="devSeclabel"/>
+ </zeroOrMore>
+ <optional>
+ <ref name="diskSourceCommon"/>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <empty/>
+ </element>
+ </optional>
+ <ref name="storageSourceExtra"/>
+ </interleave>
+ </group>
+ <ref name="diskSourceNetwork"/>
+ </choice>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="disksnapshotdriver">
+ <optional>
+ <element name="driver">
+ <optional>
+ <attribute name="type">
+ <ref name="storageFormatBacking"/>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="metadata_cache">
+ <optional>
+ <element name="max_size">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </optional>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/interface.rng b/src/main/resources/libvirt/rng/interface.rng
new file mode 100644
index 0000000..8c11f0d
--- /dev/null
+++ b/src/main/resources/libvirt/rng/interface.rng
@@ -0,0 +1,434 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for network interfaces -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ xmlns:v="http://netcf.org/xml/version/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <!-- Versions for this schema are simple integers that are incremented
+ every time a changed (but backwards compatible) version
+ is released. The current version is indicated with the v:serial
+ attribute on the start element.
+ -->
+ <start v:serial="4">
+ <choice>
+ <ref name="ethernet-interface"/>
+ <ref name="bridge-interface"/>
+ <ref name="bond-interface"/>
+ <ref name="vlan-interface"/>
+ </choice>
+ </start>
+
+ <include href="basictypes.rng"/>
+ <!--
+ FIXME: How do we handle VLAN's ? Should they be their own interface
+ or should we treat them as an option on the base interface ? For
+ example, for vlan eth0.42, it would make sense to make that part of
+ the definition of the eth0 interface.
+ -->
+
+ <!--
+ Ethernet adapter
+ -->
+ <define name="basic-ethernet-content">
+ <interleave>
+ <attribute name="type">
+ <value>ethernet</value>
+ </attribute>
+ <ref name="name-attr"/>
+ <!-- If no MAC is given when the interface is defined, it is determined
+ by using the device name.
+ FIXME: What if device name and MAC don't specify the same NIC ? -->
+ <optional>
+ <element name="mac">
+ <attribute name="address"><ref name="macAddr"/></attribute>
+ </element>
+ </optional>
+ <ref name="link-speed-state"/>
+ <!-- FIXME: Allow (some) ethtool options -->
+ </interleave>
+ </define>
+
+ <!-- Ethernet adapter without IP addressing, e.g. for a bridge -->
+ <define name="bare-ethernet-interface">
+ <element name="interface">
+ <interleave>
+ <ref name="basic-ethernet-content"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="ethernet-interface">
+ <element name="interface">
+ <interleave>
+ <ref name="startmode"/>
+ <ref name="basic-ethernet-content"/>
+ <ref name="mtu"/>
+ <ref name="interface-addressing"/>
+ </interleave>
+ </element>
+ </define>
+
+ <!--
+ VLAN's
+ -->
+ <define name="vlan-interface-common">
+ <attribute name="type">
+ <value>vlan</value>
+ </attribute>
+ <!-- The name attribute is only useful for reporting back and is always
+ of the form DEVICE.VLAN
+ -->
+ <optional><ref name="name-attr"/></optional>
+ <ref name="link-speed-state"/>
+ </define>
+
+ <define name="vlan-device">
+ <element name="vlan">
+ <attribute name="tag"><ref name="vlan-id"/></attribute>
+ <element name="interface">
+ <attribute name="name"><ref name="deviceName"/></attribute>
+ </element>
+ </element>
+ </define>
+
+ <define name="bare-vlan-interface">
+ <element name="interface">
+ <interleave>
+ <ref name="vlan-interface-common"/>
+ <ref name="vlan-device"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="vlan-interface">
+ <element name="interface">
+ <interleave>
+ <ref name="vlan-interface-common"/>
+ <ref name="startmode"/>
+ <ref name="mtu"/>
+ <ref name="interface-addressing"/>
+ <ref name="vlan-device"/>
+ </interleave>
+ </element>
+ </define>
+
+ <!--
+ Bridges
+ -->
+ <define name="bridge-interface">
+ <element name="interface">
+ <interleave>
+ <attribute name="type">
+ <value>bridge</value>
+ </attribute>
+ <ref name="name-attr"/>
+ <ref name="startmode"/>
+ <ref name="mtu"/>
+ <ref name="interface-addressing"/>
+ <element name="bridge">
+ <optional>
+ <attribute name="stp">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+ <!-- Bridge forward delay
+ (see "ip link set <dev> type bridge forward_delay") -->
+ <optional v:since="2">
+ <attribute name="delay"><ref name="timeval"/></attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="bare-ethernet-interface"/>
+ <ref name="bare-vlan-interface"/>
+ <ref v:since="2" name="bare-bond-interface"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </interleave>
+ </element>
+ </define>
+ <!-- Jim Fehlig would like support for other bridge attributes, in
+ particular hellotime, forwarddelay, and maxage
+ -->
+
+ <!--
+ Bonds
+ -->
+ <define name="bond-interface-common">
+ <attribute name="type">
+ <value>bond</value>
+ </attribute>
+ <ref name="name-attr"/>
+ <ref name="link-speed-state"/>
+ </define>
+
+ <define name="bond-element">
+ <element name="bond">
+ <optional>
+ <attribute name="mode">
+ <choice>
+ <value>balance-rr</value>
+ <!-- The primary interface is the first interface child
+ of the bond element -->
+ <value>active-backup</value>
+ <value>balance-xor</value>
+ <value>broadcast</value>
+ <value>802.3ad</value>
+ <value>balance-tlb</value>
+ <value>balance-alb</value>
+ </choice>
+ </attribute>
+ </optional>
+
+ <!-- FIXME: add more attributes
+
+ mode == 802.3ad
+ ad_select
+ lacp_rate
+ xmit_hash_policy
+
+ mode == active-backup
+ fail_over_mac
+ num_grat_arp when mode == active-backup (since 3.3.0)
+ num_unsol_na when mode == active-backup (ipv6, since 3.4.0)
+
+ mode == balance-xor
+ xmit_hash_policy (since 2.6.3/3.2.2)
+ -->
+
+ <interleave>
+ <optional>
+ <choice>
+ <element name="miimon">
+ <!-- miimon frequency in ms -->
+ <attribute name="freq"><ref name="unsignedInt"/></attribute>
+ <optional>
+ <attribute name="downdelay"><ref name="unsignedInt"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="updelay"><ref name="unsignedInt"/></attribute>
+ </optional>
+ <optional>
+ <!-- use_carrier -->
+ <attribute name="carrier">
+ <choice>
+ <!-- use MII/ETHTOOL ioctl -->
+ <value>ioctl</value>
+ <!-- use netif_carrier_ok() -->
+ <value>netif</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ <element name="arpmon">
+ <attribute name="interval"><ref name="unsignedInt"/></attribute>
+ <attribute name="target"><ref name="ipv4Addr"/></attribute>
+ <optional>
+ <attribute name="validate">
+ <choice>
+ <value>none</value>
+ <value>active</value>
+ <value>backup</value>
+ <value>all</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </choice>
+ </optional>
+
+ <oneOrMore>
+ <!-- The slave interfaces -->
+ <ref name="bare-ethernet-interface"/>
+ </oneOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="bare-bond-interface">
+ <element name="interface">
+ <interleave>
+ <ref name="bond-interface-common"/>
+ <ref name="bond-element"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="bond-interface">
+ <element name="interface">
+ <interleave>
+ <ref name="bond-interface-common"/>
+ <ref name="startmode"/>
+ <ref name="mtu"/>
+ <ref name="interface-addressing"/>
+ <ref name="bond-element"/>
+ </interleave>
+ </element>
+ </define>
+
+ <!-- Basic attributes for all interface types -->
+ <define name="name-attr">
+ <!-- The device name, like eth0 or br2 -->
+ <attribute name="name"><ref name="deviceName"/></attribute>
+ </define>
+
+ <define name="mtu">
+ <optional>
+ <element name="mtu">
+ <attribute name="size"><ref name="unsignedInt"/></attribute>
+ </element>
+ </optional>
+ </define>
+
+ <define name="startmode">
+ <optional>
+ <element name="start">
+ <attribute name="mode">
+ <choice>
+ <value>onboot</value>
+ <value>none</value>
+ <value>hotplug</value>
+ <!-- Jim Fehlig lists the following that SuSe supports:
+ manual, ifplug, nfsroot -->
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+ </define>
+
+ <!--
+ Assignment of addresses to an interface, allowing for
+ different protocols
+ -->
+ <define name="interface-addressing">
+ <choice>
+ <group>
+ <optional>
+ <ref name="protocol-ipv4"/>
+ </optional>
+ <optional v:since="3">
+ <ref name="protocol-ipv6"/>
+ </optional>
+ </group>
+ <group>
+ <optional v:since="3">
+ <ref name="protocol-ipv6"/>
+ </optional>
+ <optional>
+ <ref name="protocol-ipv4"/>
+ </optional>
+ </group>
+ </choice>
+ </define>
+
+ <define name="protocol-ipv4">
+ <element name="protocol">
+ <attribute name="family">
+ <value>ipv4</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <ref name="dhcp-element"/>
+ </optional>
+ <zeroOrMore>
+ <element name="ip">
+ <attribute name="address"><ref name="ipv4Addr"/></attribute>
+ <optional>
+ <attribute name="prefix"><ref name="ipv4Prefix"/></attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="route">
+ <attribute name="gateway"><ref name="ipv4Addr"/></attribute>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="protocol-ipv6">
+ <element name="protocol">
+ <attribute name="family">
+ <value>ipv6</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="autoconf"><empty/></element>
+ </optional>
+ <optional>
+ <ref name="dhcp-element"/>
+ </optional>
+ <zeroOrMore>
+ <element name="ip">
+ <attribute name="address"><ref name="ipv6Addr"/></attribute>
+ <optional>
+ <attribute name="prefix"><ref name="ipv6Prefix"/></attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="route">
+ <attribute name="gateway"><ref name="ipv6Addr"/></attribute>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="dhcp-element">
+ <element name="dhcp">
+ <optional>
+ <attribute name="peerdns">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <!-- Jim Fehlig (<jfehlig@novell.com>) suggest the
+ following additions to DHCP:
+
+ WRT dhcp element, would it make sense to consider hostname (hostname
+ to send to server) and if to change the local hostname to the
+ hostname delivered via dhcp? E.g. hostname="foo" (default
+ `hostname`) sethostname
+
+ Also route:
+ setrouting (default "yes")
+ setdefaultroute (default "yes")
+
+ and NIS:
+ nis (default "yes")
+ setnisdomain (default "yes")
+
+ What about dhcpv6? A separate <dhcp6 /> element?
+ -->
+
+ <!-- Jim Fehlig suggest adding static routing info
+
+ As for routing info, how about a separate route element:
+
+ <route gateway="192.168.0.1" /> # destination=default
+ <route destination="default" gateway="192.168.0.1" />
+ <route destination="10.0.0.0/8" gateway="192.168.0.2" />
+ <route destination="2001:DB8:C::/64" gateway="2001:DB8:C::1" />
+ <route destination="2001:DB8::/32"> # unrecheable route (loopback)
+
+ It would perhaps make sense to use iproute2 names, that is prefix
+ instead of destination and nexthop instead of gateway.
+ -->
+
+ <!-- Type library -->
+
+ <define name="timeval">
+ <data type="double">
+ <param name="minInclusive">0</param>
+ </data>
+ </define>
+
+ <define name="vlan-id">
+ <data type="unsignedInt">
+ <param name="maxInclusive">4095</param>
+ </data>
+ </define>
+</grammar>
diff --git a/src/main/resources/libvirt/rng/network.rng b/src/main/resources/libvirt/rng/network.rng
new file mode 100644
index 0000000..4317572
--- /dev/null
+++ b/src/main/resources/libvirt/rng/network.rng
@@ -0,0 +1,450 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt network XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="network"/>
+ </start>
+
+ <include href="basictypes.rng"/>
+ <include href="networkcommon.rng"/>
+
+ <define name="network">
+
+ <element name="network">
+ <optional>
+ <attribute name="connections">
+ <data type="unsignedInt"/>
+ </attribute>
+ </optional>
+ <!-- Enables IPv6 guest-to-guest communications on a network
+ with no gateways addresses specified -->
+ <optional>
+ <attribute name="ipv6">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="trustGuestRxFilters">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+
+ <!-- The name of the network, used to refer to it through the API
+ and in virsh -->
+ <element name="name">
+ <text/>
+ </element>
+
+ <!-- <metadata> element -->
+ <optional>
+ <ref name="metadata"/>
+ </optional>
+
+ <!-- <uuid> element -->
+ <optional>
+ <element name="uuid"><ref name="UUID"/></element>
+ </optional>
+
+ <!-- <bridge> element -->
+ <optional>
+ <!-- The name of the network to be set up; this will back
+ the network on the host -->
+ <element name="bridge">
+ <optional>
+ <attribute name="name">
+ <ref name="deviceName"/>
+ </attribute>
+ </optional>
+
+ <optional>
+ <attribute name="zone">
+ <ref name="zoneName"/>
+ </attribute>
+ </optional>
+
+ <optional>
+ <attribute name="stp">
+ <ref name="virOnOff"/>
+ </attribute>
+ </optional>
+
+ <optional>
+ <attribute name="delay">
+ <data type="unsignedLong"/>
+ </attribute>
+ </optional>
+
+ <optional>
+ <attribute name="macTableManager">
+ <ref name="macTableManager"/>
+ </attribute>
+ </optional>
+
+ </element>
+ </optional>
+
+ <!-- <mtu> element -->
+ <optional>
+ <ref name="mtu"/>
+ </optional>
+
+ <!-- <mac> element -->
+ <optional>
+ <element name="mac">
+ <attribute name="address"><ref name="uniMacAddr"/></attribute>
+ <empty/>
+ </element>
+ </optional>
+
+ <!-- <forward> element -->
+ <optional>
+ <!-- The device through which the bridge is connected to the
+ rest of the network -->
+ <element name="forward">
+ <optional>
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ </optional>
+
+ <optional>
+ <attribute name="mode">
+ <choice>
+ <value>nat</value>
+ <value>route</value>
+ <value>open</value>
+ <value>bridge</value>
+ <value>passthrough</value>
+ <value>private</value>
+ <value>vepa</value>
+ <value>hostdev</value>
+ </choice>
+ </attribute>
+ </optional>
+
+ <optional>
+ <attribute name="managed">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <choice>
+ <group>
+ <zeroOrMore>
+ <element name="interface">
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ <optional>
+ <attribute name="connections">
+ <data type="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
+ </group>
+ <group>
+ <zeroOrMore>
+ <element name="address">
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+ <ref name="pciaddress"/>
+ <optional>
+ <attribute name="connections">
+ <data type="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
+ </group>
+ </choice>
+ <optional>
+ <element name="pf">
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="driver">
+ <attribute name="name">
+ <choice>
+ <value>kvm</value>
+ <value>vfio</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="nat">
+ <optional>
+ <attribute name="ipv6">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="address">
+ <attribute name="start">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ <attribute name="end">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="port">
+ <attribute name="start">
+ <ref name="port"/>
+ </attribute>
+ <attribute name="end">
+ <ref name="port"/>
+ </attribute>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+
+ <!-- <virtualport> element -->
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
+
+ <!-- <portgroup> elements -->
+ <zeroOrMore>
+ <element name="portgroup">
+ <attribute name="name">
+ <ref name="deviceName"/>
+ </attribute>
+ <optional>
+ <attribute name="default">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="trustGuestRxFilters">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
+ <optional>
+ <ref name="bandwidth"/>
+ </optional>
+ <optional>
+ <ref name="vlan"/>
+ </optional>
+ </interleave>
+ </element>
+ </zeroOrMore>
+
+ <!-- <domain> element -->
+ <optional>
+ <element name="domain">
+ <attribute name="name"><ref name="dnsName"/></attribute>
+ <optional>
+ <attribute name="localOnly"><ref name="virYesNo"/></attribute>
+ </optional>
+ </element>
+ </optional>
+
+ <!-- Define the DNS related elements like TXT records
+ and other features in the <dns> element -->
+ <optional>
+ <element name="dns">
+ <optional>
+ <attribute name="enable">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="forwardPlainNames">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <zeroOrMore>
+ <element name="forwarder">
+ <optional>
+ <attribute name="addr"><ref name="ipAddr"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="domain"><ref name="dnsName"/></attribute>
+ </optional>
+ <empty/>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="txt">
+ <attribute name="name"><ref name="dnsName"/></attribute>
+ <attribute name="value"><text/></attribute>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="srv">
+ <attribute name="service"><text/></attribute>
+ <attribute name="protocol">
+ <ref name="protocol"/>
+ </attribute>
+ <optional>
+ <attribute name="domain"><ref name="dnsName"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="target"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="port">
+ <ref name="unsignedShort"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="priority">
+ <ref name="unsignedShort"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="weight">
+ <ref name="unsignedShort"/>
+ </attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="host">
+ <attribute name="ip"><ref name="ipAddr"/></attribute>
+ <oneOrMore>
+ <element name="hostname"><ref name="dnsName"/></element>
+ </oneOrMore>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </optional>
+ <optional>
+ <ref name="bandwidth"/>
+ </optional>
+ <optional>
+ <ref name="vlan"/>
+ </optional>
+ <optional>
+ <ref name="portOptions"/>
+ </optional>
+
+ <!-- <ip> element -->
+ <zeroOrMore>
+ <!-- The IP element sets up NAT'ing and an optional DHCP server
+ local to the host. -->
+ <element name="ip">
+ <optional>
+ <attribute name="address"><ref name="ipAddr"/></attribute>
+ </optional>
+ <optional>
+ <choice>
+ <attribute name="netmask"><ref name="ipv4Addr"/></attribute>
+ <attribute name="prefix"><ref name="ipPrefix"/></attribute>
+ </choice>
+ </optional>
+ <optional>
+ <attribute name="family"><ref name="addr-family"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="localPtr"><ref name="virYesNo"/></attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="tftp">
+ <attribute name="root"><text/></attribute>
+ </element>
+ </optional>
+ <optional>
+ <!-- Define the range(s) of IP addresses that the DHCP
+ server should hand out -->
+ <element name="dhcp">
+ <interleave>
+ <zeroOrMore>
+ <element name="range">
+ <attribute name="start"><ref name="ipAddr"/></attribute>
+ <attribute name="end"><ref name="ipAddr"/></attribute>
+ <interleave>
+ <optional>
+ <element name="lease">
+ <attribute name="expiry"><ref name="unsignedLong"/></attribute>
+ <optional>
+ <attribute name="unit"><ref name="leaseUnit"/></attribute>
+ </optional>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="host">
+ <choice>
+ <group>
+ <choice>
+ <attribute name="mac"><ref name="uniMacAddr"/></attribute>
+ <attribute name="id"><ref name="DUID"/></attribute>
+ </choice>
+ <optional>
+ <attribute name="name"><text/></attribute>
+ </optional>
+ </group>
+ <attribute name="name"><text/></attribute>
+ </choice>
+ <attribute name="ip"><ref name="ipAddr"/></attribute>
+ <interleave>
+ <optional>
+ <element name="lease">
+ <attribute name="expiry"><ref name="unsignedLong"/></attribute>
+ <optional>
+ <attribute name="unit"><ref name="leaseUnit"/></attribute>
+ </optional>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="bootp">
+ <attribute name="file"><ref name="filePath"/></attribute>
+ <optional>
+ <attribute name="server"><ref name="dnsName"/></attribute>
+ </optional>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ <!-- <route> element -->
+ <zeroOrMore>
+ <ref name="route"/>
+ </zeroOrMore>
+
+ <!-- <dnsmasq:options> -->
+ <optional>
+ <element name="options" ns="http://libvirt.org/schemas/network/dnsmasq/1.0">
+ <zeroOrMore>
+ <element name="option">
+ <attribute name="value"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+</grammar>
diff --git a/src/main/resources/libvirt/rng/networkcommon.rng b/src/main/resources/libvirt/rng/networkcommon.rng
new file mode 100644
index 0000000..6df6d43
--- /dev/null
+++ b/src/main/resources/libvirt/rng/networkcommon.rng
@@ -0,0 +1,294 @@
+<?xml version="1.0"?>
+<!-- network-related definitions used in multiple grammars -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <define name="virtualPortProfileID">
+ <data type="string">
+ <param name="maxLength">39</param>
+ </data>
+ </define>
+
+ <define name="virtualPortProfile">
+ <choice>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>802.1Qbg</value>
+ </attribute>
+ <optional>
+ <element name="parameters">
+ <optional>
+ <attribute name="managerid">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="typeid">
+ <ref name="uint24"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="typeidversion">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="instanceid">
+ <ref name="UUID"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </group>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>802.1Qbh</value>
+ </attribute>
+ <optional>
+ <element name="parameters">
+ <optional>
+ <attribute name="profileid">
+ <ref name="virtualPortProfileID"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </group>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>openvswitch</value>
+ </attribute>
+ <optional>
+ <element name="parameters">
+ <optional>
+ <attribute name="profileid">
+ <ref name="virtualPortProfileID"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="interfaceid">
+ <ref name="UUID"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </group>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>midonet</value>
+ </attribute>
+ <element name="parameters">
+ <attribute name="interfaceid">
+ <ref name="UUID"/>
+ </attribute>
+ </element>
+ </element>
+ </group>
+ <group>
+ <!-- use this when no type attribute is present -->
+ <element name="virtualport">
+ <optional>
+ <element name="parameters">
+ <optional>
+ <attribute name="managerid">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="typeid">
+ <ref name="uint24"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="typeidversion">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="instanceid">
+ <ref name="UUID"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="profileid">
+ <ref name="virtualPortProfileID"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="interfaceid">
+ <ref name="UUID"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </element>
+ </group>
+ </choice>
+ </define>
+
+ <define name="bandwidth">
+ <element name="bandwidth">
+ <optional>
+ <attribute name="classID">
+ <ref name="positiveInteger"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="inbound">
+ <ref name="bandwidth-attributes"/>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="outbound">
+ <ref name="bandwidth-attributes"/>
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="bandwidth-attributes">
+ <optional>
+ <attribute name="average">
+ <ref name="speed"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="peak">
+ <ref name="speed"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="floor">
+ <ref name="speed"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="burst">
+ <ref name="BurstSize"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="speed">
+ <data type="unsignedInt">
+ <param name="pattern">[0-9]+</param>
+ <param name="minInclusive">1</param>
+ </data>
+ </define>
+ <define name="BurstSize">
+ <data type="unsignedInt">
+ <param name="pattern">[0-9]+</param>
+ <param name="minInclusive">1</param>
+ </data>
+ </define>
+
+ <define name="unsignedShort">
+ <data type="integer">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+ </define>
+ <define name="protocol">
+ <data type="string">
+ <param name="pattern">(tcp)|(udp)</param>
+ </data>
+ </define>
+ <define name="addr-family">
+ <data type="string">
+ <param name="pattern">(ipv4)|(ipv6)</param>
+ </data>
+ </define>
+
+ <define name="vlan">
+ <element name="vlan">
+ <optional>
+ <attribute name="trunk">
+ <value>yes</value>
+ </attribute>
+ </optional>
+ <oneOrMore>
+ <element name="tag">
+ <attribute name="id">
+ <data type="unsignedInt">
+ <param name="maxInclusive">4095</param>
+ </data>
+ </attribute>
+ <optional>
+ <attribute name="nativeMode">
+ <choice>
+ <value>tagged</value>
+ <value>untagged</value>
+ </choice>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="port">
+ <data type="integer">
+ <param name="minInclusive">1</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+ </define>
+
+ <!-- The (static) route element specifies a network address and gateway
+ address to access that network. Both the network address and
+ the gateway address must be specified. -->
+ <define name="route">
+ <element name="route">
+ <optional>
+ <attribute name="family"><ref name="addr-family"/></attribute>
+ </optional>
+ <attribute name="address"><ref name="ipAddr"/></attribute>
+ <optional>
+ <choice>
+ <attribute name="netmask"><ref name="ipv4Addr"/></attribute>
+ <attribute name="prefix"><ref name="ipPrefix"/></attribute>
+ </choice>
+ </optional>
+ <attribute name="gateway"><ref name="ipAddr"/></attribute>
+ <optional>
+ <attribute name="metric"><ref name="unsignedInt"/></attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="macTableManager">
+ <choice>
+ <value>kernel</value>
+ <value>libvirt</value>
+ </choice>
+ </define>
+
+ <define name="mtu">
+ <element name="mtu">
+ <attribute name="size">
+ <ref name="unsignedShort"/>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="portOptions">
+ <element name="port">
+ <optional>
+ <attribute name="isolated">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/networkport.rng b/src/main/resources/libvirt/rng/networkport.rng
new file mode 100644
index 0000000..1a12a32
--- /dev/null
+++ b/src/main/resources/libvirt/rng/networkport.rng
@@ -0,0 +1,160 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt network port XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <include href="networkcommon.rng"/>
+
+ <start>
+ <ref name="networkport"/>
+ </start>
+
+ <define name="networkport">
+ <element name="networkport">
+ <interleave>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ <ref name="owner"/>
+ <ref name="mac"/>
+ <optional>
+ <ref name="group"/>
+ </optional>
+ <optional>
+ <ref name="rxfilters"/>
+ </optional>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
+ <optional>
+ <ref name="bandwidth"/>
+ </optional>
+ <optional>
+ <ref name="vlan"/>
+ </optional>
+ <optional>
+ <ref name="portOptions"/>
+ </optional>
+ <optional>
+ <ref name="plug"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="owner">
+ <element name="owner">
+ <element name="name">
+ <text/>
+ </element>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </element>
+ </define>
+
+ <define name="mac">
+ <element name="mac">
+ <attribute name="address">
+ <ref name="uniMacAddr"/>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="group">
+ <element name="group">
+ <ref name="deviceName"/>
+ </element>
+ </define>
+
+ <define name="rxfilters">
+ <element name="rxfilters">
+ <attribute name="trustGuest">
+ <ref name="virYesNo"/>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="plug">
+ <element name="plug">
+ <choice>
+ <ref name="plugnetwork"/>
+ <ref name="plugbridge"/>
+ <ref name="plugdirect"/>
+ <ref name="plughostdevpci"/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="plugnetwork">
+ <attribute name="type">
+ <value>network</value>
+ </attribute>
+ <attribute name="bridge">
+ <ref name="deviceName"/>
+ </attribute>
+ <optional>
+ <attribute name="macTableManager">
+ <ref name="macTableManager"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="plugbridge">
+ <attribute name="type">
+ <value>bridge</value>
+ </attribute>
+ <attribute name="bridge">
+ <ref name="deviceName"/>
+ </attribute>
+ <optional>
+ <attribute name="macTableManager">
+ <ref name="macTableManager"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="plugdirect">
+ <attribute name="type">
+ <value>direct</value>
+ </attribute>
+ <attribute name="dev">
+ <ref name="deviceName"/>
+ </attribute>
+ <attribute name="mode">
+ <choice>
+ <value>bridge</value>
+ <value>passthrough</value>
+ <value>private</value>
+ <value>vepa</value>
+ </choice>
+ </attribute>
+ </define>
+
+ <define name="plughostdevpci">
+ <attribute name="type">
+ <value>hostdev-pci</value>
+ </attribute>
+ <optional>
+ <attribute name="managed">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <element name="driver">
+ <attribute name="name">
+ <choice>
+ <value>kvm</value>
+ <value>vfio</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <element name="address">
+ <ref name="pciaddress"/>
+ </element>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/nodedev.rng b/src/main/resources/libvirt/rng/nodedev.rng
new file mode 100644
index 0000000..5840dc9
--- /dev/null
+++ b/src/main/resources/libvirt/rng/nodedev.rng
@@ -0,0 +1,765 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt node device XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <start>
+ <ref name="device"/>
+ </start>
+
+ <define name="device">
+ <element name="device">
+ <!-- The name of the network, used to refer to it through the API
+ and in virsh -->
+ <element name="name"><text/></element>
+ <optional>
+ <element name="path"><text/></element>
+ </optional>
+ <optional>
+ <element name="devnode">
+ <attribute name="type">
+ <value>dev</value>
+ </attribute>
+ <text/>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="devnode">
+ <attribute name="type">
+ <value>link</value>
+ </attribute>
+ <text/>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <ref name="parent"/>
+ </optional>
+
+ <optional>
+ <element name="driver">
+ <element name="name"><text/></element>
+ </element>
+ </optional>
+
+ <zeroOrMore>
+ <ref name="capability"/>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="parent">
+ <element name="parent">
+ <choice>
+ <group>
+ <attribute name="wwnn">
+ <ref name="wwn"/>
+ </attribute>
+ <attribute name="wwpn">
+ <ref name="wwn"/>
+ </attribute>
+ <empty/>
+ </group>
+ <group>
+ <attribute name="fabric_wwn">
+ <ref name="wwn"/>
+ </attribute>
+ <empty/>
+ </group>
+ <text/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="capability">
+ <element name="capability">
+ <choice>
+ <ref name="capsystem"/>
+ <ref name="cappcidev"/>
+ <ref name="capusbdev"/>
+ <ref name="capusbinterface"/>
+ <ref name="capnet"/>
+ <ref name="capscsihost"/>
+ <ref name="capscsitarget"/>
+ <ref name="capscsi"/>
+ <ref name="capstorage"/>
+ <ref name="capdrm"/>
+ <ref name="capmdev"/>
+ <ref name="capccwdev"/>
+ <ref name="capcssdev"/>
+ <ref name="capvdpa"/>
+ <ref name="capapcard"/>
+ <ref name="capapqueue"/>
+ <ref name="capapmatrix"/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="capsystem">
+ <attribute name="type">
+ <value>system</value>
+ </attribute>
+
+ <optional>
+ <element name="product"><text/></element>
+ </optional>
+
+ <element name="hardware">
+ <optional>
+ <element name="vendor"><text/></element>
+ </optional>
+ <optional>
+ <element name="version"><text/></element>
+ </optional>
+ <optional>
+ <element name="serial"><text/></element>
+ </optional>
+
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </element>
+
+
+ <element name="firmware">
+ <optional>
+ <element name="vendor"><text/></element>
+ </optional>
+ <optional>
+ <element name="version"><text/></element>
+ </optional>
+ <optional>
+ <element name="release_date"><text/></element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="cappcidev">
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+
+ <optional>
+ <element name="class">
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{6}</param>
+ </data>
+ </element>
+ </optional>
+ <element name="domain">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="bus">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="slot">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="function">
+ <ref name="unsignedLong"/>
+ </element>
+
+ <element name="product">
+ <attribute name="id">
+ <ref name="hexuint"/>
+ </attribute>
+
+ <choice>
+ <text/>
+ <empty/>
+ </choice>
+ </element>
+
+ <element name="vendor">
+ <attribute name="id">
+ <ref name="hexuint"/>
+ </attribute>
+
+ <choice>
+ <text/>
+ <empty/>
+ </choice>
+ </element>
+
+ <optional>
+ <element name="capability">
+ <attribute name="type">
+ <value>phys_function</value>
+ </attribute>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="capability">
+ <attribute name="type">
+ <value>virt_functions</value>
+ </attribute>
+ <optional>
+ <attribute name="maxCount">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <ref name="address"/>
+ </zeroOrMore>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="capability">
+ <attribute name="type">
+ <choice>
+ <value>pci-bridge</value>
+ <value>cardbus-bridge</value>
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+
+ <optional>
+ <ref name="mdev_types"/>
+ </optional>
+
+ <optional>
+ <element name="iommuGroup">
+ <attribute name="number">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <oneOrMore>
+ <ref name="address"/>
+ </oneOrMore>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="numa">
+ <optional>
+ <attribute name="node">
+ <data type="int"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+
+ <optional>
+ <element name="pci-express">
+ <zeroOrMore>
+ <element name="link">
+ <attribute name="validity">
+ <choice>
+ <value>cap</value>
+ <value>sta</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="port">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="speed">
+ <data type="string">
+ <param name="pattern">[0-9]+(.[0-9]+)?</param>
+ </data>
+ </attribute>
+ </optional>
+ <attribute name="width">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </define>
+
+ <define name="capusbdev">
+ <attribute name="type">
+ <value>usb_device</value>
+ </attribute>
+
+ <element name="bus">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="device">
+ <ref name="unsignedLong"/>
+ </element>
+
+ <element name="product">
+ <attribute name="id">
+ <ref name="hexuint"/>
+ </attribute>
+
+ <choice>
+ <text/>
+ <empty/>
+ </choice>
+ </element>
+
+ <element name="vendor">
+ <attribute name="id">
+ <ref name="hexuint"/>
+ </attribute>
+
+ <choice>
+ <text/>
+ <empty/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="capusbinterface">
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+
+ <element name="number">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="class">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="subclass">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="protocol">
+ <ref name="unsignedLong"/>
+ </element>
+
+ <optional>
+ <element name="description">
+ <text/>
+ </element>
+ </optional>
+ </define>
+
+ <define name="capnet">
+ <attribute name="type">
+ <value>net</value>
+ </attribute>
+
+ <element name="interface">
+ <text/>
+ </element>
+ <optional>
+ <element name="address">
+ <ref name="mac"/>
+ </element>
+ </optional>
+ <ref name="link-speed-state"/>
+
+ <zeroOrMore>
+ <element name="feature">
+ <attribute name="name">
+ <ref name="netfeaturename"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+
+ <zeroOrMore>
+ <ref name="subcapnet"/>
+ </zeroOrMore>
+ </define>
+
+ <define name="netfeaturename">
+ <data type="string">
+ <param name="pattern">[a-zA-Z\-_]+</param>
+ </data>
+ </define>
+
+ <define name="subcapnet">
+ <element name="capability">
+ <choice>
+ <ref name="subcapnet80203"/>
+ <ref name="subcapnet80211"/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="subcapnet80203">
+ <attribute name="type">
+ <value>80203</value>
+ </attribute>
+ </define>
+
+ <define name="subcapnet80211">
+ <attribute name="type">
+ <value>80211</value>
+ </attribute>
+ </define>
+
+ <define name="capsfchost">
+ <attribute name="type">
+ <value>fc_host</value>
+ </attribute>
+
+ <element name="wwnn">
+ <ref name="wwn"/>
+ </element>
+
+ <element name="wwpn">
+ <ref name="wwn"/>
+ </element>
+
+ <optional>
+ <element name="fabric_wwn">
+ <ref name="wwn"/>
+ </element>
+ </optional>
+ </define>
+
+ <define name="capsvports">
+ <attribute name="type">
+ <value>vports_ops</value>
+ </attribute>
+ <element name="max_vports">
+ <ref name="unsignedInt"/>
+ </element>
+ <element name="vports">
+ <ref name="unsignedInt"/>
+ </element>
+ </define>
+
+ <define name="capscsihost">
+ <attribute name="type">
+ <value>scsi_host</value>
+ </attribute>
+
+ <element name="host">
+ <ref name="unsignedLong"/>
+ </element>
+
+ <optional>
+ <element name="unique_id">
+ <ref name="positiveInteger"/>
+ </element>
+ </optional>
+
+ <optional>
+ <zeroOrMore>
+ <element name="capability">
+ <choice>
+ <ref name="capsfchost"/>
+ <ref name="capsvports"/>
+ </choice>
+ </element>
+ </zeroOrMore>
+ </optional>
+ </define>
+
+ <define name="capsfcrport">
+ <attribute name="type">
+ <value>fc_remote_port</value>
+ </attribute>
+
+ <element name="rport">
+ <text/>
+ </element>
+
+ <element name="wwpn">
+ <ref name="wwn"/>
+ </element>
+ </define>
+
+ <define name="capscsitarget">
+ <attribute name="type">
+ <value>scsi_target</value>
+ </attribute>
+
+ <element name="target">
+ <text/>
+ </element>
+
+ <optional>
+ <element name="capability">
+ <ref name="capsfcrport"/>
+ </element>
+ </optional>
+ </define>
+
+ <define name="capscsi">
+ <attribute name="type">
+ <value>scsi</value>
+ </attribute>
+
+ <element name="host">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="bus">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="target">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="lun">
+ <ref name="unsignedLong"/>
+ </element>
+
+ <element name="type">
+ <text/>
+ </element>
+ </define>
+
+ <define name="capstorage">
+ <attribute name="type">
+ <value>storage</value>
+ </attribute>
+
+ <element name="block">
+ <ref name="path"/>
+ </element>
+
+ <optional>
+ <element name="bus">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="drive_type">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="model">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="vendor">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="serial">
+ <text/>
+ </element>
+ </optional>
+
+ <choice>
+ <ref name="capstorageremoveable"/>
+ <ref name="capstoragefixed"/>
+ </choice>
+
+ <optional>
+ <element name="capability">
+ <attribute name="type">
+ <value>hotpluggable</value>
+ </attribute>
+ </element>
+ </optional>
+ </define>
+
+ <define name="capstorageremoveable">
+ <element name="capability">
+ <attribute name="type">
+ <value>removable</value>
+ </attribute>
+ <element name="media_available">
+ <choice>
+ <value>1</value>
+ <value>0</value>
+ </choice>
+ </element>
+
+ <element name="media_size">
+ <ref name="unsignedLong"/>
+ </element>
+ <optional>
+ <element name="media_label">
+ <text/>
+ </element>
+ </optional>
+ <ref name="blockData"/>
+ </element>
+ </define>
+
+ <define name="capstoragefixed">
+ <element name="size">
+ <ref name="unsignedLong"/>
+ </element>
+ <ref name="blockData"/>
+ </define>
+
+ <define name="blockData">
+ <optional>
+ <element name="logical_block_size">
+ <ref name="unsignedLong"/>
+ </element>
+ <element name="num_blocks">
+ <ref name="unsignedLong"/>
+ </element>
+ </optional>
+ </define>
+
+ <define name="capdrm">
+ <attribute name="type">
+ <value>drm</value>
+ </attribute>
+ <element name="type">
+ <choice>
+ <value>primary</value>
+ <value>control</value>
+ <value>render</value>
+ </choice>
+ </element>
+ </define>
+
+ <define name="capmdev">
+ <attribute name="type">
+ <value>mdev</value>
+ </attribute>
+ <element name="type">
+ <attribute name="id">
+ <data type="string"/>
+ </attribute>
+ </element>
+ <optional>
+ <element name="iommuGroup">
+ <attribute name="number">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="attr">
+ <attribute name="name"/>
+ <attribute name="value"/>
+ </element>
+ </zeroOrMore>
+ </define>
+
+ <define name="capccwdev">
+ <attribute name="type">
+ <value>ccw</value>
+ </attribute>
+ <element name="cssid">
+ <ref name="ccwCssidRange"/>
+ </element>
+ <element name="ssid">
+ <ref name="ccwSsidRange"/>
+ </element>
+ <element name="devno">
+ <ref name="ccwDevnoRange"/>
+ </element>
+ </define>
+
+ <define name="capcssdev">
+ <attribute name="type">
+ <value>css</value>
+ </attribute>
+ <element name="cssid">
+ <ref name="ccwCssidRange"/>
+ </element>
+ <element name="ssid">
+ <ref name="ccwSsidRange"/>
+ </element>
+ <element name="devno">
+ <ref name="ccwDevnoRange"/>
+ </element>
+ <optional>
+ <ref name="mdev_types"/>
+ </optional>
+ </define>
+
+ <define name="capvdpa">
+ <attribute name="type">
+ <value>vdpa</value>
+ </attribute>
+ <element name="chardev">
+ <ref name="path"/>
+ </element>
+ </define>
+
+ <define name="capapcard">
+ <attribute name="type">
+ <value>ap_card</value>
+ </attribute>
+ <element name="ap-adapter">
+ <ref name="uint8"/>
+ </element>
+ </define>
+
+ <define name="capapqueue">
+ <attribute name="type">
+ <value>ap_queue</value>
+ </attribute>
+ <element name="ap-adapter">
+ <ref name="uint8"/>
+ </element>
+ <element name="ap-domain">
+ <ref name="apDomainRange"/>
+ </element>
+ </define>
+
+ <define name='capapmatrix'>
+ <attribute name='type'>
+ <value>ap_matrix</value>
+ </attribute>
+ <optional>
+ <ref name="mdev_types"/>
+ </optional>
+ </define>
+
+ <define name="address">
+ <element name="address">
+ <attribute name="domain"><ref name="hexuint"/></attribute>
+ <attribute name="bus"><ref name="hexuint"/></attribute>
+ <attribute name="slot"><ref name="hexuint"/></attribute>
+ <attribute name="function"><ref name="hexuint"/></attribute>
+ </element>
+ </define>
+
+ <define name="mac">
+ <data type="string">
+ <param name="pattern">([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}</param>
+ </data>
+ </define>
+
+ <define name="path">
+ <data type="string">
+ <param name="pattern">/[a-zA-Z0-9_\+\-/%]+</param>
+ </data>
+ </define>
+
+ <define name="mdev_types">
+ <element name="capability">
+ <attribute name="type">
+ <value>mdev_types</value>
+ </attribute>
+ <oneOrMore>
+ <element name="type">
+ <attribute name="id">
+ <data type="string"/>
+ </attribute>
+ <optional>
+ <element name="name"><text/></element>
+ </optional>
+ <element name="deviceAPI">
+ <choice>
+ <value>vfio-pci</value>
+ <value>vfio-ccw</value>
+ <value>vfio-ap</value>
+ </choice>
+ </element>
+ <element name="availableInstances">
+ <ref name="unsignedInt"/>
+ </element>
+ </element>
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="apDomainRange">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,4}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">255</param>
+ </data>
+ </choice>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/nwfilter.rng b/src/main/resources/libvirt/rng/nwfilter.rng
new file mode 100644
index 0000000..a75de7e
--- /dev/null
+++ b/src/main/resources/libvirt/rng/nwfilter.rng
@@ -0,0 +1,986 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <include href="nwfilter_params.rng"/>
+ <start>
+ <ref name="filter"/>
+ </start>
+ <define name="filter">
+ <element name="filter">
+ <ref name="filter-node-attributes"/>
+ <optional>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <element name="filterref">
+ <ref name="filterref-node-attributes"/>
+ </element>
+ <element name="rule">
+ <ref name="rule-node-attributes"/>
+ <choice>
+ <empty/>
+ <element name="mac">
+ <ref name="match-attribute"/>
+ <ref name="common-l2-attributes"/>
+ <ref name="mac-attributes"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="vlan">
+ <ref name="match-attribute"/>
+ <ref name="common-l2-attributes"/>
+ <ref name="vlan-attributes"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="stp">
+ <ref name="match-attribute"/>
+ <ref name="srcmacandmask-attributes"/>
+ <ref name="stp-attributes"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="arp">
+ <ref name="match-attribute"/>
+ <ref name="common-l2-attributes"/>
+ <ref name="arp-attributes"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="rarp">
+ <ref name="match-attribute"/>
+ <ref name="common-l2-attributes"/>
+ <ref name="arp-attributes"/> <!-- same as arp -->
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="ip">
+ <ref name="match-attribute"/>
+ <ref name="common-l2-attributes"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-port-attributes"/>
+ <ref name="ip-attributes"/>
+ <ref name="dscp-attribute"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="ipv6">
+ <ref name="match-attribute"/>
+ <ref name="common-l2-attributes"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-port-attributes"/>
+ <ref name="ip-attributes"/>
+ <ref name="icmp-attribute-ranges"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="tcp">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-port-attributes"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="tcp-attributes"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="udp">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-port-attributes"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="sctp">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-port-attributes"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="icmp">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="icmp-attributes"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="igmp">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="all">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="esp">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="ah">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="udplite">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ip-attributes-p1"/>
+ <ref name="common-ip-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="tcp-ipv6">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-port-attributes"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-ipv6-attributes-p2"/>
+ <ref name="tcp-attributes"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="udp-ipv6">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-port-attributes"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-ipv6-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="sctp-ipv6">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-port-attributes"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-ipv6-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="icmpv6">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-ipv6-attributes-p2"/>
+ <ref name="icmp-attributes"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="all-ipv6">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-ipv6-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="esp-ipv6">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-ipv6-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="ah-ipv6">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-ipv6-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ <element name="udplite-ipv6">
+ <ref name="match-attribute"/>
+ <ref name="srcmac-attribute"/>
+ <ref name="common-ipv6-attributes-p1"/>
+ <ref name="common-ipv6-attributes-p2"/>
+ <ref name="comment-attribute"/>
+ </element>
+ </choice>
+ </element>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <!-- ########### attributes of XML nodes ############ -->
+
+ <define name="filter-node-attributes">
+ <attribute name="name">
+ <data type="NCName"/>
+ </attribute>
+ <optional>
+ <attribute name="chain">
+ <choice>
+ <value>root</value>
+ <data type="string">
+ <param name="pattern">mac[a-zA-Z0-9_\.:\-]{0,9}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">stp[a-zA-Z0-9_\.:\-]{0,9}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">vlan[a-zA-Z0-9_\.:\-]{0,8}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">arp[a-zA-Z0-9_\.:\-]{0,9}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">rarp[a-zA-Z0-9_\.:\-]{0,8}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">ipv4[a-zA-Z0-9_\.:\-]{0,8}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">ipv6[a-zA-Z0-9_\.:\-]{0,8}</param>
+ </data>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="priority">
+ <ref name="priority-type"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="rule-node-attributes">
+ <attribute name="action">
+ <ref name="action-type"/>
+ </attribute>
+ <attribute name="direction">
+ <ref name="direction-type"/>
+ </attribute>
+ <optional>
+ <attribute name="priority">
+ <ref name="priority-type"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="statematch">
+ <ref name="statematch-type"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="match-attribute">
+ <interleave>
+ <optional>
+ <attribute name="match">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="srcmac-attribute">
+ <interleave>
+ <optional>
+ <attribute name="srcmacaddr">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="srcmacandmask-attributes">
+ <interleave>
+ <ref name="srcmac-attribute"/>
+ <optional>
+ <attribute name="srcmacmask">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="common-l2-attributes">
+ <interleave>
+ <ref name="srcmacandmask-attributes"/>
+ <optional>
+ <attribute name="dstmacaddr">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstmacmask">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="common-ip-attributes-p1">
+ <interleave>
+ <optional>
+ <attribute name="srcipaddr">
+ <ref name="addrIP"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="srcipmask">
+ <ref name="addrMask"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstipaddr">
+ <ref name="addrIP"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstipmask">
+ <ref name="addrMask"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="common-ip-attributes-p2">
+ <interleave>
+ <optional>
+ <attribute name="srcipfrom">
+ <ref name="addrIP"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="srcipto">
+ <ref name="addrIP"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstipfrom">
+ <ref name="addrIP"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstipto">
+ <ref name="addrIP"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dscp">
+ <ref name="sixbitrange"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="connlimit-above">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="state">
+ <ref name="stateflags-type"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ipset">
+ <ref name="ipset-name-type"/>
+ </attribute>
+ <attribute name="ipsetflags">
+ <ref name="ipset-flags-type"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="common-ipv6-attributes-p1">
+ <interleave>
+ <optional>
+ <attribute name="srcipaddr">
+ <ref name="addrIPv6"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="srcipmask">
+ <ref name="addrMaskv6"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstipaddr">
+ <ref name="addrIPv6"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstipmask">
+ <ref name="addrMaskv6"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="common-ipv6-attributes-p2">
+ <interleave>
+ <optional>
+ <attribute name="srcipfrom">
+ <ref name="addrIPv6"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="srcipto">
+ <ref name="addrIPv6"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstipfrom">
+ <ref name="addrIPv6"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstipto">
+ <ref name="addrIPv6"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dscp">
+ <ref name="sixbitrange"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="common-port-attributes">
+ <interleave>
+ <optional>
+ <attribute name="srcportstart">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="srcportend">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstportstart">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dstportend">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="icmp-attributes">
+ <interleave>
+ <optional>
+ <attribute name="type">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="code">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="icmp-attribute-ranges">
+ <interleave>
+ <optional>
+ <attribute name="type">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="typeend">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="code">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="codeend">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="mac-attributes">
+ <interleave>
+ <optional>
+ <attribute name="protocolid">
+ <ref name="mac-protocolid"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="vlan-attributes">
+ <interleave>
+ <optional>
+ <attribute name="vlanid">
+ <ref name="vlan-vlanid"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="encap-protocol">
+ <ref name="mac-protocolid"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="stp-attributes">
+ <optional>
+ <attribute name="type">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="flags">
+ <ref name="uint8"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="root-priority">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="root-priority-hi">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="root-address">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="root-address-mask">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="root-cost">
+ <ref name="uint32range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="root-cost-hi">
+ <ref name="uint32range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="sender-priority">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="sender-priority-hi">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="sender-address">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="sender-address-mask">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="port">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="port-hi">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="age">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="age-hi">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="max-age">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="max-age-hi">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="hello-time">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="hello-time-hi">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="forward-delay">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="forward-delay-hi">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="arp-attributes">
+ <interleave>
+ <optional>
+ <attribute name="arpsrcmacaddr">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="arpsrcipaddr">
+ <ref name="addrIP"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="arpdstmacaddr">
+ <ref name="addrMAC"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="arpdstipaddr">
+ <ref name="addrIP"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="hwtype">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="opcode">
+ <ref name="arpOpcodeType"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="protocoltype">
+ <ref name="uint16range"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="gratuitous">
+ <ref name="boolean"/>
+ </attribute>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="ip-attributes">
+ <optional>
+ <attribute name="protocol">
+ <ref name="ipProtocolType"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="dscp-attribute">
+ <optional>
+ <attribute name="dscp">
+ <ref name="sixbitrange"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="comment-attribute">
+ <optional>
+ <attribute name="comment">
+ <ref name="comment-type"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="tcp-attributes">
+ <optional>
+ <attribute name="flags">
+ <ref name="tcpflags-type"/>
+ </attribute>
+ </optional>
+ </define>
+
+ <!-- ################ type library ################ -->
+
+ <define name="variable-name-type">
+ <data type="string">
+ <param name="pattern">$[ ]*[a-zA-Z0-9_]+(\[[ ]*[@]?[0-9]+[ ]*\])?[ ]*</param>
+ </data>
+ </define>
+
+ <define name="addrMAC">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="string">
+ <param name="pattern">([a-fA-F0-9]{1,2}:){5}[a-fA-F0-9]{1,2}</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="addrIP">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="string">
+ <param name="pattern">([0-2]?[0-9]?[0-9]\.){3}[0-2]?[0-9]?[0-9]</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="addrIPv6">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="string">
+ <param name="pattern">([a-fA-F0-9]{0,4}:){2,7}([a-fA-F0-9]*)(([0-2]?[0-9]?[0-9]\.){3}[0-2]?[0-9]?[0-9])?</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="addrMask">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">32</param>
+ </data>
+
+ <data type="string">
+ <param name="pattern">([0-2]?[0-9]?[0-9]\.){3}[0-2]?[0-9]?[0-9]</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="addrMaskv6">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">128</param>
+ </data>
+
+ <data type="string">
+ <param name="pattern">([a-fA-F0-9]{0,4}:){2,7}([a-fA-F0-9]*)</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="sixbitrange">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x([0-3][0-9a-fA-F]|[0-9a-fA-F])</param>
+ </data>
+
+ <ref name="variable-name-type"/>
+
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">63</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="mac-protocolid">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="string">
+ <param name="pattern">0x([6-9a-fA-F][0-9a-fA-F]{2}|[0-9a-fA-F]{4})</param>
+ </data>
+
+ <data type="int">
+ <param name="minInclusive">1536</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+
+ <choice>
+ <value>arp</value>
+ <value>rarp</value>
+ <value>ipv4</value>
+ <value>ipv6</value>
+ <value>vlan</value>
+ </choice>
+ </choice>
+ </define>
+
+ <define name="vlan-vlanid">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="string">
+ <param name="pattern">0x([0-9a-fA-F]{1,3})</param>
+ </data>
+
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">4095</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="uint16range">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,4}</param>
+ </data>
+
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="uint32range">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,8}</param>
+ </data>
+
+ <data type="unsignedInt"/>
+ </choice>
+ </define>
+
+ <define name="boolean">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ <value>true</value>
+ <value>false</value>
+ <value>1</value>
+ <value>0</value>
+ </choice>
+ </define>
+
+ <define name="arpOpcodeType">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+
+ <data type="string">
+ <param name="pattern">([Rr]eply|[Rr]equest|[Rr]equest_[Rr]everse|[Rr]eply_[Rr]everse|DRARP_[Rr]equest|DRARP_[Rr]eply|DRARP_[Ee]rror|InARP_[Rr]equest|ARP_NAK)</param>
+ </data>
+
+ </choice>
+ </define>
+
+ <define name="ipProtocolType">
+ <choice>
+ <ref name="variable-name-type"/>
+
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,2}</param>
+ </data>
+
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">255</param>
+ </data>
+
+ <choice>
+ <value>tcp</value>
+ <value>udp</value>
+ <value>udplite</value>
+ <value>esp</value>
+ <value>ah</value>
+ <value>icmp</value>
+ <value>igmp</value>
+ <value>sctp</value>
+ <value>icmpv6</value>
+ </choice>
+ </choice>
+ </define>
+
+ <define name="action-type">
+ <choice>
+ <value>drop</value>
+ <value>accept</value>
+ <value>reject</value>
+ <value>continue</value>
+ <value>return</value>
+ </choice>
+ </define>
+
+ <define name="direction-type">
+ <choice>
+ <value>in</value>
+ <value>out</value>
+ <value>inout</value>
+ </choice>
+ </define>
+
+ <define name="priority-type">
+ <data type="int">
+ <param name="minInclusive">-1000</param>
+ <param name="maxInclusive">1000</param>
+ </data>
+ </define>
+ <define name="statematch-type">
+ <data type="string">
+ <param name="pattern">([Ff][Aa][Ll][Ss][Ee]|0)</param>
+ </data>
+ </define>
+
+ <define name="comment-type">
+ <data type="string"/>
+ </define>
+
+ <define name="stateflags-type">
+ <data type="string">
+ <param name="pattern">((NEW|ESTABLISHED|RELATED|INVALID)(,(NEW|ESTABLISHED|RELATED|INVALID))*|NONE)</param>
+ </data>
+ </define>
+
+ <define name="tcpflags-type">
+ <data type="string">
+ <param name="pattern">((SYN|ACK|URG|PSH|FIN|RST)(,(SYN|ACK|URG|PSH|FIN|RST))*|ALL|NONE)/((SYN|ACK|URG|PSH|FIN|RST)(,(SYN|ACK|URG|PSH|FIN|RST))*|ALL|NONE)</param>
+ </data>
+ </define>
+
+ <define name="ipset-name-type">
+ <choice>
+ <ref name="variable-name-type"/>
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\.:\-\+ ]{1,31}</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name="ipset-flags-type">
+ <data type="string">
+ <param name="pattern">([Ss][Rr][Cc]|[Dd][Ss][Tt])(,([Ss][Rr][Cc]|[Dd][Ss][Tt])){0,5}</param>
+ </data>
+ </define>
+</grammar>
diff --git a/src/main/resources/libvirt/rng/nwfilter_params.rng b/src/main/resources/libvirt/rng/nwfilter_params.rng
new file mode 100644
index 0000000..a3e7b35
--- /dev/null
+++ b/src/main/resources/libvirt/rng/nwfilter_params.rng
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!-- network-related definitions used in multiple grammars -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <define name="filterref-node-attributes">
+ <attribute name="filter">
+ <data type="NCName"/>
+ </attribute>
+ <zeroOrMore>
+ <element name="parameter">
+ <attribute name="name">
+ <ref name="filter-param-name"/>
+ </attribute>
+ <attribute name="value">
+ <ref name="filter-param-value"/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </define>
+
+ <define name="filter-param-name">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_]+</param>
+ </data>
+ </define>
+ <define name="filter-param-value">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\.:]+</param>
+ </data>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/nwfilterbinding.rng b/src/main/resources/libvirt/rng/nwfilterbinding.rng
new file mode 100644
index 0000000..a0a956e
--- /dev/null
+++ b/src/main/resources/libvirt/rng/nwfilterbinding.rng
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <!-- domain-related definitions used in multiple grammars -->
+ <include href="basictypes.rng"/>
+ <include href="nwfilter_params.rng"/>
+
+ <start>
+ <ref name="filterbinding"/>
+ </start>
+
+ <define name="filterbinding">
+ <element name="filterbinding">
+ <interleave>
+ <element name="owner">
+ <element name="name">
+ <text/>
+ </element>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </element>
+
+ <element name="portdev">
+ <attribute name="name"/>
+ <empty/>
+ </element>
+
+ <optional>
+ <element name="linkdev">
+ <attribute name="name"/>
+ <empty/>
+ </element>
+ </optional>
+
+ <element name="mac">
+ <attribute name="address">
+ <ref name="uniMacAddr"/>
+ </attribute>
+ <empty/>
+ </element>
+
+ <element name="filterref">
+ <ref name="filterref-node-attributes"/>
+ </element>
+ </interleave>
+ </element>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/secret.rng b/src/main/resources/libvirt/rng/secret.rng
new file mode 100644
index 0000000..c90e2eb
--- /dev/null
+++ b/src/main/resources/libvirt/rng/secret.rng
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt secret properties XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <ref name="secret"/>
+ </start>
+
+ <include href="basictypes.rng"/>
+
+ <define name="secret">
+ <element name="secret">
+ <optional>
+ <attribute name="ephemeral">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="private">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <interleave>
+ <optional>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="description">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name="usage">
+ <choice>
+ <ref name="usagevolume"/>
+ <ref name="usageceph"/>
+ <ref name="usageiscsi"/>
+ <ref name="usagetls"/>
+ <ref name="usagevtpm"/>
+ <!-- More choices later -->
+ </choice>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="usagevolume">
+ <attribute name="type">
+ <value>volume</value>
+ </attribute>
+ <element name="volume">
+ <ref name="absFilePath"/>
+ </element>
+ </define>
+
+ <define name="usageceph">
+ <attribute name="type">
+ <value>ceph</value>
+ </attribute>
+ <element name="name">
+ <ref name="objectName"/>
+ </element>
+ </define>
+
+ <define name="usageiscsi">
+ <attribute name="type">
+ <value>iscsi</value>
+ </attribute>
+ <element name="target">
+ <ref name="objectName"/>
+ </element>
+ </define>
+
+ <define name="usagetls">
+ <attribute name="type">
+ <value>tls</value>
+ </attribute>
+ <element name="name">
+ <ref name="objectName"/>
+ </element>
+ </define>
+
+ <define name="usagevtpm">
+ <attribute name="type">
+ <value>vtpm</value>
+ </attribute>
+ <element name="name">
+ <ref name="objectName"/>
+ </element>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/storagecommon.rng b/src/main/resources/libvirt/rng/storagecommon.rng
new file mode 100644
index 0000000..e3d08a8
--- /dev/null
+++ b/src/main/resources/libvirt/rng/storagecommon.rng
@@ -0,0 +1,238 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for common libvirt XML storage elements -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <!-- This schema is not designed for standalone use; another file
+ must include both this file and basictypes.rng -->
+
+ <define name="encryption">
+ <element name="encryption">
+ <attribute name="format">
+ <choice>
+ <value>default</value>
+ <value>qcow</value>
+ <value>luks</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <ref name="secret"/>
+ <optional>
+ <element name="cipher">
+ <ref name="keycipher"/>
+ </element>
+ <element name="ivgen">
+ <ref name="keyivgen"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="initiatorinfo">
+ <element name="initiator">
+ <element name="iqn">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <empty/>
+ </element>
+ </element>
+ </define>
+
+ <define name="reconnect">
+ <element name="reconnect">
+ <attribute name="enabled">
+ <ref name="virYesNo"/>
+ </attribute>
+ <optional>
+ <attribute name="timeout">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="unixSocketSource">
+ <element name="source">
+ <attribute name="type">
+ <value>unix</value>
+ </attribute>
+ <attribute name="path">
+ <ref name="absFilePath"/>
+ </attribute>
+ <attribute name="mode">
+ <choice>
+ <value>server</value>
+ <value>client</value>
+ </choice>
+ </attribute>
+ <optional>
+ <ref name="reconnect"/>
+ </optional>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="reservations">
+ <element name="reservations">
+ <optional>
+ <attribute name="managed">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ <optional>
+ <ref name="unixSocketSource"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="secret">
+ <element name="secret">
+ <attribute name="type">
+ <value>passphrase</value>
+ </attribute>
+ <choice>
+ <attribute name="uuid">
+ <ref name="UUID"/>
+ </attribute>
+ <attribute name="usage">
+ <text/>
+ </attribute>
+ </choice>
+ </element>
+ </define>
+
+ <define name="compat">
+ <element name="compat">
+ <data type="string">
+ <param name="pattern">[0-9]+\.[0-9]+</param>
+ </data>
+ </element>
+ </define>
+ <define name="fileFormatFeatures">
+ <element name="features">
+ <interleave>
+ <optional>
+ <element name="lazy_refcounts">
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <!-- split the list of known storage formats into two, those where
+ we know how to follow backing chains, and all others -->
+ <define name="storageFormatBacking">
+ <choice>
+ <value>cow</value>
+ <value>qcow</value>
+ <value>qcow2</value>
+ <value>qed</value>
+ <value>vmdk</value>
+ </choice>
+ </define>
+ <define name="storageFormat">
+ <choice>
+ <value>raw</value>
+ <value>dir</value>
+ <value>bochs</value>
+ <value>cloop</value>
+ <value>dmg</value>
+ <value>iso</value>
+ <value>vpc</value>
+ <value>vdi</value>
+ <value>fat</value>
+ <value>vhd</value>
+ <value>ploop</value>
+ <value>luks</value>
+ <ref name="storageFormatBacking"/>
+ </choice>
+ </define>
+
+ <define name="storageStartupPolicy">
+ <!-- Use a combine="choice" override in client files that want to
+ add additional attributes to a <source> sub-element
+ associated with a storage source -->
+ <notAllowed/>
+ </define>
+
+ <define name="storageSourceExtra">
+ <!-- Use a combine="choice" override in client files that want to
+ add additional elements as siblings of a <source> sub-element
+ associated with a storage source -->
+ <notAllowed/>
+ </define>
+
+ <define name="permissions">
+ <optional>
+ <element name="permissions">
+ <interleave>
+ <optional>
+ <element name="mode">
+ <ref name="octalMode"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="owner">
+ <choice>
+ <ref name="unsignedInt"/>
+ <value>-1</value>
+ </choice>
+ </element>
+ </optional>
+ <optional>
+ <element name="group">
+ <choice>
+ <ref name="unsignedInt"/>
+ <value>-1</value>
+ </choice>
+ </element>
+ </optional>
+ <optional>
+ <element name="label">
+ <text/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="keycipher">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <attribute name="size">
+ <ref name="unsignedInt"/>
+ </attribute>
+ <optional>
+ <attribute name="mode">
+ <text/>
+ </attribute>
+ <attribute name="hash">
+ <text/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="keyivgen">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="hash">
+ <text/>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="refreshVolumeAllocation">
+ <choice>
+ <value>default</value>
+ <value>capacity</value>
+ </choice>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/storagepool.rng b/src/main/resources/libvirt/rng/storagepool.rng
new file mode 100644
index 0000000..bd24b8b
--- /dev/null
+++ b/src/main/resources/libvirt/rng/storagepool.rng
@@ -0,0 +1,788 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt storage pool XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <include href="storagecommon.rng"/>
+ <start>
+ <ref name="pool"/>
+ </start>
+
+
+ <define name="pool">
+ <element name="pool">
+ <choice>
+ <ref name="pooldir"/>
+ <ref name="poolfs"/>
+ <ref name="poolnetfs"/>
+ <ref name="poollogical"/>
+ <ref name="pooldisk"/>
+ <ref name="pooliscsi"/>
+ <ref name="pooliscsidirect"/>
+ <ref name="poolscsi"/>
+ <ref name="poolmpath"/>
+ <ref name="poolrbd"/>
+ <ref name="poolsheepdog"/>
+ <ref name="poolgluster"/>
+ <ref name="poolzfs"/>
+ <ref name="poolvstorage"/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="pooldir">
+ <attribute name="type">
+ <value>dir</value>
+ </attribute>
+ <interleave>
+ <ref name="commonmetadata"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcedir"/>
+ <ref name="target"/>
+ </interleave>
+ </define>
+
+ <define name="poolfs">
+ <attribute name="type">
+ <value>fs</value>
+ </attribute>
+ <interleave>
+ <ref name="commonmetadata"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcefs"/>
+ <ref name="target"/>
+ </interleave>
+ <optional>
+ <ref name="fs_mount_opts"/>
+ </optional>
+ </define>
+
+ <define name="poolnetfs">
+ <attribute name="type">
+ <value>netfs</value>
+ </attribute>
+ <interleave>
+ <ref name="commonmetadata"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcenetfs"/>
+ <ref name="target"/>
+ </interleave>
+ <optional>
+ <ref name="fs_mount_opts"/>
+ </optional>
+ </define>
+
+ <define name="poollogical">
+ <attribute name="type">
+ <value>logical</value>
+ </attribute>
+ <interleave>
+ <ref name="commonMetadataNameOptional"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcelogical"/>
+ <ref name="targetlogical"/>
+ </interleave>
+ </define>
+
+ <define name="pooldisk">
+ <attribute name="type">
+ <value>disk</value>
+ </attribute>
+ <interleave>
+ <ref name="commonmetadata"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcedisk"/>
+ <ref name="target"/>
+ </interleave>
+ </define>
+
+ <define name="pooliscsi">
+ <attribute name="type">
+ <value>iscsi</value>
+ </attribute>
+ <interleave>
+ <ref name="commonmetadata"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourceiscsi"/>
+ <ref name="target"/>
+ </interleave>
+ </define>
+
+ <define name="pooliscsidirect">
+ <attribute name="type">
+ <value>iscsi-direct</value>
+ </attribute>
+ <interleave>
+ <ref name="commonmetadata"/>
+ <optional>
+ <ref name="sizing"/>
+ </optional>
+ <ref name="features"/>
+ <ref name="sourceiscsidirect"/>
+ </interleave>
+ </define>
+
+ <define name="poolscsi">
+ <attribute name="type">
+ <value>scsi</value>
+ </attribute>
+ <interleave>
+ <ref name="commonmetadata"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcescsi"/>
+ <ref name="target"/>
+ </interleave>
+ </define>
+
+ <define name="poolmpath">
+ <attribute name="type">
+ <value>mpath</value>
+ </attribute>
+ <interleave>
+ <ref name="commonmetadata"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <optional>
+ <ref name="sourcempath"/>
+ </optional>
+ <ref name="target"/>
+ </interleave>
+ </define>
+
+ <define name="poolrbd">
+ <attribute name="type">
+ <value>rbd</value>
+ </attribute>
+ <interleave>
+ <ref name="commonMetadataNameOptional"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcerbd"/>
+ <ref name="refresh"/>
+ </interleave>
+ <optional>
+ <ref name="rbd_config_opts"/>
+ </optional>
+ </define>
+
+ <define name="poolsheepdog">
+ <attribute name="type">
+ <value>sheepdog</value>
+ </attribute>
+ <interleave>
+ <ref name="commonMetadataNameOptional"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcesheepdog"/>
+ </interleave>
+ </define>
+
+ <define name="poolgluster">
+ <attribute name="type">
+ <value>gluster</value>
+ </attribute>
+ <interleave>
+ <ref name="commonMetadataNameOptional"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcegluster"/>
+ </interleave>
+ </define>
+
+ <define name="poolzfs">
+ <attribute name="type">
+ <value>zfs</value>
+ </attribute>
+ <interleave>
+ <ref name="commonMetadataNameOptional"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcezfs"/>
+ <optional>
+ <ref name="target"/>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="poolvstorage">
+ <attribute name="type">
+ <value>vstorage</value>
+ </attribute>
+ <interleave>
+ <ref name="commonMetadataNameOptional"/>
+ <ref name="sizing"/>
+ <ref name="features"/>
+ <ref name="sourcevstorage"/>
+ <ref name="target"/>
+ </interleave>
+ </define>
+
+ <define name="sourceinfovendor">
+ <interleave>
+ <optional>
+ <element name="vendor">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="product">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="commonMetadataNameOptional">
+ <interleave>
+ <optional>
+ <element name="name">
+ <ref name="objectName"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="commonmetadata">
+ <interleave>
+ <element name="name">
+ <ref name="objectName"/>
+ </element>
+ <optional>
+ <element name="uuid">
+ <ref name="UUID"/>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="sizing">
+ <interleave>
+ <optional>
+ <element name="capacity">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="allocation">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="available">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="features">
+ <optional>
+ <element name="features">
+ <interleave>
+ <optional>
+ <element name="cow">
+ <attribute name="state">
+ <ref name="virYesNo"/>
+ </attribute>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="target">
+ <element name="target">
+ <interleave>
+ <element name="path">
+ <ref name="absFilePath"/>
+ </element>
+ <ref name="permissions"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="targetlogical">
+ <element name="target">
+ <interleave>
+ <optional>
+ <element name="path">
+ <ref name="absFilePath"/>
+ </element>
+ </optional>
+ <ref name="permissions"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourceinfohost">
+ <oneOrMore>
+ <element name="host">
+ <attribute name="name">
+ <choice>
+ <ref name="dnsName"/>
+ <ref name="ipAddr"/>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="port">
+ <ref name="PortNumber"/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </oneOrMore>
+ </define>
+
+ <define name="sourceinfodev">
+ <element name="device">
+ <attribute name="path">
+ <choice>
+ <ref name="absFilePath"/>
+ <ref name="genericName"/>
+ <ref name="IscsiQualifiedName"/>
+ </choice>
+ </attribute>
+ <choice>
+ <empty/>
+ <ref name="devextents"/>
+ </choice>
+ <optional>
+ <attribute name="part_separator">
+ <ref name="virYesNo"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="sourceinfodeviscsidirect">
+ <element name="device">
+ <attribute name="path">
+ <ref name="IscsiQualifiedName"/>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="devextents">
+ <oneOrMore>
+ <element name="freeExtent">
+ <attribute name="start">
+ <ref name="unsignedLong"/>
+ </attribute>
+ <attribute name="end">
+ <ref name="unsignedLong"/>
+ </attribute>
+ </element>
+ </oneOrMore>
+ </define>
+
+ <define name="sourceinfodir">
+ <element name="dir">
+ <attribute name="path">
+ <ref name="absDirPath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="sourceinfonetrelativepath">
+ <element name="dir">
+ <attribute name="path">
+ <ref name="dirPath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </define>
+
+ <define name="sourceinfoname">
+ <element name="name">
+ <text/>
+ </element>
+ </define>
+
+ <define name="sourceinfoauth">
+ <element name="auth">
+ <attribute name="type">
+ <choice>
+ <value>chap</value>
+ <value>ceph</value>
+ </choice>
+ </attribute>
+ <attribute name="username">
+ <text/>
+ </attribute>
+ <ref name="sourceinfoauthsecret"/>
+ </element>
+ </define>
+
+ <define name="sourceinfoauthsecret">
+ <element name="secret">
+ <choice>
+ <attribute name="uuid">
+ <text/>
+ </attribute>
+ <attribute name="usage">
+ <text/>
+ </attribute>
+ </choice>
+ </element>
+ </define>
+
+ <define name="sourcezfs">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfoname"/>
+ <optional>
+ <ref name="sourceinfodev"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourcevstorage">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfoname"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourcefmtfs">
+ <optional>
+ <element name="format">
+ <attribute name="type">
+ <choice>
+ <value>auto</value>
+ <value>ext2</value>
+ <value>ext3</value>
+ <value>ext4</value>
+ <value>ufs</value>
+ <value>iso9660</value>
+ <value>udf</value>
+ <value>gfs</value>
+ <value>gfs2</value>
+ <value>vfat</value>
+ <value>hfs+</value>
+ <value>xfs</value>
+ <value>ocfs2</value>
+ <value>vmfs</value>
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+ </define>
+
+
+ <define name="sourcefmtnetfs">
+ <optional>
+ <element name="format">
+ <attribute name="type">
+ <choice>
+ <value>auto</value>
+ <value>nfs</value>
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+ </define>
+
+
+ <define name="sourcefmtdisk">
+ <optional>
+ <element name="format">
+ <attribute name="type">
+ <choice>
+ <value>unknown</value>
+ <value>dos</value>
+ <value>dvh</value>
+ <value>gpt</value>
+ <value>mac</value>
+ <value>bsd</value>
+ <value>pc98</value>
+ <value>sun</value>
+ <value>lvm2</value>
+ </choice>
+ </attribute>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </element>
+ </optional>
+ </define>
+
+
+ <define name="sourcefmtlogical">
+ <optional>
+ <element name="format">
+ <attribute name="type">
+ <choice>
+ <value>unknown</value> <!-- back-compat requires keeping "unknown" not "auto" -->
+ <value>lvm2</value>
+ </choice>
+ </attribute>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </element>
+ </optional>
+ </define>
+
+
+ <define name="sourcedir">
+ <optional>
+ <element name="source">
+ <empty/>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </element>
+ </optional>
+ </define>
+
+ <define name="sourcefs">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfodev"/>
+ <ref name="sourcefmtfs"/>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourcenetfs">
+ <element name="source">
+ <choice>
+ <group>
+ <interleave>
+ <ref name="sourceinfohost"/>
+ <ref name="sourceinfodir"/>
+ <ref name="sourcefmtnetfs"/>
+ <optional>
+ <element name="protocol">
+ <attribute name="ver">
+ <ref name="unsignedInt"/>
+ </attribute>
+ </element>
+ </optional>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </interleave>
+ </group>
+ <group>
+ <interleave>
+ <ref name="sourceinfohost"/>
+ <ref name="sourceinfonetrelativepath"/>
+ <element name="format">
+ <attribute name="type">
+ <choice>
+ <value>cifs</value>
+ <value>glusterfs</value>
+ </choice>
+ </attribute>
+ </element>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="sourcelogical">
+ <element name="source">
+ <interleave>
+ <oneOrMore>
+ <optional>
+ <ref name="sourceinfoname"/>
+ </optional>
+ <optional>
+ <ref name="sourceinfodev"/>
+ </optional>
+ </oneOrMore>
+ <ref name="sourcefmtlogical"/>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourcedisk">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfodev"/>
+ <ref name="sourcefmtdisk"/>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourceiscsi">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfohost"/>
+ <ref name="sourceinfodev"/>
+ <optional>
+ <ref name="initiatorinfo"/>
+ </optional>
+ <optional>
+ <ref name="sourceinfoauth"/>
+ </optional>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourceiscsidirect">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfohost"/>
+ <ref name="sourceinfodeviscsidirect"/>
+ <ref name="initiatorinfo"/>
+ <optional>
+ <ref name="sourceinfoauth"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourcescsi">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfoadapter"/>
+ <optional>
+ <ref name="sourceinfovendor"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourcempath">
+ <element name="source">
+ <empty/>
+ </element>
+ </define>
+
+ <define name="sourcerbd">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfoname"/>
+ <ref name="sourceinfohost"/>
+ <optional>
+ <ref name="sourceinfoauth"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourcesheepdog">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfohost"/>
+ <ref name="sourceinfoname"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sourcegluster">
+ <element name="source">
+ <interleave>
+ <ref name="sourceinfohost"/>
+ <ref name="sourceinfoname"/>
+ <optional>
+ <ref name="sourceinfodir"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="IscsiQualifiedName">
+ <data type="string">
+ <param name="pattern">iqn\.[0-9]{4}-(0[1-9]|1[0-2])\.[a-zA-Z0-9\.\-]+(:.+)?</param>
+ </data>
+ </define>
+
+ <define name="refresh">
+ <optional>
+ <element name="refresh">
+ <interleave>
+ <ref name="refreshVolume"/>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="refreshVolume">
+ <optional>
+ <element name="volume">
+ <optional>
+ <attribute name="allocation">
+ <ref name="refreshVolumeAllocation"/>
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ </define>
+
+ <!--
+ Optional storage pool extensions in their own namespace:
+ "fs" or "netfs"
+ -->
+
+ <define name="fs_mount_opts">
+ <element name="mount_opts" ns="http://libvirt.org/schemas/storagepool/fs/1.0">
+ <zeroOrMore>
+ <element name="option">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <!--
+ Optional storage pool extensions in their own namespace:
+ RBD
+ -->
+
+ <define name="rbd_config_opts">
+ <element name="config_opts" ns="http://libvirt.org/schemas/storagepool/rbd/1.0">
+ <zeroOrMore>
+ <element name="option">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <attribute name="value">
+ <text/>
+ </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/storagepoolcaps.rng b/src/main/resources/libvirt/rng/storagepoolcaps.rng
new file mode 100644
index 0000000..ec65fba
--- /dev/null
+++ b/src/main/resources/libvirt/rng/storagepoolcaps.rng
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt storage pool capabilities XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <start>
+ <ref name="storagepoolCapabilities"/>
+ </start>
+
+
+ <define name="storagepoolCapabilities">
+ <element name="storagepoolCapabilities">
+ <zeroOrMore>
+ <ref name="poolCapsType"/>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="poolCapsType">
+ <element name="pool">
+ <ref name="poolCapsTypes"/>
+ <ref name="poolCapsSupported"/>
+ <optional>
+ <ref name="poolCapsPoolOptions"/>
+ </optional>
+ <optional>
+ <ref name="poolCapsVolOptions"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="poolCapsTypes">
+ <attribute name="type">
+ <text/>
+ </attribute>
+ </define>
+
+ <define name="poolCapsSupported">
+ <attribute name="supported">
+ <ref name="virYesNo"/>
+ </attribute>
+ </define>
+
+ <define name="poolCapsPoolOptions">
+ <element name="poolOptions">
+ <optional>
+ <ref name="poolDefaultFormat"/>
+ </optional>
+ <optional>
+ <ref name="poolCapsEnum"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="poolCapsVolOptions">
+ <element name="volOptions">
+ <ref name="poolDefaultFormat"/>
+ <ref name="poolCapsEnum"/>
+ </element>
+ </define>
+
+ <define name="poolDefaultFormat">
+ <element name="defaultFormat">
+ <attribute name="type">
+ <text/>
+ </attribute>
+ </element>
+ </define>
+
+ <define name="poolCapsEnum">
+ <zeroOrMore>
+ <element name="enum">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <ref name="value"/>
+ </element>
+ </zeroOrMore>
+ </define>
+
+ <define name="value">
+ <zeroOrMore>
+ <element name="value">
+ <text/>
+ </element>
+ </zeroOrMore>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/rng/storagevol.rng b/src/main/resources/libvirt/rng/storagevol.rng
new file mode 100644
index 0000000..22ce5ea
--- /dev/null
+++ b/src/main/resources/libvirt/rng/storagevol.rng
@@ -0,0 +1,235 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt storage volume XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <include href="basictypes.rng"/>
+ <start>
+ <ref name="vol"/>
+ </start>
+
+ <include href="storagecommon.rng"/>
+
+
+ <define name="vol">
+ <element name="volume">
+ <optional>
+ <attribute name="type">
+ <choice>
+ <value>file</value>
+ <value>block</value>
+ <value>dir</value>
+ <value>network</value>
+ <value>netdir</value>
+ </choice>
+ </attribute>
+ </optional>
+ <interleave>
+ <element name="name">
+ <ref name="volName"/>
+ </element>
+ <optional>
+ <element name="key">
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="source"/>
+ </optional>
+ <ref name="sizing"/>
+ <ref name="target"/>
+ <optional>
+ <ref name="backingStore"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="sizing">
+ <interleave>
+ <optional>
+ <element name="capacity">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="allocation">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="physical">
+ <ref name="scaledInteger"/>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="timestamps">
+ <optional>
+ <element name="timestamps">
+ <interleave>
+ <optional>
+ <element name="atime">
+ <ref name="timestamp"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="btime">
+ <ref name="timestamp"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="ctime">
+ <ref name="timestamp"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="mtime">
+ <ref name="timestamp"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ </define>
+
+ <define name="timestamp">
+ <data type="string">
+ <param name="pattern">[0-9]+(\.[0-9]{0,9})?</param>
+ </data>
+ </define>
+
+ <define name="target">
+ <element name="target">
+ <interleave>
+ <optional>
+ <element name="path">
+ <choice>
+ <data type="anyURI"/>
+ <ref name="absFilePath"/>
+ </choice>
+ </element>
+ </optional>
+ <ref name="format"/>
+ <ref name="permissions"/>
+ <ref name="timestamps"/>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <optional>
+ <ref name="compat"/>
+ </optional>
+ <optional>
+ <element name="nocow">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="fileFormatFeatures"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="backingStore">
+ <element name="backingStore">
+ <interleave>
+ <element name="path">
+ <ref name="absFilePath"/>
+ </element>
+ <ref name="format"/>
+ <ref name="permissions"/>
+ <ref name="timestamps"/>
+ </interleave>
+ </element>
+ </define>
+
+
+ <define name="source">
+ <element name="source">
+ <zeroOrMore>
+ <ref name="sourcedev"/>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="sourcedev">
+ <element name="device">
+ <attribute name="path">
+ <ref name="absFilePath"/>
+ </attribute>
+ <choice>
+ <empty/>
+ <ref name="devextents"/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="devextents">
+ <oneOrMore>
+ <element name="extent">
+ <attribute name="start">
+ <ref name="unsignedLong"/>
+ </attribute>
+ <attribute name="end">
+ <ref name="unsignedLong"/>
+ </attribute>
+ </element>
+ </oneOrMore>
+ </define>
+
+ <define name="formatdev">
+ <choice>
+ <value>none</value>
+ <value>auto</value>
+ <value>ext2</value>
+ <value>ext3</value>
+ <value>ext4</value>
+ <value>ufs</value>
+ <value>iso9660</value>
+ <value>udf</value>
+ <value>gfs</value>
+ <value>gfs2</value>
+ <value>vfat</value>
+ <value>hfs+</value>
+ <value>xfs</value>
+ <value>ocfs2</value>
+ <value>vmfs</value>
+ </choice>
+ </define>
+
+ <define name="formatfile">
+ <choice>
+ <value>unknown</value>
+ <ref name="storageFormat"/>
+ </choice>
+ </define>
+
+ <define name="formatdisk">
+ <choice>
+ <value>none</value>
+ <value>linux</value>
+ <value>fat16</value>
+ <value>fat32</value>
+ <value>linux-swap</value>
+ <value>linux-lvm</value>
+ <value>linux-raid</value>
+ <value>extended</value>
+ </choice>
+ </define>
+
+ <define name="format">
+ <optional>
+ <element name="format">
+ <attribute name="type">
+ <choice>
+ <ref name="formatfile"/>
+ <ref name="formatdev"/>
+ <ref name="formatdisk"/>
+ </choice>
+ </attribute>
+ </element>
+ </optional>
+ </define>
+
+</grammar>
diff --git a/src/main/resources/libvirt/xsl/xml-output-transformation.xsl b/src/main/resources/libvirt/xsl/xml-output-transformation.xsl
new file mode 100644
index 0000000..febed54
--- /dev/null
+++ b/src/main/resources/libvirt/xsl/xml-output-transformation.xsl
@@ -0,0 +1,12 @@
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan">
+ <xsl:output method="xml" omit-xml-declaration="yes"
+ encoding="UTF-8" indent="yes" xalan:indent-amount="2" />
+ <xsl:strip-space elements="*" />
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/src/test/java/org/openslx/libvirt/domain/DomainTest.java b/src/test/java/org/openslx/libvirt/domain/DomainTest.java
new file mode 100644
index 0000000..a604b21
--- /dev/null
+++ b/src/test/java/org/openslx/libvirt/domain/DomainTest.java
@@ -0,0 +1,294 @@
+package org.openslx.libvirt.domain;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.math.BigInteger;
+
+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.openslx.libvirt.domain.Domain.CpuCheck;
+import org.openslx.libvirt.domain.Domain.CpuMode;
+import org.openslx.libvirt.xml.LibvirtXmlDocumentException;
+import org.openslx.libvirt.xml.LibvirtXmlSerializationException;
+import org.openslx.libvirt.xml.LibvirtXmlTestResources;
+import org.openslx.libvirt.xml.LibvirtXmlValidationException;
+
+public class DomainTest
+{
+ @BeforeAll
+ public static void setUp()
+ {
+ // disable logging with log4j
+ LogManager.getRootLogger().setLevel( Level.OFF );
+ }
+
+ private Domain newDomainInstance( String xmlFileName )
+ {
+ Domain domain = null;
+
+ try {
+ domain = new Domain( LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName ) );
+ } catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) {
+ String errorMsg = new String( "Cannot prepare requested Libvirt domain XML file from the resources folder" );
+ fail( errorMsg );
+ }
+
+ return domain;
+ }
+
+ @Test
+ @DisplayName( "Get VM type from libvirt XML file" )
+ public void testGetType()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( Domain.Type.KVM.toString(), vm.getType().toString() );
+ }
+
+ @Test
+ @DisplayName( "Set VM type from libvirt XML file" )
+ public void testSetType()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setType( Domain.Type.QEMU );
+ assertEquals( Domain.Type.QEMU.toString(), vm.getType().toString() );
+ }
+
+ @Test
+ @DisplayName( "Get VM name from libvirt XML file" )
+ public void testGetName()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( "ubuntu-20-04", vm.getName() );
+ }
+
+ @Test
+ @DisplayName( "Set VM name in libvirt XML file" )
+ public void testSetName()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setName( "ubuntu-18-04" );
+ assertEquals( "ubuntu-18-04", vm.getName() );
+ }
+
+ @Test
+ @DisplayName( "Get VM title from libvirt XML file" )
+ public void testGetTitle()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( "Ubuntu 20.04", vm.getTitle() );
+ }
+
+ @Test
+ @DisplayName( "Set VM title in libvirt XML file" )
+ public void testSetTitle()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setTitle( "Ubuntu 18.04" );
+ assertEquals( "Ubuntu 18.04", vm.getTitle() );
+ }
+
+ @Test
+ @DisplayName( "Get VM description from libvirt XML file" )
+ public void testGetDescription()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( "Ubuntu 20.04 desktop installation", vm.getDescription() );
+ }
+
+ @Test
+ @DisplayName( "Set VM description in libvirt XML file" )
+ public void testSetDescription()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setDescription( "Ubuntu 18.04 server installation" );
+ assertEquals( "Ubuntu 18.04 server installation", vm.getDescription() );
+ }
+
+ @Test
+ @DisplayName( "Get VM UUID from libvirt XML file" )
+ public void testGetUuid()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( "8dc5433c-0228-49e4-b019-fa2b606aa544", vm.getUuid() );
+ }
+
+ @Test
+ @DisplayName( "Set VM UUID in libvirt XML file" )
+ public void testSetUuid()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setUuid( "5ab08167-3d95-400e-ac83-e6af8d150971" );
+ assertEquals( "5ab08167-3d95-400e-ac83-e6af8d150971", vm.getUuid() );
+ }
+
+ @Test
+ @DisplayName( "Get VM memory from libvirt XML file" )
+ public void testGetMemory()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( new BigInteger( "4294967296" ).toString(), vm.getMemory().toString() );
+ }
+
+ @Test
+ @DisplayName( "Set VM memory in libvirt XML file" )
+ public void testSetMemory()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setMemory( new BigInteger( "12073740288" ) );
+ assertEquals( new BigInteger( "12073740288" ).toString(), vm.getMemory().toString() );
+ }
+
+ @Test
+ @DisplayName( "Get current VM memory from libvirt XML file" )
+ public void testGetCurrentMemory()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( new BigInteger( "4294967296" ).toString(), vm.getCurrentMemory().toString() );
+ }
+
+ @Test
+ @DisplayName( "Set current VM memory in libvirt XML file" )
+ public void testSetCurrentMemory()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setCurrentMemory( new BigInteger( "8087237632" ) );
+ assertEquals( new BigInteger( "8087237632" ).toString(), vm.getCurrentMemory().toString() );
+ }
+
+ @Test
+ @DisplayName( "Get VM number of vCpus from libvirt XML file" )
+ public void testGetVCpu()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 2, vm.getVCpu() );
+ }
+
+ @Test
+ @DisplayName( "Set VM number of vCpus in libvirt XML file" )
+ public void testSetVCpu()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setVCpu( 4 );
+ assertEquals( 4, vm.getVCpu() );
+ }
+
+ @Test
+ @DisplayName( "Get VM CPU model from libvirt XML file" )
+ public void testGetCpuModel()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertNull( vm.getCpuModel() );
+ }
+
+ @Test
+ @DisplayName( "Set VM CPU model in libvirt XML file" )
+ public void testSetCpuModel()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setCpuModel( "core2duo" );
+ assertEquals( "core2duo", vm.getCpuModel() );
+ }
+
+ @Test
+ @DisplayName( "Get VM CPU mode from libvirt XML file" )
+ public void testGetCpuModelMode()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( CpuMode.HOST_MODEL.toString(), vm.getCpuMode().toString() );
+ }
+
+ @Test
+ @DisplayName( "Set VM CPU mode in libvirt XML file" )
+ public void testSetCpuModelMode()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setCpuMode( CpuMode.HOST_PASSTHROUGH );
+ assertEquals( CpuMode.HOST_PASSTHROUGH.toString(), vm.getCpuMode().toString() );
+ }
+
+ @Test
+ @DisplayName( "Get VM CPU check from libvirt XML file" )
+ public void testGetCpuCheck()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( CpuCheck.PARTIAL.toString(), vm.getCpuCheck().toString() );
+ }
+
+ @Test
+ @DisplayName( "Set VM CPU check in libvirt XML file" )
+ public void testSetCpuCheck()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ vm.setCpuCheck( CpuCheck.NONE );
+ assertEquals( CpuCheck.NONE.toString(), vm.getCpuCheck().toString() );
+ }
+
+ @Test
+ @DisplayName( "Get all VM devices from libvirt XML file" )
+ public void testGetDevices()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 21, vm.getDevices().size() );
+ }
+
+ @Test
+ @DisplayName( "Get all VM controller devices from libvirt XML file" )
+ public void testGetControllerDevices()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 14, vm.getControllerDevices().size() );
+ }
+
+ @Test
+ @DisplayName( "Get all VM disk devices from libvirt XML file" )
+ public void testGetDiskDevices()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 3, vm.getDiskDevices().size() );
+ }
+
+ @Test
+ @DisplayName( "Get all VM hostdev devices from libvirt XML file" )
+ public void testGetHostdevDevices()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 0, vm.getHostdevDevices().size() );
+ }
+
+ @Test
+ @DisplayName( "Get all VM interface devices from libvirt XML file" )
+ public void testGetInterfaceDevices()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 1, vm.getInterfaceDevices().size() );
+ }
+
+ @Test
+ @DisplayName( "Get all VM graphic devices from libvirt XML file" )
+ public void testGetGraphicDevices()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 1, vm.getGraphicDevices().size() );
+ }
+
+ @Test
+ @DisplayName( "Get all VM sound devices from libvirt XML file" )
+ public void testGetSoundDevices()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 1, vm.getSoundDevices().size() );
+ }
+
+ @Test
+ @DisplayName( "Get all VM video devices from libvirt XML file" )
+ public void testGetVideoDevices()
+ {
+ Domain vm = this.newDomainInstance( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
+ assertEquals( 1, vm.getVideoDevices().size() );
+ }
+}
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..75b934e
--- /dev/null
+++ b/src/test/java/org/openslx/libvirt/xml/LibvirtXmlDocumentTest.java
@@ -0,0 +1,260 @@
+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.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+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, InputStream 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 );
+ InputStream rngSchema = LibvirtXmlResources.getLibvirtRng( rngSchemaFileName );
+ document = new LibvirtXmlDocumentStub( xmlFile, rngSchema );
+ } 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/java/org/openslx/util/vm/DiskImageTest.java b/src/test/java/org/openslx/util/vm/DiskImageTest.java
new file mode 100644
index 0000000..e1105d8
--- /dev/null
+++ b/src/test/java/org/openslx/util/vm/DiskImageTest.java
@@ -0,0 +1,260 @@
+package org.openslx.util.vm;
+
+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 java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.openslx.util.vm.DiskImage.ImageFormat;
+import org.openslx.util.vm.DiskImage.UnknownImageFormatException;
+
+public class DiskImageTest
+{
+ @BeforeAll
+ public static void setUp()
+ {
+ // disable logging with log4j
+ LogManager.getRootLogger().setLevel( Level.OFF );
+ }
+
+ @Test
+ @DisplayName( "Test detection of VMDK disk image" )
+ public void testVmdkDiskImage() throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ File file = DiskImageTestResources.getDiskFile( "image-default.vmdk" );
+ DiskImage image = new DiskImage( file );
+
+ assertEquals( ImageFormat.VMDK.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 18, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of VDI disk image" )
+ public void testVdiDiskImage() throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image-default.vdi" ) );
+
+ assertEquals( ImageFormat.VDI.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 0, image.hwVersion );
+ assertNotNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of default QCOW2 disk image" )
+ public void testQcow2DiskImage() throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image-default.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of compressed, 16384 byte cluster QCOW2 disk image with extended L2 tables" )
+ public void testQcow2DetectionL2Compressed16384DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-on_l2-on.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( true, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of compressed, 16384 byte cluster QCOW2 disk image without extended L2 tables" )
+ public void testQcow2DetectionNonL2Compressed16384DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-on_l2-off.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( true, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of non-compressed, 16384 byte cluster QCOW2 disk image with extended L2 tables" )
+ public void testQcow2DetectionL2NonCompressed16384DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-off_l2-on.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of non-compressed, 16384 byte cluster QCOW2 disk image without extended L2 tables" )
+ public void testQcow2DetectionNonL2NonCompressed16384DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-off_l2-off.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of compressed, 65536 byte cluster QCOW2 disk image with extended L2 tables" )
+ public void testQcow2DetectionL2Compressed65536DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-on_l2-on.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( true, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of compressed, 65536 byte cluster QCOW2 disk image without extended L2 tables" )
+ public void testQcow2DetectionNonL2Compressed65536DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-on_l2-off.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( true, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of non-compressed, 65536 byte cluster QCOW2 disk image with extended L2 tables" )
+ public void testQcow2DetectionL2NonCompressed65536DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-off_l2-on.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of non-compressed, 65536 byte cluster QCOW2 disk image without extended L2 tables" )
+ public void testQcow2DetectionNonL2NonCompressed65536DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-off_l2-off.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of compressed, 2097152 byte cluster QCOW2 disk image with extended L2 tables" )
+ public void testQcow2DetectionL2Compressed2097152DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-on_l2-on.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( true, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of compressed, 2097152 byte cluster QCOW2 disk image without extended L2 tables" )
+ public void testQcow2DetectionNonL2Compressed2097152DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-on_l2-off.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( true, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of non-compressed, 2097152 byte cluster QCOW2 disk image with extended L2 tables" )
+ public void testQcow2DetectionL2NonCompressed2097152DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-off_l2-on.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test detection of non-compressed, 2097152 byte cluster QCOW2 disk image without extended L2 tables" )
+ public void testQcow2DetectionNonL2NonCompressed2097152DiskImage()
+ throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ DiskImage image = new DiskImage( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-off_l2-off.qcow2" ) );
+
+ assertEquals( ImageFormat.QCOW2.toString(), image.getImageFormat().toString() );
+ assertEquals( true, image.isStandalone );
+ assertEquals( false, image.isSnapshot );
+ assertEquals( false, image.isCompressed );
+ assertEquals( 3, image.hwVersion );
+ assertNull( image.diskDescription );
+ }
+
+ @Test
+ @DisplayName( "Test of invalid disk image" )
+ public void testInvalidDiskImage() throws FileNotFoundException, IOException, UnknownImageFormatException
+ {
+ Assertions.assertThrows( UnknownImageFormatException.class, () -> {
+ new DiskImage( DiskImageTestResources.getDiskFile( "image-default.invalid" ) );
+ } );
+ }
+}
diff --git a/src/test/java/org/openslx/util/vm/DiskImageTestResources.java b/src/test/java/org/openslx/util/vm/DiskImageTestResources.java
new file mode 100644
index 0000000..1f164bd
--- /dev/null
+++ b/src/test/java/org/openslx/util/vm/DiskImageTestResources.java
@@ -0,0 +1,16 @@
+package org.openslx.util.vm;
+
+import java.io.File;
+import java.net.URL;
+
+public class DiskImageTestResources
+{
+ private static final String DISK_PREFIX_PATH = File.separator + "disk";
+
+ public static File getDiskFile( String diskFileName )
+ {
+ String diskPath = DiskImageTestResources.DISK_PREFIX_PATH + File.separator + diskFileName;
+ URL disk = DiskImageTestResources.class.getResource( diskPath );
+ return new File( disk.getFile() );
+ }
+}
diff --git a/src/test/java/org/openslx/util/vm/QemuMetaDataTest.java b/src/test/java/org/openslx/util/vm/QemuMetaDataTest.java
new file mode 100644
index 0000000..f201a77
--- /dev/null
+++ b/src/test/java/org/openslx/util/vm/QemuMetaDataTest.java
@@ -0,0 +1,466 @@
+package org.openslx.util.vm;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+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.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.openslx.libvirt.domain.Domain;
+import org.openslx.libvirt.domain.device.ControllerUsb;
+import org.openslx.libvirt.domain.device.DiskCdrom;
+import org.openslx.libvirt.domain.device.DiskFloppy;
+import org.openslx.libvirt.domain.device.DiskStorage;
+import org.openslx.libvirt.domain.device.Interface;
+import org.openslx.libvirt.domain.device.Sound;
+import org.openslx.libvirt.xml.LibvirtXmlTestResources;
+import org.openslx.util.vm.DiskImage.ImageFormat;
+import org.openslx.util.vm.VmMetaData.EtherType;
+import org.openslx.util.vm.VmMetaData.EthernetDevType;
+import org.openslx.util.vm.VmMetaData.SoundCardType;
+import org.openslx.util.vm.VmMetaData.UsbSpeed;
+
+public class QemuMetaDataTest
+{
+ private static Domain getPrivateDomainFromQemuMetaData( QemuMetaData qemuMetadata )
+ throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
+ {
+ Field privateDomainField = QemuMetaData.class.getDeclaredField( "vmConfig" );
+ privateDomainField.setAccessible( true );
+ return Domain.class.cast( privateDomainField.get( qemuMetadata ) );
+ }
+
+ @BeforeAll
+ public static void setUp()
+ {
+ // disable logging with log4j
+ LogManager.getRootLogger().setLevel( Level.OFF );
+ }
+
+ @Test
+ @DisplayName( "Test display name from VM configuration" )
+ public void testQemuMetaDataGetDisplayName() throws UnsupportedVirtualizerFormatException, IOException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-archlinux-vm.xml" );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ final String displayName = vmConfig.getDisplayName();
+
+ assertEquals( "archlinux", displayName );
+ }
+
+ @Test
+ @DisplayName( "Test machine snapshot state from VM configuration" )
+ public void testQemuMetaDataIsMachineSnapshot() throws UnsupportedVirtualizerFormatException, IOException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-archlinux-vm.xml" );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ final boolean isVmSnapshot = vmConfig.isMachineSnapshot();
+
+ assertEquals( false, isVmSnapshot );
+ }
+
+ @Test
+ @DisplayName( "Test supported image formats from VM configuration" )
+ public void testQemuMetaDataGetSupportedImageFormats() throws UnsupportedVirtualizerFormatException, IOException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-archlinux-vm.xml" );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ final List<DiskImage.ImageFormat> supportedImageFormats = vmConfig.getSupportedImageFormats();
+
+ assertNotNull( supportedImageFormats );
+ assertEquals( 3, supportedImageFormats.size() );
+ assertEquals( true, supportedImageFormats
+ .containsAll( Arrays.asList( ImageFormat.QCOW2, ImageFormat.VMDK, ImageFormat.VDI ) ) );
+ }
+
+ @Test
+ @DisplayName( "Test output of HDDs from VM configuration" )
+ public void testQemuMetaDataGetHdds() throws UnsupportedVirtualizerFormatException, IOException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-archlinux-vm.xml" );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ final List<VmMetaData.HardDisk> hdds = vmConfig.getHdds();
+
+ assertNotNull( hdds );
+ assertEquals( 1, hdds.size() );
+ assertEquals( "/var/lib/libvirt/images/archlinux.qcow2", hdds.get( 0 ).diskImage );
+ }
+
+ @Test
+ @DisplayName( "Test output of unfiltered VM configuration" )
+ public void testQemuMetaDataGetDefinitionArray() throws UnsupportedVirtualizerFormatException, IOException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-archlinux-vm.xml" );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ final String unfilteredXmlConfig = new String( vmConfig.getDefinitionArray(), StandardCharsets.UTF_8 );
+ final String originalXmlConfig = FileUtils.readFileToString( file, StandardCharsets.UTF_8 );
+
+ assertNotNull( unfilteredXmlConfig );
+
+ final int lengthUnfilteredXmlConfig = unfilteredXmlConfig.split( System.lineSeparator() ).length;
+ final int lengthOriginalXmlConfig = originalXmlConfig.split( System.lineSeparator() ).length;
+
+ assertEquals( lengthOriginalXmlConfig, lengthUnfilteredXmlConfig );
+ }
+
+ @Test
+ @DisplayName( "Test output of filtered VM configuration" )
+ public void testQemuMetaDataGetFilteredDefinitionArray() throws UnsupportedVirtualizerFormatException, IOException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-archlinux-vm.xml" );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ final int numberOfDeletedElements = 4;
+
+ final String filteredXmlConfig = new String( vmConfig.getFilteredDefinitionArray(), StandardCharsets.UTF_8 );
+ final String originalXmlConfig = FileUtils.readFileToString( file, StandardCharsets.UTF_8 );
+
+ assertNotNull( filteredXmlConfig );
+
+ final int lengthFilteredXmlConfig = filteredXmlConfig.split( System.lineSeparator() ).length;
+ final int lengthOriginalXmlConfig = originalXmlConfig.split( System.lineSeparator() ).length;
+
+ assertEquals( lengthOriginalXmlConfig, lengthFilteredXmlConfig + numberOfDeletedElements );
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test add HDD to VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-no-hdd.xml" } )
+ public void testQemuMetaDataAddHdd( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File diskFile = DiskImageTestResources.getDiskFile( "image-default.qcow2" );
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ final int numHddsLibvirtDomainXmlBeforeAdd = vmLibvirtDomainConfig.getDiskStorageDevices().size();
+ final int numHddsQemuMetaDataBeforeAdd = vmConfig.hdds.size();
+
+ vmConfig.addHddTemplate( diskFile, null, null );
+
+ final int numHddsLibvirtDomainXmlAfterAdd = vmLibvirtDomainConfig.getDiskStorageDevices().size();
+ final int numHddsQemuMetaDataAfterAdd = vmConfig.hdds.size();
+
+ assertTrue( numHddsLibvirtDomainXmlBeforeAdd == numHddsQemuMetaDataBeforeAdd );
+ assertTrue( numHddsLibvirtDomainXmlAfterAdd == numHddsQemuMetaDataAfterAdd );
+ assertTrue( numHddsQemuMetaDataBeforeAdd >= 0 );
+ assertTrue( numHddsQemuMetaDataAfterAdd > 0 );
+
+ if ( numHddsQemuMetaDataBeforeAdd >= 1 ) {
+ // update existing HDD in the Libvirt XML config, but do not add a new HDD
+ assertEquals( numHddsQemuMetaDataBeforeAdd, numHddsQemuMetaDataAfterAdd );
+ } else {
+ // numHddsQemuMetaDataBeforeAdd == 0
+ // add a HDD to the Libvirt XML config, since there was no HDD available
+ assertEquals( numHddsQemuMetaDataBeforeAdd + 1, numHddsQemuMetaDataAfterAdd );
+ }
+
+ DiskStorage addedStorageDevice = vmLibvirtDomainConfig.getDiskStorageDevices().get( 0 );
+ assertEquals( diskFile.getAbsolutePath(), addedStorageDevice.getStorageSource() );
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test add CDROM to VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-cdrom.xml" } )
+ public void testQemuMetaDataAddCdrom( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File diskFile = DiskImageTestResources.getDiskFile( "image-default.qcow2" );
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ final int numCdromsLibvirtDomainXmlBeforeAdd = vmLibvirtDomainConfig.getDiskCdromDevices().size();
+
+ vmConfig.addCdrom( 0, diskFile.getAbsolutePath() );
+
+ final int numCdromsLibvirtDomainXmlAfterAdd = vmLibvirtDomainConfig.getDiskCdromDevices().size();
+
+ assertTrue( numCdromsLibvirtDomainXmlBeforeAdd >= 0 );
+ assertTrue( numCdromsLibvirtDomainXmlAfterAdd > 0 );
+
+ DiskCdrom addedCdromDevice = vmLibvirtDomainConfig.getDiskCdromDevices().get( 0 );
+ assertEquals( diskFile.getAbsolutePath(), addedCdromDevice.getStorageSource() );
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test add physical CDROM drive to VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-cdrom.xml" } )
+ public void testQemuMetaDataAddPhysicalCdromDrive( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ final int numCdromsLibvirtDomainXmlBeforeAdd = vmLibvirtDomainConfig.getDiskCdromDevices().size();
+
+ vmConfig.addCdrom( 0, null );
+
+ final int numCdromsLibvirtDomainXmlAfterAdd = vmLibvirtDomainConfig.getDiskCdromDevices().size();
+
+ assertTrue( numCdromsLibvirtDomainXmlBeforeAdd >= 0 );
+ assertTrue( numCdromsLibvirtDomainXmlAfterAdd > 0 );
+
+ DiskCdrom addedCdromDevice = vmLibvirtDomainConfig.getDiskCdromDevices().get( 0 );
+ assertEquals( QemuMetaData.CDROM_DEFAULT_PHYSICAL_DRIVE, addedCdromDevice.getStorageSource() );
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test add floppy to VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-floppy.xml" } )
+ public void testQemuMetaDataAddFloppy( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File diskFile = DiskImageTestResources.getDiskFile( "image-default.qcow2" );
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ final int numFloppiesLibvirtDomainXmlBeforeAdd = vmLibvirtDomainConfig.getDiskFloppyDevices().size();
+
+ vmConfig.addFloppy( 0, diskFile.getAbsolutePath(), true );
+
+ final int numFloppiesLibvirtDomainXmlAfterAdd = vmLibvirtDomainConfig.getDiskFloppyDevices().size();
+
+ assertTrue( numFloppiesLibvirtDomainXmlBeforeAdd >= 0 );
+ assertTrue( numFloppiesLibvirtDomainXmlAfterAdd > 0 );
+
+ DiskFloppy addedFloppyDevice = vmLibvirtDomainConfig.getDiskFloppyDevices().get( 0 );
+ assertTrue( addedFloppyDevice.isReadOnly() );
+ assertEquals( diskFile.getAbsolutePath(), addedFloppyDevice.getStorageSource() );
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test add CPU core count to VM configuration" )
+ @ValueSource( ints = { 2, 4, 6, 8 } )
+ public void testQemuMetaDataAddCpuCoreCount( int coreCount )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( "qemu-kvm_default-archlinux-vm.xml" );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ vmConfig.addCpuCoreCount( coreCount );
+
+ assertEquals( coreCount, vmLibvirtDomainConfig.getVCpu() );
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test get sound card from VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-no-sound.xml" } )
+ public void testQemuMetaDataGetSoundCardType( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ SoundCardType soundCardType = vmConfig.getSoundCard();
+
+ if ( vmLibvirtDomainConfig.getSoundDevices().isEmpty() ) {
+ assertEquals( SoundCardType.NONE, soundCardType );
+ } else {
+ assertEquals( SoundCardType.HD_AUDIO, soundCardType );
+ }
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test set sound card in VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-no-sound.xml" } )
+ public void testQemuMetaDataSetSoundCardType( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ final int numSoundDevsLibvirtDomainXmlBeforeAdd = vmLibvirtDomainConfig.getSoundDevices().size();
+
+ vmConfig.setSoundCard( SoundCardType.SOUND_BLASTER );
+
+ final int numSoundDevsLibvirtDomainXmlAfterAdd = vmLibvirtDomainConfig.getSoundDevices().size();
+
+ assertTrue( numSoundDevsLibvirtDomainXmlBeforeAdd >= 0 );
+ assertTrue( numSoundDevsLibvirtDomainXmlAfterAdd > 0 );
+
+ Sound addedSoundDevice = vmLibvirtDomainConfig.getSoundDevices().get( 0 );
+ assertEquals( Sound.Model.SB16, addedSoundDevice.getModel() );
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test get ethernet device type from VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-no-nic.xml" } )
+ public void testQemuMetaDataGetEthernetDevType( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ EthernetDevType ethernetDeviceType = vmConfig.getEthernetDevType( 0 );
+
+ if ( vmLibvirtDomainConfig.getInterfaceDevices().isEmpty() ) {
+ assertEquals( EthernetDevType.NONE, ethernetDeviceType );
+ } else {
+ assertEquals( EthernetDevType.PARAVIRT, ethernetDeviceType );
+ }
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test set ethernet device type in VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-no-nic.xml" } )
+ public void testQemuMetaDataSetEthernetDevType( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ vmConfig.setEthernetDevType( 0, EthernetDevType.E1000E );
+
+ if ( !vmLibvirtDomainConfig.getInterfaceDevices().isEmpty() ) {
+ Interface addedEthernetDevice = vmLibvirtDomainConfig.getInterfaceDevices().get( 0 );
+ assertEquals( Interface.Model.E1000E, addedEthernetDevice.getModel() );
+ }
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test get maximal USB speed from VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-no-usb.xml" } )
+ public void testQemuMetaDataGetMaxUsbSpeed( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ UsbSpeed maxUsbSpeed = vmConfig.getMaxUsbSpeed();
+
+ if ( vmLibvirtDomainConfig.getUsbControllerDevices().isEmpty() ) {
+ assertEquals( UsbSpeed.NONE, maxUsbSpeed );
+ } else {
+ assertEquals( UsbSpeed.USB3_0, maxUsbSpeed );
+ }
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test set maximal USB speed in VM configuration" )
+ @ValueSource( strings = { "qemu-kvm_default-archlinux-vm.xml", "qemu-kvm_default-archlinux-vm-no-usb.xml" } )
+ public void testQemuMetaDataSetMaxUsbSpeed( String xmlFileName )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ final int numUsbControllersLibvirtDomainXmlBeforeAdd = vmLibvirtDomainConfig.getUsbControllerDevices().size();
+
+ vmConfig.setMaxUsbSpeed( UsbSpeed.USB2_0 );
+
+ final int numUsbControllersLibvirtDomainXmlAfterAdd = vmLibvirtDomainConfig.getUsbControllerDevices().size();
+
+ assertTrue( numUsbControllersLibvirtDomainXmlBeforeAdd >= 0 );
+ assertTrue( numUsbControllersLibvirtDomainXmlAfterAdd > 0 );
+
+ ControllerUsb addedUsbControllerDevice = vmLibvirtDomainConfig.getUsbControllerDevices().get( 0 );
+ assertEquals( ControllerUsb.Model.ICH9_EHCI1, addedUsbControllerDevice.getModel() );
+ }
+
+ static Stream<Arguments> configAndEthernetTypeProvider()
+ {
+ return Stream.of(
+ arguments( "qemu-kvm_default-archlinux-vm.xml", EtherType.BRIDGED ),
+ arguments( "qemu-kvm_default-archlinux-vm.xml", EtherType.HOST_ONLY ),
+ arguments( "qemu-kvm_default-archlinux-vm.xml", EtherType.NAT ),
+ arguments( "qemu-kvm_default-archlinux-vm-no-usb.xml", EtherType.BRIDGED ),
+ arguments( "qemu-kvm_default-archlinux-vm-no-usb.xml", EtherType.HOST_ONLY ),
+ arguments( "qemu-kvm_default-archlinux-vm-no-usb.xml", EtherType.NAT ) );
+ }
+
+ @ParameterizedTest
+ @DisplayName( "Test add ethernet device to VM configuration" )
+ @MethodSource( "configAndEthernetTypeProvider" )
+ public void testQemuMetaDataAddEthernet( String xmlFileName, EtherType ethernetType )
+ throws UnsupportedVirtualizerFormatException, NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException
+ {
+ File file = LibvirtXmlTestResources.getLibvirtXmlFile( xmlFileName );
+ QemuMetaData vmConfig = new QemuMetaData( null, file );
+
+ Domain vmLibvirtDomainConfig = QemuMetaDataTest.getPrivateDomainFromQemuMetaData( vmConfig );
+
+ final int numEthernetDevsLibvirtDomainXmlBeforeAdd = vmLibvirtDomainConfig.getInterfaceDevices().size();
+
+ vmConfig.addEthernet( ethernetType );
+
+ final int numEthernetDevsLibvirtDomainXmlAfterAdd = vmLibvirtDomainConfig.getInterfaceDevices().size();
+
+ assertTrue( numEthernetDevsLibvirtDomainXmlBeforeAdd >= 0 );
+ assertTrue( numEthernetDevsLibvirtDomainXmlAfterAdd > 0 );
+
+ Interface addedEthernetDevice = vmLibvirtDomainConfig.getInterfaceDevices().get( 0 );
+ switch ( ethernetType ) {
+ case BRIDGED:
+ assertEquals( Interface.Type.BRIDGE, addedEthernetDevice.getType() );
+ assertEquals( Interface.Model.VIRTIO, addedEthernetDevice.getModel() );
+ assertEquals( QemuMetaData.NETWORK_DEFAULT_BRIDGE, addedEthernetDevice.getSource() );
+ break;
+ case HOST_ONLY:
+ assertEquals( Interface.Type.NETWORK, addedEthernetDevice.getType() );
+ assertEquals( Interface.Model.VIRTIO, addedEthernetDevice.getModel() );
+ assertEquals( QemuMetaData.NETWORK_DEFAULT_HOST_ONLY, addedEthernetDevice.getSource() );
+ break;
+ case NAT:
+ assertEquals( Interface.Type.NETWORK, addedEthernetDevice.getType() );
+ assertEquals( Interface.Model.VIRTIO, addedEthernetDevice.getModel() );
+ assertEquals( QemuMetaData.NETWORK_DEFAULT_NAT, addedEthernetDevice.getSource() );
+ break;
+ }
+ }
+}
diff --git a/src/test/resources/disk/image-default.invalid b/src/test/resources/disk/image-default.invalid
new file mode 100644
index 0000000..0402ef7
--- /dev/null
+++ b/src/test/resources/disk/image-default.invalid
@@ -0,0 +1 @@
+UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU \ No newline at end of file
diff --git a/src/test/resources/disk/image-default.qcow2 b/src/test/resources/disk/image-default.qcow2
new file mode 100644
index 0000000..1b2dc74
--- /dev/null
+++ b/src/test/resources/disk/image-default.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image-default.vdi b/src/test/resources/disk/image-default.vdi
new file mode 100644
index 0000000..f997236
--- /dev/null
+++ b/src/test/resources/disk/image-default.vdi
Binary files differ
diff --git a/src/test/resources/disk/image-default.vmdk b/src/test/resources/disk/image-default.vmdk
new file mode 100644
index 0000000..810f4cd
--- /dev/null
+++ b/src/test/resources/disk/image-default.vmdk
Binary files differ
diff --git a/src/test/resources/disk/image_cs-16384_cp-off_l2-off.qcow2 b/src/test/resources/disk/image_cs-16384_cp-off_l2-off.qcow2
new file mode 100644
index 0000000..d2442bf
--- /dev/null
+++ b/src/test/resources/disk/image_cs-16384_cp-off_l2-off.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-16384_cp-off_l2-on.qcow2 b/src/test/resources/disk/image_cs-16384_cp-off_l2-on.qcow2
new file mode 100644
index 0000000..7102bde
--- /dev/null
+++ b/src/test/resources/disk/image_cs-16384_cp-off_l2-on.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-16384_cp-on_l2-off.qcow2 b/src/test/resources/disk/image_cs-16384_cp-on_l2-off.qcow2
new file mode 100644
index 0000000..5a52263
--- /dev/null
+++ b/src/test/resources/disk/image_cs-16384_cp-on_l2-off.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-16384_cp-on_l2-on.qcow2 b/src/test/resources/disk/image_cs-16384_cp-on_l2-on.qcow2
new file mode 100644
index 0000000..df6de37
--- /dev/null
+++ b/src/test/resources/disk/image_cs-16384_cp-on_l2-on.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-2097152_cp-off_l2-off.qcow2 b/src/test/resources/disk/image_cs-2097152_cp-off_l2-off.qcow2
new file mode 100644
index 0000000..e08a0f6
--- /dev/null
+++ b/src/test/resources/disk/image_cs-2097152_cp-off_l2-off.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-2097152_cp-off_l2-on.qcow2 b/src/test/resources/disk/image_cs-2097152_cp-off_l2-on.qcow2
new file mode 100644
index 0000000..3144160
--- /dev/null
+++ b/src/test/resources/disk/image_cs-2097152_cp-off_l2-on.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-2097152_cp-on_l2-off.qcow2 b/src/test/resources/disk/image_cs-2097152_cp-on_l2-off.qcow2
new file mode 100644
index 0000000..fa6e589
--- /dev/null
+++ b/src/test/resources/disk/image_cs-2097152_cp-on_l2-off.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-2097152_cp-on_l2-on.qcow2 b/src/test/resources/disk/image_cs-2097152_cp-on_l2-on.qcow2
new file mode 100644
index 0000000..3f4c7d1
--- /dev/null
+++ b/src/test/resources/disk/image_cs-2097152_cp-on_l2-on.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-65536_cp-off_l2-off.qcow2 b/src/test/resources/disk/image_cs-65536_cp-off_l2-off.qcow2
new file mode 100644
index 0000000..e3dc7f6
--- /dev/null
+++ b/src/test/resources/disk/image_cs-65536_cp-off_l2-off.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-65536_cp-off_l2-on.qcow2 b/src/test/resources/disk/image_cs-65536_cp-off_l2-on.qcow2
new file mode 100644
index 0000000..884b0be
--- /dev/null
+++ b/src/test/resources/disk/image_cs-65536_cp-off_l2-on.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-65536_cp-on_l2-off.qcow2 b/src/test/resources/disk/image_cs-65536_cp-on_l2-off.qcow2
new file mode 100644
index 0000000..6e16faf
--- /dev/null
+++ b/src/test/resources/disk/image_cs-65536_cp-on_l2-off.qcow2
Binary files differ
diff --git a/src/test/resources/disk/image_cs-65536_cp-on_l2-on.qcow2 b/src/test/resources/disk/image_cs-65536_cp-on_l2-on.qcow2
new file mode 100644
index 0000000..5219f20
--- /dev/null
+++ b/src/test/resources/disk/image_cs-65536_cp-on_l2-on.qcow2
Binary files differ
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 @@
+<domain type='kvm'>
+ <name>archlinux</name>
+ <uuid>22bbd81f-b31b-4242-9907-8840844944bf</uuid>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://archlinux.org/archlinux/rolling"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2'/>
+ <source file='/var/lib/libvirt/images/archlinux.qcow2'/>
+ <target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </disk>
+ <disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <source file='/var/lib/libvirt/images/cdrom.qcow2'/>
+ <target dev='hda' bus='ide'/>
+ </disk>
+ <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='pci' index='7' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='7' port='0x16'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+ </controller>
+ <interface type='network'>
+ <mac address='52:54:00:c1:4e:70'/>
+ <source network='test'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='tablet' bus='usb'>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <sound model='ich9'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+ </sound>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='2'/>
+ </redirdev>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='3'/>
+ </redirdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
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 @@
+<domain type='kvm'>
+ <name>archlinux</name>
+ <uuid>22bbd81f-b31b-4242-9907-8840844944bf</uuid>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://archlinux.org/archlinux/rolling"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2'/>
+ <source file='/var/lib/libvirt/images/archlinux.qcow2'/>
+ <target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </disk>
+ <disk type='file' device='floppy'>
+ <driver name='qemu' type='raw'/>
+ <source file='/var/lib/libvirt/images/floppy.qcow2'/>
+ <target dev='fda' bus='fdc'/>
+ </disk>
+ <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='pci' index='7' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='7' port='0x16'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+ </controller>
+ <interface type='network'>
+ <mac address='52:54:00:c1:4e:70'/>
+ <source network='test'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='tablet' bus='usb'>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <sound model='ich9'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+ </sound>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='2'/>
+ </redirdev>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='3'/>
+ </redirdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
+
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 @@
+<domain type='kvm'>
+ <name>archlinux</name>
+ <uuid>22bbd81f-b31b-4242-9907-8840844944bf</uuid>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://archlinux.org/archlinux/rolling"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='pci' index='7' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='7' port='0x16'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+ </controller>
+ <interface type='network'>
+ <mac address='52:54:00:c1:4e:70'/>
+ <source network='test'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='tablet' bus='usb'>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <sound model='ich9'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+ </sound>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='2'/>
+ </redirdev>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='3'/>
+ </redirdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
+
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 @@
+<domain type='kvm'>
+ <name>archlinux</name>
+ <uuid>22bbd81f-b31b-4242-9907-8840844944bf</uuid>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://archlinux.org/archlinux/rolling"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2'/>
+ <source file='/var/lib/libvirt/images/archlinux.qcow2'/>
+ <target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </disk>
+ <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='pci' index='7' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='7' port='0x16'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+ </controller>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='tablet' bus='usb'>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <sound model='ich9'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+ </sound>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='2'/>
+ </redirdev>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='3'/>
+ </redirdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
+
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 @@
+<domain type='kvm'>
+ <name>archlinux</name>
+ <uuid>22bbd81f-b31b-4242-9907-8840844944bf</uuid>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://archlinux.org/archlinux/rolling"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2'/>
+ <source file='/var/lib/libvirt/images/archlinux.qcow2'/>
+ <target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </disk>
+ <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='pci' index='7' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='7' port='0x16'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+ </controller>
+ <interface type='network'>
+ <mac address='52:54:00:c1:4e:70'/>
+ <source network='test'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='tablet' bus='usb'>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='2'/>
+ </redirdev>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='3'/>
+ </redirdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
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 @@
+<domain type='kvm'>
+ <name>archlinux</name>
+ <uuid>22bbd81f-b31b-4242-9907-8840844944bf</uuid>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://archlinux.org/archlinux/rolling"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2'/>
+ <source file='/var/lib/libvirt/images/archlinux.qcow2'/>
+ <target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </disk>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='pci' index='7' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='7' port='0x16'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+ </controller>
+ <interface type='network'>
+ <mac address='52:54:00:c1:4e:70'/>
+ <source network='test'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <sound model='ich9'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+ </sound>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
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 @@
+<domain type='kvm'>
+ <name>archlinux</name>
+ <uuid>22bbd81f-b31b-4242-9907-8840844944bf</uuid>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://archlinux.org/archlinux/rolling"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2'/>
+ <source file='/var/lib/libvirt/images/archlinux.qcow2'/>
+ <target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </disk>
+ <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='pci' index='7' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='7' port='0x16'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+ </controller>
+ <interface type='network'>
+ <mac address='52:54:00:c1:4e:70'/>
+ <source network='test'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='tablet' bus='usb'>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <sound model='ich9'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+ </sound>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='2'/>
+ </redirdev>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='3'/>
+ </redirdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
+
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 @@
+<domain type='kvm'>
+ <uuid>8dc5433c-0228-49e4-b019-fa2b606aa544</uuid>
+ <title>Ubuntu 20.04</title>
+ <description>Ubuntu 20.04 desktop installation</description>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://ubuntu.com/ubuntu/20.04"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.1'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <bwLehrpool>Hello World!</bwLehrpool>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw' cache='none' io='native'/>
+ <source dev='/dev/data/ubuntu-20-04.img'/>
+ <target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </disk>
+ <disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <target dev='sda' bus='sata'/>
+ <readonly/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <disk type='file' device='floppy'>
+ <driver name='qemu' type='raw'/>
+ <target dev='fda' bus='fdc'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0' model='ich9-ehci1'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x7'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci1'>
+ <master startport='0'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci2'>
+ <master startport='2'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x1'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci3'>
+ <master startport='4'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x2'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='scsi' index='0' model='virtio-scsi'>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='fdc' index='0'/>
+ <interface type='network'>
+ <mac address='52:54:00:0d:90:0c'/>
+ <source network='default'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='tablet' bus='usb'>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <sound model='ich9'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+ </sound>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='2'/>
+ </redirdev>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='3'/>
+ </redirdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
+
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 @@
+<domain type='kvm'>
+ <name>ubuntu-20-04</name>
+ <uuid>8dc5433c-0228-49e4-b019-fa2b606aa544</uuid>
+ <title>Ubuntu 20.04</title>
+ <description>Ubuntu 20.04 desktop installation</description>
+ <metadata>
+ <libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
+ <libosinfo:os id="http://ubuntu.com/ubuntu/20.04"/>
+ </libosinfo:libosinfo>
+ </metadata>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-q35-5.1'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <vmport state='off'/>
+ </features>
+ <cpu mode='host-model' check='partial'/>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw' cache='none' io='native'/>
+ <source dev='/dev/data/ubuntu-20-04.img'/>
+ <target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+ </disk>
+ <disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <target dev='sda' bus='sata'/>
+ <readonly/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <disk type='file' device='floppy'>
+ <driver name='qemu' type='raw'/>
+ <target dev='fda' bus='fdc'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0' model='ich9-ehci1'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x7'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci1'>
+ <master startport='0'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci2'>
+ <master startport='2'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x1'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci3'>
+ <master startport='4'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x2'/>
+ </controller>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pcie-root'/>
+ <controller type='pci' index='1' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='1' port='0x10'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+ </controller>
+ <controller type='pci' index='2' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='2' port='0x11'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+ </controller>
+ <controller type='pci' index='3' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='3' port='0x12'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+ </controller>
+ <controller type='pci' index='4' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='4' port='0x13'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+ </controller>
+ <controller type='pci' index='5' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='5' port='0x14'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+ </controller>
+ <controller type='pci' index='6' model='pcie-root-port'>
+ <model name='pcie-root-port'/>
+ <target chassis='6' port='0x15'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+ </controller>
+ <controller type='virtio-serial' index='0'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='scsi' index='0' model='virtio-scsi'>
+ <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='fdc' index='0'/>
+ <interface type='network'>
+ <mac address='52:54:00:0d:90:0c'/>
+ <source network='default'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+ <serial type='pty'>
+ <target type='isa-serial' port='0'>
+ <model name='isa-serial'/>
+ </target>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <channel type='unix'>
+ <target type='virtio' name='org.qemu.guest_agent.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='1'/>
+ </channel>
+ <channel type='spicevmc'>
+ <target type='virtio' name='com.redhat.spice.0'/>
+ <address type='virtio-serial' controller='0' bus='0' port='2'/>
+ </channel>
+ <input type='tablet' bus='usb'>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='spice' autoport='yes'>
+ <listen type='address'/>
+ <image compression='off'/>
+ </graphics>
+ <sound model='ich9'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+ </sound>
+ <video>
+ <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+ </video>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='2'/>
+ </redirdev>
+ <redirdev bus='usb' type='spicevmc'>
+ <address type='usb' bus='0' port='3'/>
+ </redirdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+ </memballoon>
+ <rng model='virtio'>
+ <backend model='random'>/dev/urandom</backend>
+ <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ </rng>
+ </devices>
+</domain>
+