diff options
author | Stephan Schwär | 2021-02-19 00:09:54 +0100 |
---|---|---|
committer | Stephan Schwär | 2021-02-19 00:09:54 +0100 |
commit | 62dce8c2b4e519f689e89038ff2afe4496f60628 (patch) | |
tree | 7c6802a436e2beb943065b5a377b61eb77d93170 | |
parent | [docker] Check recieved content. (diff) | |
parent | Add automatic RelaxNG schema validation for Libvirt domain XML documents (diff) | |
download | master-sync-shared-62dce8c2b4e519f689e89038ff2afe4496f60628.tar.gz master-sync-shared-62dce8c2b4e519f689e89038ff2afe4496f60628.tar.xz master-sync-shared-62dce8c2b4e519f689e89038ff2afe4496f60628.zip |
Merge remote-tracking branch 'origin/feature/qemu-integration'
100 files changed, 23792 insertions, 106 deletions
@@ -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">[ -~]{0,8}</param> + </data> + </element> + </optional> + <optional> + <element name="product"> + <data type="string"> + <!-- All printable characters --> + <param name="pattern">[ -~]{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">[!#$%&'*+\-.0-9A-Z\^_`a-z|~]+</param> + </data> + </attribute> + <data type="string"> + <param name="pattern">"?[!#$%&'()*+\-./0-9:>=<?@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 Binary files differnew file mode 100644 index 0000000..1b2dc74 --- /dev/null +++ b/src/test/resources/disk/image-default.qcow2 diff --git a/src/test/resources/disk/image-default.vdi b/src/test/resources/disk/image-default.vdi Binary files differnew file mode 100644 index 0000000..f997236 --- /dev/null +++ b/src/test/resources/disk/image-default.vdi diff --git a/src/test/resources/disk/image-default.vmdk b/src/test/resources/disk/image-default.vmdk Binary files differnew file mode 100644 index 0000000..810f4cd --- /dev/null +++ b/src/test/resources/disk/image-default.vmdk 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 Binary files differnew file mode 100644 index 0000000..d2442bf --- /dev/null +++ b/src/test/resources/disk/image_cs-16384_cp-off_l2-off.qcow2 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 Binary files differnew file mode 100644 index 0000000..7102bde --- /dev/null +++ b/src/test/resources/disk/image_cs-16384_cp-off_l2-on.qcow2 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 Binary files differnew file mode 100644 index 0000000..5a52263 --- /dev/null +++ b/src/test/resources/disk/image_cs-16384_cp-on_l2-off.qcow2 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 Binary files differnew file mode 100644 index 0000000..df6de37 --- /dev/null +++ b/src/test/resources/disk/image_cs-16384_cp-on_l2-on.qcow2 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 Binary files differnew file mode 100644 index 0000000..e08a0f6 --- /dev/null +++ b/src/test/resources/disk/image_cs-2097152_cp-off_l2-off.qcow2 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 Binary files differnew file mode 100644 index 0000000..3144160 --- /dev/null +++ b/src/test/resources/disk/image_cs-2097152_cp-off_l2-on.qcow2 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 Binary files differnew file mode 100644 index 0000000..fa6e589 --- /dev/null +++ b/src/test/resources/disk/image_cs-2097152_cp-on_l2-off.qcow2 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 Binary files differnew file mode 100644 index 0000000..3f4c7d1 --- /dev/null +++ b/src/test/resources/disk/image_cs-2097152_cp-on_l2-on.qcow2 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 Binary files differnew file mode 100644 index 0000000..e3dc7f6 --- /dev/null +++ b/src/test/resources/disk/image_cs-65536_cp-off_l2-off.qcow2 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 Binary files differnew file mode 100644 index 0000000..884b0be --- /dev/null +++ b/src/test/resources/disk/image_cs-65536_cp-off_l2-on.qcow2 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 Binary files differnew file mode 100644 index 0000000..6e16faf --- /dev/null +++ b/src/test/resources/disk/image_cs-65536_cp-on_l2-off.qcow2 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 Binary files differnew file mode 100644 index 0000000..5219f20 --- /dev/null +++ b/src/test/resources/disk/image_cs-65536_cp-on_l2-on.qcow2 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> + |