diff options
author | Manuel Bentele | 2021-06-10 10:47:50 +0200 |
---|---|---|
committer | Manuel Bentele | 2021-06-10 10:49:50 +0200 |
commit | 9cda192f9f5ee9ffb5d8d6962651d950a7b0fd76 (patch) | |
tree | 04fb49f4724a39f0e4a318102f1c11c30ee9310c | |
parent | Add test Libvirt capabilities XML file with old QEMU machine versions (diff) | |
download | master-sync-shared-9cda192f9f5ee9ffb5d8d6962651d950a7b0fd76.tar.gz master-sync-shared-9cda192f9f5ee9ffb5d8d6962651d950a7b0fd76.tar.xz master-sync-shared-9cda192f9f5ee9ffb5d8d6962651d950a7b0fd76.zip |
Add Libvirt PCI, shared memory and hypervisor features for GPU passthrough
19 files changed, 1561 insertions, 46 deletions
diff --git a/src/main/java/org/openslx/libvirt/domain/Domain.java b/src/main/java/org/openslx/libvirt/domain/Domain.java index 50d0811..e0a90e5 100644 --- a/src/main/java/org/openslx/libvirt/domain/Domain.java +++ b/src/main/java/org/openslx/libvirt/domain/Domain.java @@ -26,11 +26,14 @@ 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.HostdevPci; +import org.openslx.libvirt.domain.device.HostdevUsb; 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.Parallel; import org.openslx.libvirt.domain.device.Serial; +import org.openslx.libvirt.domain.device.Shmem; import org.openslx.libvirt.domain.device.Sound; import org.openslx.libvirt.domain.device.Video; import org.openslx.libvirt.xml.LibvirtXmlDocument; @@ -272,6 +275,66 @@ public class Domain extends LibvirtXmlDocument } /** + * Returns the state of the Hyper-V vendor identifier feature. + * + * @return state of the Hyper-V vendor identifier feature. + */ + public boolean isFeatureHypervVendorIdStateOn() + { + return this.getRootXmlNode().getXmlElementAttributeValueAsBool( "features/hyperv/vendor_id", "state" ); + } + + /** + * Sets the state of the Hyper-V vendor identifier feature. + * + * @param on state for the Hyper-V vendor identifier feature. + */ + public void setFeatureHypervVendorIdState( boolean on ) + { + this.getRootXmlNode().setXmlElementAttributeValueOnOff( "features/hyperv/vendor_id", "state", on ); + } + + /** + * Returns the value of the Hyper-V vendor identifier feature. + * + * @return value of the Hyper-V vendor identifier feature. + */ + public String getFeatureHypervVendorIdValue() + { + return this.getRootXmlNode().getXmlElementAttributeValue( "features/hyperv/vendor_id", "value" ); + } + + /** + * Sets the value of the Hyper-V vendor identifier feature. + * + * @param value value for the Hyper-V vendor identifier feature. + */ + public void setFeatureHypervVendorIdValue( String value ) + { + this.getRootXmlNode().setXmlElementAttributeValue( "features/hyperv/vendor_id", "value", value ); + } + + /** + * Returns the state of the KVM hidden feature. + * + * @return state of the KVM hidden feature. + */ + public boolean isFeatureKvmHiddenStateOn() + { + return this.getRootXmlNode().getXmlElementAttributeValueAsBool( "features/kvm/hidden", "state" ); + } + + /** + * Sets the state of the KVM hidden feature. + * + * @param on state for the KVM hidden feature. + */ + public void setFeatureKvmHiddenState( boolean on ) + { + this.getRootXmlNode().setXmlElementAttributeValueOnOff( "features/kvm/hidden", "state", on ); + } + + /** * Returns virtual machine UUID defined in the Libvirt domain XML document. * * @return UUID of virtual machine. @@ -860,6 +923,28 @@ public class Domain extends LibvirtXmlDocument } /** + * Returns list of virtual machine PCI hostdev devices specified in the Libvirt domain XML + * document. + * + * @return list of virtual machine PCI hostdev devices. + */ + public ArrayList<HostdevPci> getHostdevPciDevices() + { + return Domain.filterDevices( HostdevPci.class, this.getDevices() ); + } + + /** + * Returns list of virtual machine USB hostdev devices specified in the Libvirt domain XML + * document. + * + * @return list of virtual machine USB hostdev devices. + */ + public ArrayList<HostdevUsb> getHostdevUsbDevices() + { + return Domain.filterDevices( HostdevUsb.class, this.getDevices() ); + } + + /** * Returns list of virtual machine network interface devices specified in the Libvirt domain XML * document. * @@ -903,6 +988,17 @@ public class Domain extends LibvirtXmlDocument } /** + * Returns list of virtual machine shared memory devices specified in the Libvirt domain XML + * document. + * + * @return list of virtual machine shared memory devices. + */ + public ArrayList<Shmem> getShmemDevices() + { + return Domain.filterDevices( Shmem.class, this.getDevices() ); + } + + /** * Returns list of virtual machine sound devices specified in the Libvirt domain XML document. * * @return list of virtual machine sound devices. @@ -1066,9 +1162,9 @@ public class Domain extends LibvirtXmlDocument } /** - * Adds a virtual machine disk device to the Libvirt domain XML document. + * Adds a virtual machine hostdev device to the Libvirt domain XML document. * - * @return reference to the added disk device if creation was successful. + * @return reference to the added hostdev device if creation was successful. */ public Hostdev addHostdevDevice() { @@ -1076,6 +1172,26 @@ public class Domain extends LibvirtXmlDocument } /** + * Adds a virtual machine PCI hostdev device to the Libvirt domain XML document. + * + * @return reference to the added PCI hostdev device if creation was successful. + */ + public HostdevPci addHostdevPciDevice() + { + return HostdevPci.class.cast( this.addDevice( new HostdevPci() ) ); + } + + /** + * Adds a virtual machine USB hostdev device to the Libvirt domain XML document. + * + * @return reference to the added USB hostdev device if creation was successful. + */ + public HostdevUsb addHostdevUsbDevice() + { + return HostdevUsb.class.cast( this.addDevice( new HostdevUsb() ) ); + } + + /** * Adds a virtual machine network device to the Libvirt domain XML document. * * @return reference to the added network device if creation was successful. @@ -1166,6 +1282,16 @@ public class Domain extends LibvirtXmlDocument } /** + * Adds a virtual machine shared memory device to the Libvirt domain XML document. + * + * @return reference to the added shared memory device if creation was successful. + */ + public Shmem addShmemDevice() + { + return Shmem.class.cast( this.addDevice( new Shmem() ) ); + } + + /** * Adds a virtual machine sound device to the Libvirt domain XML document. * * @return reference to the added sound device if creation was successful. diff --git a/src/main/java/org/openslx/libvirt/domain/device/Device.java b/src/main/java/org/openslx/libvirt/domain/device/Device.java index c252793..1e0e031 100644 --- a/src/main/java/org/openslx/libvirt/domain/device/Device.java +++ b/src/main/java/org/openslx/libvirt/domain/device/Device.java @@ -95,6 +95,9 @@ public class Device extends LibvirtXmlNode } else if ( device instanceof Serial ) { LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.SERIAL ); createdDevice = Serial.createInstance( xmlNode ); + } else if ( device instanceof Shmem ) { + LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.SHMEM ); + createdDevice = Shmem.createInstance( xmlNode ); } else if ( device instanceof Sound ) { LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.SOUND ); createdDevice = Sound.createInstance( xmlNode ); @@ -152,6 +155,9 @@ public class Device extends LibvirtXmlNode case SERIAL: device = Serial.newInstance( xmlNode ); break; + case SHMEM: + device = Shmem.newInstance( xmlNode ); + break; case SOUND: device = Sound.newInstance( xmlNode ); break; @@ -181,6 +187,7 @@ public class Device extends LibvirtXmlNode GRAPHICS ( "graphics" ), PARALLEL ( "parallel" ), SERIAL ( "serial" ), + SHMEM ( "shmem" ), SOUND ( "sound" ), VIDEO ( "video" ); // @formatter:on diff --git a/src/main/java/org/openslx/libvirt/domain/device/GraphicsSpice.java b/src/main/java/org/openslx/libvirt/domain/device/GraphicsSpice.java index f087296..4d06f78 100644 --- a/src/main/java/org/openslx/libvirt/domain/device/GraphicsSpice.java +++ b/src/main/java/org/openslx/libvirt/domain/device/GraphicsSpice.java @@ -46,7 +46,7 @@ public class GraphicsSpice extends Graphics */ public void setOpenGl( boolean enabled ) { - this.setXmlElementAttributeValue( "gl", "enable", enabled ); + this.setXmlElementAttributeValueYesNo( "gl", "enable", enabled ); } /** diff --git a/src/main/java/org/openslx/libvirt/domain/device/Hostdev.java b/src/main/java/org/openslx/libvirt/domain/device/Hostdev.java index cb09099..4c6775e 100644 --- a/src/main/java/org/openslx/libvirt/domain/device/Hostdev.java +++ b/src/main/java/org/openslx/libvirt/domain/device/Hostdev.java @@ -29,6 +29,33 @@ public class Hostdev extends Device } /** + * Checks if hostdev device is managed. + * + * If {@link #isManaged()} returns <code>true</code> the 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 hostdev device is managed. + */ + public boolean isManaged() + { + return this.getXmlElementAttributeValueAsBool( "managed" ); + } + + /** + * Sets state whether hostdev device is managed. + * + * If the <code>managed</code> parameter is set to <code>true</code> the hostdev device is + * detached from the host before being passed on to the guest and reattached to the host after + * the guest exits. + * + * @param managed state whether hostdev device is managed or not. + */ + public void setManaged( boolean managed ) + { + this.setXmlElementAttributeValueYesNo( "managed", managed ); + } + + /** * Removes boot oder entry of the hostdev device. */ public void removeBootOrder() @@ -47,11 +74,13 @@ public class Hostdev extends Device { Hostdev addedHostdev = null; + xmlNode.setXmlElementAttributeValue( "mode", "subsystem" ); + if ( hostdev instanceof HostdevPci ) { - xmlNode.setXmlElementAttributeValue( "device", Type.PCI.toString() ); + xmlNode.setXmlElementAttributeValue( "type", Type.PCI.toString() ); addedHostdev = HostdevPci.createInstance( xmlNode ); } else if ( hostdev instanceof HostdevUsb ) { - xmlNode.setXmlElementAttributeValue( "device", Type.USB.toString() ); + xmlNode.setXmlElementAttributeValue( "type", Type.USB.toString() ); addedHostdev = HostdevUsb.createInstance( xmlNode ); } diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevAddressableSource.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevAddressableSource.java new file mode 100644 index 0000000..9377421 --- /dev/null +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevAddressableSource.java @@ -0,0 +1,26 @@ +package org.openslx.libvirt.domain.device; + +/** + * Addressable source operations for a hostdev device. + * + * @author Manuel Bentele + * @version 1.0 + * + * @param <T> type of the source. + */ +public abstract interface HostdevAddressableSource<T> +{ + /** + * Returns the source of the source device (on the Libvirt host). + * + * @return source of the source device (on the Libvirt host). + */ + public T getSource(); + + /** + * Sets the source for the source device (on the Libvirt host). + * + * @param source source for the source device (on the Libvirt host). + */ + public void setSource( T source ); +} diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevAddressableTarget.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevAddressableTarget.java new file mode 100644 index 0000000..8a6f9a1 --- /dev/null +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevAddressableTarget.java @@ -0,0 +1,26 @@ +package org.openslx.libvirt.domain.device; + +/** + * Addressable target operations for a hostdev device. + * + * @author Manuel Bentele + * @version 1.0 + * + * @param <T> type of the target. + */ +public abstract interface HostdevAddressableTarget<T> +{ + /** + * Returns the target of the target device (in the virtual machine). + * + * @return target of the target device (in the virtual machine). + */ + public T getTarget(); + + /** + * Sets the target for the target device (in the virtual machine). + * + * @param target target for the target device (in the virtual machine). + */ + public void setTarget( T target ); +} diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevPci.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevPci.java index a0563f2..d2be2d6 100644 --- a/src/main/java/org/openslx/libvirt/domain/device/HostdevPci.java +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevPci.java @@ -8,7 +8,8 @@ import org.openslx.libvirt.xml.LibvirtXmlNode; * @author Manuel Bentele * @version 1.0 */ -public class HostdevPci extends Hostdev +public class HostdevPci extends Hostdev implements HostdevAddressableSource<HostdevPciDeviceAddress>, + HostdevAddressableTarget<HostdevPciDeviceAddress> { /** * Creates an empty hostdev PCI device. @@ -29,30 +30,67 @@ public class HostdevPci extends Hostdev } /** - * Checks if PCI hostdev device is managed. + * Returns the PCI device address from an address XML element selected by a XPath expression. * - * 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. + * @param expression XPath expression to select the XML address element. + * @return PCI device address from the selected XML address element. */ - public boolean isManaged() + private HostdevPciDeviceAddress getPciAddress( final String expression ) { - return this.getXmlElementAttributeValueAsBool( "managed" ); + String pciDomain = this.getXmlElementAttributeValue( expression, "domain" ); + String pciBus = this.getXmlElementAttributeValue( expression, "bus" ); + String pciDevice = this.getXmlElementAttributeValue( expression, "slot" ); + String pciFunction = this.getXmlElementAttributeValue( expression, "function" ); + + pciDomain = HostdevUtils.removeHexPrefix( pciDomain ); + pciBus = HostdevUtils.removeHexPrefix( pciBus ); + pciDevice = HostdevUtils.removeHexPrefix( pciDevice ); + pciFunction = HostdevUtils.removeHexPrefix( pciFunction ); + + return HostdevPciDeviceAddress.valueOf( pciDomain + ":" + pciBus + ":" + pciDevice + "." + pciFunction ); } /** - * Sets state whether PCI hostdev device is managed. + * Sets the PCI device address for an XML address element selected by a XPath expression. * - * 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. - * - * @param managed state whether PCI hostdev device is managed or not. + * @param expression XPath expression to select the XML address element. + * @param address PCI device address for the selected XML address element. */ - public void setManaged( boolean managed ) + private void setPciAddress( final String expression, final HostdevPciDeviceAddress address ) + { + final String pciDomain = HostdevUtils.appendHexPrefix( address.getPciDomainAsString() ); + final String pciBus = HostdevUtils.appendHexPrefix( address.getPciBusAsString() ); + final String pciDevice = HostdevUtils.appendHexPrefix( address.getPciDeviceAsString() ); + final String pciFunction = HostdevUtils.appendHexPrefix( address.getPciFunctionAsString() ); + + this.setXmlElementAttributeValue( expression, "domain", pciDomain ); + this.setXmlElementAttributeValue( expression, "bus", pciBus ); + this.setXmlElementAttributeValue( expression, "slot", pciDevice ); + this.setXmlElementAttributeValue( expression, "function", pciFunction ); + } + + @Override + public HostdevPciDeviceAddress getSource() + { + return this.getPciAddress( "source/address" ); + } + + @Override + public void setSource( HostdevPciDeviceAddress address ) + { + this.setPciAddress( "source/address", address ); + } + + @Override + public HostdevPciDeviceAddress getTarget() + { + return this.getPciAddress( "address" ); + } + + @Override + public void setTarget( HostdevPciDeviceAddress address ) { - this.setXmlElementAttributeValue( "managed", managed ); + this.setPciAddress( "address", address ); } /** diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevPciDeviceAddress.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevPciDeviceAddress.java new file mode 100644 index 0000000..9281500 --- /dev/null +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevPciDeviceAddress.java @@ -0,0 +1,295 @@ +package org.openslx.libvirt.domain.device; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Representation of a slot address from a PCI device. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class HostdevPciDeviceAddress +{ + /** + * Regular expression to parse a PCI device address from a {@link String}. + * <p> + * The regular expression matches a PCI device address if its textual PCI device address is + * well-formed according to the following examples: + * + * <pre> + * 0000:00:02.3 + * 0000:01:00.0 + * 0000:00:1f.3 + * </pre> + */ + private static final String DEVICE_ADDRESS_REGEX = "^([a-f0-9]{4}):([a-f0-9]{2}):([a-f0-9]{2})\\.([0-7]{1})$"; + + /** + * Minimum value for a valid PCI device address component number. + */ + private static final int DEVICE_ADDRESS_MIN_VALUE = 0; + + /** + * Maximum value for a valid PCI device domain number. + */ + private static final int DEVICE_ADDRESS_DOMAIN_MAX_VALUE = 0xffff; + + /** + * Maximum value for a valid PCI device bus number. + */ + private static final int DEVICE_ADDRESS_BUS_MAX_VALUE = 0xff; + + /** + * Maximum value for a valid PCI device device number. + */ + private static final int DEVICE_ADDRESS_DEVICE_MAX_VALUE = 0x1f; + + /** + * Maximum value for a valid PCI device function number. + */ + private static final int DEVICE_ADDRESS_FUNCTION_MAX_VALUE = 0x7; + + /** + * Domain number of the PCI device address. + */ + final int pciDomain; + + /** + * Bus number of the PCI device address. + */ + final int pciBus; + + /** + * Device number of the PCI device address. + */ + final int pciDevice; + + /** + * Function number of the PCI device address. + */ + final int pciFunction; + + /** + * Creates a new PCI device address and sets the address information to the default + * address {@code 0000:00:00.0}. + */ + public HostdevPciDeviceAddress() + { + this( 0, 0, 0, 0 ); + } + + /** + * Creates a new PCI device address consisting of a specified PCI bus, device, and function. + * <p> + * The domain of the PCI device address is set to the default number {@code 0000}. + * + * @param pciBus number of the PCI bus. + * @param pciDevice number of the PCI device. + * @param pciFunction number of the PCI function. + * + * @throws IllegalArgumentException failed to validate the PCI bus, device and function. + */ + public HostdevPciDeviceAddress( int pciBus, int pciDevice, int pciFunction ) throws IllegalArgumentException + { + this( 0, pciBus, pciDevice, pciFunction ); + } + + /** + * Creates a new PCI device address consisting of a specified PCI domain, bus, device, and + * function. + * + * @param pciDomain number of the PCI domain. + * @param pciBus number of the PCI bus. + * @param pciDevice number of the PCI device. + * @param pciFunction number of the PCI function. + * + * @throws IllegalArgumentException failed to validate the PCI domain, bus, device and function. + */ + public HostdevPciDeviceAddress( int pciDomain, int pciBus, int pciDevice, int pciFunction ) + throws IllegalArgumentException + { + HostdevPciDeviceAddress.validatePciDeviceAddress( pciDomain, "PCI domain", + HostdevPciDeviceAddress.DEVICE_ADDRESS_DOMAIN_MAX_VALUE ); + HostdevPciDeviceAddress.validatePciDeviceAddress( pciBus, "PCI bus", + HostdevPciDeviceAddress.DEVICE_ADDRESS_BUS_MAX_VALUE ); + HostdevPciDeviceAddress.validatePciDeviceAddress( pciDevice, "PCI device", + HostdevPciDeviceAddress.DEVICE_ADDRESS_DEVICE_MAX_VALUE ); + HostdevPciDeviceAddress.validatePciDeviceAddress( pciFunction, "PCI function", + HostdevPciDeviceAddress.DEVICE_ADDRESS_FUNCTION_MAX_VALUE ); + + this.pciDomain = pciDomain; + this.pciBus = pciBus; + this.pciDevice = pciDevice; + this.pciFunction = pciFunction; + } + + /** + * Validates a specified PCI address component (PCI domain, bus, device or function). + * + * @param address value of the PCI address component. + * @param addressName name of the PCI address component. + * @param upperLimit maximum value for the PCI address component + * + * @throws IllegalArgumentException + */ + private static void validatePciDeviceAddress( final int address, final String addressName, final int upperLimit ) + throws IllegalArgumentException + { + if ( address < HostdevPciDeviceAddress.DEVICE_ADDRESS_MIN_VALUE ) { + throw new IllegalArgumentException( + "The " + addressName + " address must be larger or equal than " + + HostdevPciDeviceAddress.DEVICE_ADDRESS_MIN_VALUE ); + } else if ( address > upperLimit ) { + throw new IllegalArgumentException( + "The " + addressName + " address must be smaller or equal than " + upperLimit ); + } + } + + /** + * Returns the PCI domain. + * + * @return PCI domain. + */ + public int getPciDomain() + { + return this.pciDomain; + } + + /** + * Returns the PCI domain as {@link String}. + * + * @return PCI domain as {@link String}. + */ + public String getPciDomainAsString() + { + return String.format( "%04x", HostdevPciDeviceAddress.DEVICE_ADDRESS_DOMAIN_MAX_VALUE & this.getPciDomain() ); + } + + /** + * Returns the PCI bus. + * + * @return PCI bus. + */ + public int getPciBus() + { + return this.pciBus; + } + + /** + * Returns the PCI bus as {@link String}. + * + * @return PCI bus as {@link String}. + */ + public String getPciBusAsString() + { + return String.format( "%02x", HostdevPciDeviceAddress.DEVICE_ADDRESS_BUS_MAX_VALUE & this.getPciBus() ); + } + + /** + * Returns the PCI device. + * + * @return PCI device. + */ + public int getPciDevice() + { + return this.pciDevice; + } + + /** + * Returns the PCI device as {@link String}. + * + * @return PCI device as {@link String}. + */ + public String getPciDeviceAsString() + { + return String.format( "%02x", HostdevPciDeviceAddress.DEVICE_ADDRESS_DEVICE_MAX_VALUE & this.getPciDevice() ); + } + + /** + * Returns the PCI function. + * + * @return PCI function. + */ + public int getPciFunction() + { + return this.pciFunction; + } + + /** + * Returns the PCI function as {@link String}. + * + * @return PCI function as {@link String}. + */ + public String getPciFunctionAsString() + { + return String.format( "%01x", HostdevPciDeviceAddress.DEVICE_ADDRESS_FUNCTION_MAX_VALUE & this.getPciFunction() ); + } + + /** + * Creates a new PCI device address parsed from a {@link String}. + * <p> + * The PCI device address consists of a PCI domain, bus, device and function parsed from the + * specified {@link String}. + * + * @param pciDeviceAddress textual information containing a PCI device address as {@link String}. + * The textual PCI device address should be well-formed according to the defined + * regular expression {@link #DEVICE_ADDRESS_REGEX}. + * + * @return PCI device address instance. + */ + public static HostdevPciDeviceAddress valueOf( String pciDeviceAddress ) + { + HostdevPciDeviceAddress parsedPciDeviceAddress; + + if ( pciDeviceAddress == null || pciDeviceAddress.isEmpty() ) { + parsedPciDeviceAddress = null; + } else { + final Pattern pciDeviceAddressPattern = Pattern.compile( HostdevPciDeviceAddress.DEVICE_ADDRESS_REGEX ); + final Matcher pciDeviceAddressMatcher = pciDeviceAddressPattern.matcher( pciDeviceAddress.toLowerCase() ); + + if ( pciDeviceAddressMatcher.find() ) { + final int pciDomain = Integer.valueOf( pciDeviceAddressMatcher.group( 1 ), 16 ); + final int pciBus = Integer.valueOf( pciDeviceAddressMatcher.group( 2 ), 16 ); + final int pciDevice = Integer.valueOf( pciDeviceAddressMatcher.group( 3 ), 16 ); + final int pciFunction = Integer.valueOf( pciDeviceAddressMatcher.group( 4 ), 16 ); + + try { + parsedPciDeviceAddress = new HostdevPciDeviceAddress( pciDomain, pciBus, pciDevice, pciFunction ); + } catch ( IllegalArgumentException e ) { + parsedPciDeviceAddress = null; + } + } else { + parsedPciDeviceAddress = null; + } + } + + return parsedPciDeviceAddress; + } + + @Override + public boolean equals( Object obj ) + { + if ( obj == null ) { + return false; + } else if ( this.getClass() != obj.getClass() ) { + return false; + } else { + // check if PCI domain, bus, device and function are equal + final HostdevPciDeviceAddress other = HostdevPciDeviceAddress.class.cast( obj ); + if ( this.getPciDomain() == other.getPciDomain() && this.getPciBus() == other.getPciBus() + && this.getPciDevice() == other.getPciDevice() && this.getPciFunction() == other.getPciFunction() ) { + return true; + } else { + return false; + } + } + } + + @Override + public String toString() + { + return this.getPciDomainAsString() + ":" + this.getPciBusAsString() + ":" + this.getPciDeviceAsString() + "." + + this.getPciFunctionAsString(); + } +} diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevPciDeviceDescription.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevPciDeviceDescription.java new file mode 100644 index 0000000..25c29f2 --- /dev/null +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevPciDeviceDescription.java @@ -0,0 +1,186 @@ +package org.openslx.libvirt.domain.device; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Representation of a PCI device description. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class HostdevPciDeviceDescription extends Hostdev +{ + /** + * Regular expression to parse a PCI vendor and device identifier from a {@link String}. + * <p> + * The regular expression matches a PCI device description if its textual PCI device description + * is well-formed according to the following examples: + * + * <pre> + * 8086:a170 + * 10ec:8168 + * 8086:15b7 + * </pre> + */ + private static final String DEVICE_DESCRIPTION_REGEX = "^([a-f0-9]{4}):([a-f0-9]{4})$"; + + /** + * Minimum value of a valid identifier from a PCI device description. + */ + private static final int DEVICE_DESCRIPTION_ID_MIN_VALUE = 0x0000; + + /** + * Maximum value of a valid identifier from a PCI device description. + */ + private static final int DEVICE_DESCRIPTION_ID_MAX_VALUE = 0xffff; + + /** + * Vendor identifier of the PCI device. + */ + final int vendorId; + + /** + * Device identifier of the PCI device. + */ + final int deviceId; + + /** + * Creates a new PCI device description consisting of a PCI vendor and device identifier. + * + * @param vendorId vendor identifier of the PCI device. + * @param deviceId device identifier of the PCI device. + * + * @throws throws IllegalArgumentException failed to validate the PCI device description + * identifiers. + */ + public HostdevPciDeviceDescription( int vendorId, int deviceId ) throws IllegalArgumentException + { + HostdevPciDeviceDescription.validatePciDeviceDescriptionId( "PCI vendor ID", vendorId ); + HostdevPciDeviceDescription.validatePciDeviceDescriptionId( "PCI device ID", deviceId ); + + this.vendorId = vendorId; + this.deviceId = deviceId; + } + + /** + * Validates a PCI device description ID (a PCI vendor or device ID). + * + * @param idName name of the PCI device description identifier. + * @param id value of the PCI device description identifier that should be validated. + * + * @throws IllegalArgumentException failed to validate the PCI device description identifier. + */ + private static void validatePciDeviceDescriptionId( final String idName, final int id ) + throws IllegalArgumentException + { + if ( id < HostdevPciDeviceDescription.DEVICE_DESCRIPTION_ID_MIN_VALUE ) { + throw new IllegalArgumentException( + "The " + idName + "must be larger or equal than " + + HostdevPciDeviceDescription.DEVICE_DESCRIPTION_ID_MIN_VALUE ); + } else if ( id > HostdevPciDeviceDescription.DEVICE_DESCRIPTION_ID_MAX_VALUE ) { + throw new IllegalArgumentException( + "The " + idName + "must be smaller or equal than " + + HostdevPciDeviceDescription.DEVICE_DESCRIPTION_ID_MAX_VALUE ); + } + } + + /** + * Returns the PCI vendor identifier. + * + * @return PCI vendor identifier. + */ + public int getVendorId() + { + return this.vendorId; + } + + /** + * Returns the PCI vendor identifier as {@link String}. + * + * @return PCI vendor identifier as {@link String}. + */ + public String getVendorIdAsString() + { + return String.format( "%04x", HostdevPciDeviceDescription.DEVICE_DESCRIPTION_ID_MAX_VALUE & this.getVendorId() ); + } + + /** + * Returns the PCI device identifier. + * + * @return PCI device identifier. + */ + public int getDeviceId() + { + return this.deviceId; + } + + /** + * Returns the PCI device identifier as {@link String}. + * + * @return PCI device identifier as {@link String}. + */ + public String getDeviceIdAsString() + { + return String.format( "%04x", HostdevPciDeviceDescription.DEVICE_DESCRIPTION_ID_MAX_VALUE & this.getDeviceId() ); + } + + /** + * Creates a new PCI device description parsed from a {@link String}. + * <p> + * The PCI device description consists of a PCI vendor and device identifier parsed from the + * specified {@link String}. + * + * @param vendorDeviceId textual information containing a PCI device description as + * {@link String}. The textual PCI device description should be well-formed according + * to the defined regular expression {@link #DEVICE_DESCRIPTION_REGEX}. + * + * @return PCI device description instance. + */ + public static HostdevPciDeviceDescription valueOf( String vendorDeviceId ) + { + final HostdevPciDeviceDescription pciDeviceDescription; + + if ( vendorDeviceId == null || vendorDeviceId.isEmpty() ) { + pciDeviceDescription = null; + } else { + final Pattern pciDeviceDescPattern = Pattern.compile( HostdevPciDeviceDescription.DEVICE_DESCRIPTION_REGEX ); + final Matcher pciDeviceDescMatcher = pciDeviceDescPattern.matcher( vendorDeviceId.toLowerCase() ); + + if ( pciDeviceDescMatcher.find() ) { + final int vendorId = Integer.valueOf( pciDeviceDescMatcher.group( 1 ), 16 ); + final int deviceId = Integer.valueOf( pciDeviceDescMatcher.group( 2 ), 16 ); + + pciDeviceDescription = new HostdevPciDeviceDescription( vendorId, deviceId ); + } else { + pciDeviceDescription = null; + } + } + + return pciDeviceDescription; + } + + @Override + public boolean equals( Object obj ) + { + if ( obj == null ) { + return false; + } else if ( this.getClass() != obj.getClass() ) { + return false; + } else { + // check if vendor and device ID are equal + final HostdevPciDeviceDescription other = HostdevPciDeviceDescription.class.cast( obj ); + if ( this.getVendorId() == other.getVendorId() && this.getDeviceId() == other.getDeviceId() ) { + return true; + } else { + return false; + } + } + } + + @Override + public String toString() + { + return this.getVendorIdAsString() + ":" + this.getDeviceIdAsString(); + } +} diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevUsb.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevUsb.java index e1fcc0c..a9d7f89 100644 --- a/src/main/java/org/openslx/libvirt/domain/device/HostdevUsb.java +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevUsb.java @@ -8,7 +8,8 @@ import org.openslx.libvirt.xml.LibvirtXmlNode; * @author Manuel Bentele * @version 1.0 */ -public class HostdevUsb extends Hostdev +public class HostdevUsb extends Hostdev implements HostdevAddressableSource<HostdevUsbDeviceDescription>, + HostdevAddressableTarget<HostdevUsbDeviceAddress> { /** * Creates an empty hostdev USB device. @@ -28,6 +29,44 @@ public class HostdevUsb extends Hostdev super( xmlNode ); } + @Override + public HostdevUsbDeviceDescription getSource() + { + String vendorId = this.getXmlElementAttributeValue( "source/address/vendor", "id" ); + String productId = this.getXmlElementAttributeValue( "source/address/product", "id" ); + + vendorId = HostdevUtils.removeHexPrefix( vendorId ); + productId = HostdevUtils.removeHexPrefix( productId ); + + return HostdevUsbDeviceDescription.valueOf( vendorId + ":" + productId ); + } + + @Override + public void setSource( HostdevUsbDeviceDescription description ) + { + final String vendorId = HostdevUtils.appendHexPrefix( description.getVendorIdAsString() ); + final String productId = HostdevUtils.appendHexPrefix( description.getProductIdAsString() ); + + this.setXmlElementAttributeValue( "source/address/vendor", "id", vendorId ); + this.setXmlElementAttributeValue( "source/address/product", "id", productId ); + } + + @Override + public HostdevUsbDeviceAddress getTarget() + { + final String usbBus = this.getXmlElementAttributeValue( "address", "bus" ); + final String usbPort = this.getXmlElementAttributeValue( "address", "port" ); + + return HostdevUsbDeviceAddress.valueOf( usbBus, usbPort ); + } + + @Override + public void setTarget( HostdevUsbDeviceAddress address ) + { + this.setXmlElementAttributeValue( "address", "bus", Integer.toString( address.getUsbBus() ) ); + this.setXmlElementAttributeValue( "address", "port", Integer.toString( address.getUsbPort() ) ); + } + /** * Creates a non-existent hostdev USB device as Libvirt XML device element. * diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevUsbDeviceAddress.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevUsbDeviceAddress.java new file mode 100644 index 0000000..02961a1 --- /dev/null +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevUsbDeviceAddress.java @@ -0,0 +1,141 @@ +package org.openslx.libvirt.domain.device; + +/** + * Representation of an address from an USB device. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class HostdevUsbDeviceAddress +{ + /** + * Minimum value for a valid USB device address component number. + */ + private static final int DEVICE_ADDRESS_MIN_VALUE = 0; + + /** + * Maximum value for a valid USB device bus number. + */ + static final int DEVICE_BUS_MAX_VALUE = 127; + + /** + * Maximum value for a valid USB device port number. + */ + static final int DEVICE_PORT_MAX_VALUE = 127; + + /** + * USB bus of the USB device. + */ + final int usbBus; + + /** + * USB port of the USB device. + */ + final int usbPort; + + public HostdevUsbDeviceAddress( int usbBus, int usbPort ) throws IllegalArgumentException + { + HostdevUsbDeviceAddress.validateUsbDeviceAddress( usbBus, "USB bus", + HostdevUsbDeviceAddress.DEVICE_BUS_MAX_VALUE ); + HostdevUsbDeviceAddress.validateUsbDeviceAddress( usbPort, "USB port", + HostdevUsbDeviceAddress.DEVICE_PORT_MAX_VALUE ); + + this.usbBus = usbBus; + this.usbPort = usbPort; + } + + /** + * Validates a specified USB device address component (USB bus or port). + * + * @param address value of the USB address component. + * @param addressName name of the USB address component. + * @param upperLimit maximum value for the USB address component + * + * @throws IllegalArgumentException + */ + private static void validateUsbDeviceAddress( final int address, final String addressName, final int upperLimit ) + throws IllegalArgumentException + { + if ( address < HostdevUsbDeviceAddress.DEVICE_ADDRESS_MIN_VALUE ) { + throw new IllegalArgumentException( + "The " + addressName + " must be larger or equal than " + + HostdevUsbDeviceAddress.DEVICE_ADDRESS_MIN_VALUE ); + } else if ( address > upperLimit ) { + throw new IllegalArgumentException( + "The " + addressName + " must be smaller or equal than " + upperLimit ); + } + } + + /** + * Returns the USB bus number. + * + * @return USB bus number. + */ + public int getUsbBus() + { + return this.usbBus; + } + + /** + * Returns the USB port number. + * + * @return USB port number. + */ + public int getUsbPort() + { + return this.usbPort; + } + + /** + * Creates a new USB device address parsed from {@link String}s. + * + * @param usbBus textual information containing a decimal USB device bus number as + * {@link String}. + * @param usbPort textual information containing a decimal USB device port number as + * {@link String}. + * @return USB device address instance. + */ + public static HostdevUsbDeviceAddress valueOf( String usbBus, String usbPort ) + { + HostdevUsbDeviceAddress usbDeviceAddress; + + if ( usbBus == null || usbBus.isEmpty() || usbPort == null || usbPort.isEmpty() ) { + usbDeviceAddress = null; + } else { + try { + final int parsedUsbBus = Integer.valueOf( usbBus ); + final int parsedUsbPort = Integer.valueOf( usbPort ); + usbDeviceAddress = new HostdevUsbDeviceAddress( parsedUsbBus, parsedUsbPort ); + } catch ( IllegalArgumentException e ) { + usbDeviceAddress = null; + } + } + + return usbDeviceAddress; + + } + + @Override + public boolean equals( Object obj ) + { + if ( obj == null ) { + return false; + } else if ( this.getClass() != obj.getClass() ) { + return false; + } else { + // check if USB bus and port are equal + final HostdevUsbDeviceAddress other = HostdevUsbDeviceAddress.class.cast( obj ); + if ( this.getUsbBus() == other.getUsbBus() && this.getUsbPort() == other.getUsbPort() ) { + return true; + } else { + return false; + } + } + } + + @Override + public String toString() + { + return this.getUsbBus() + ":" + this.getUsbPort(); + } +} diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevUsbDeviceDescription.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevUsbDeviceDescription.java new file mode 100644 index 0000000..d1c546f --- /dev/null +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevUsbDeviceDescription.java @@ -0,0 +1,190 @@ +package org.openslx.libvirt.domain.device; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Representation of an USB device description. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class HostdevUsbDeviceDescription +{ + /** + * Regular expression to parse an USB device vendor and product identifier from a {@link String}. + * <p> + * The regular expression matches an USB device description if its textual USB device description + * is well-formed according to the following examples: + * + * <pre> + * 1d6b:0002 + * 0461:4d22 + * 046d:c312 + * </pre> + */ + private static final String DEVICE_DESCRIPTION_REGEX = "^([a-f0-9]{4}):([a-f0-9]{4})$"; + + /** + * Minimum value of a valid identifier from an USB device description. + */ + private static final int DEVICE_DESCRIPTION_ID_MIN_VALUE = 0x0000; + + /** + * Maximum value of a valid identifier from an USB device description. + */ + private static final int DEVICE_DESCRIPTION_ID_MAX_VALUE = 0xffff; + + /** + * Vendor identifier of the USB device. + */ + final int vendorId; + + /** + * Product identifier of the USB device. + */ + final int productId; + + /** + * Creates a new USB device description consisting of a USB vendor and product identifier. + * + * @param vendorId vendor identifier of the USB device. + * @param productId product identifier of the USB device. + * + * @throws throws IllegalArgumentException failed to validate the USB device description + * identifiers. + */ + public HostdevUsbDeviceDescription( int vendorId, int productId ) throws IllegalArgumentException + { + HostdevUsbDeviceDescription.validateUsbDeviceDescriptionId( "PCI vendor ID", vendorId ); + HostdevUsbDeviceDescription.validateUsbDeviceDescriptionId( "PCI device ID", productId ); + + this.vendorId = vendorId; + this.productId = productId; + } + + /** + * Validates an USB device description ID (an USB vendor or product ID). + * + * @param idName name of the USB device description identifier. + * @param id value of the USB device description identifier that should be validated. + * + * @throws IllegalArgumentException failed to validate the USB device description identifier. + */ + private static void validateUsbDeviceDescriptionId( final String idName, final int id ) + throws IllegalArgumentException + { + if ( id < HostdevUsbDeviceDescription.DEVICE_DESCRIPTION_ID_MIN_VALUE ) { + throw new IllegalArgumentException( + "The " + idName + "must be larger or equal than " + + HostdevUsbDeviceDescription.DEVICE_DESCRIPTION_ID_MIN_VALUE ); + } else if ( id > HostdevUsbDeviceDescription.DEVICE_DESCRIPTION_ID_MAX_VALUE ) { + throw new IllegalArgumentException( + "The " + idName + "must be smaller or equal than " + + HostdevUsbDeviceDescription.DEVICE_DESCRIPTION_ID_MAX_VALUE ); + } + } + + /** + * Returns the USB device vendor identifier. + * + * @return USB device vendor identifier. + */ + public int getVendorId() + { + return this.vendorId; + } + + /** + * Returns the USB vendor identifier as {@link String}. + * + * @return USB vendor identifier as {@link String}. + */ + public String getVendorIdAsString() + { + return String.format( "%04x", HostdevUsbDeviceDescription.DEVICE_DESCRIPTION_ID_MAX_VALUE & this.getVendorId() ); + } + + /** + * Returns the USB device product identifier. + * + * @return USB device product identifier. + */ + public int getProductId() + { + return this.productId; + } + + /** + * Returns the USB device product identifier as {@link String}. + * + * @return USB device product identifier as {@link String}. + */ + public String getProductIdAsString() + { + return String.format( "%04x", HostdevUsbDeviceDescription.DEVICE_DESCRIPTION_ID_MAX_VALUE & this.getProductId() ); + } + + /** + * Creates a new USB device description parsed from a {@link String}. + * <p> + * The USB device description consists of an USB vendor and product identifier parsed from the + * specified {@link String}. + * + * @param vendorProductId textual information containing an USB device description as + * {@link String}. The textual USB device description should be well-formed according + * to the defined regular expression {@link #DEVICE_DESCRIPTION_REGEX}. + * + * @return USB device description instance. + */ + public static HostdevUsbDeviceDescription valueOf( String vendorProductId ) + { + HostdevUsbDeviceDescription usbDeviceDescription; + + if ( vendorProductId == null || vendorProductId.isEmpty() ) { + usbDeviceDescription = null; + } else { + final Pattern usbDeviceDescPattern = Pattern.compile( HostdevUsbDeviceDescription.DEVICE_DESCRIPTION_REGEX ); + final Matcher usbDeviceDescMatcher = usbDeviceDescPattern.matcher( vendorProductId.toLowerCase() ); + + if ( usbDeviceDescMatcher.find() ) { + final int vendorId = Integer.valueOf( usbDeviceDescMatcher.group( 1 ), 16 ); + final int productId = Integer.valueOf( usbDeviceDescMatcher.group( 2 ), 16 ); + + try { + usbDeviceDescription = new HostdevUsbDeviceDescription( vendorId, productId ); + } catch ( IllegalArgumentException e ) { + usbDeviceDescription = null; + } + } else { + usbDeviceDescription = null; + } + } + + return usbDeviceDescription; + } + + @Override + public boolean equals( Object obj ) + { + if ( obj == null ) { + return false; + } else if ( this.getClass() != obj.getClass() ) { + return false; + } else { + // check if vendor and product ID are equal + final HostdevUsbDeviceDescription other = HostdevUsbDeviceDescription.class.cast( obj ); + if ( this.getVendorId() == other.getVendorId() && this.getProductId() == other.getProductId() ) { + return true; + } else { + return false; + } + } + } + + @Override + public String toString() + { + return this.getVendorIdAsString() + ":" + this.getProductIdAsString(); + } +} diff --git a/src/main/java/org/openslx/libvirt/domain/device/HostdevUtils.java b/src/main/java/org/openslx/libvirt/domain/device/HostdevUtils.java new file mode 100644 index 0000000..204bea9 --- /dev/null +++ b/src/main/java/org/openslx/libvirt/domain/device/HostdevUtils.java @@ -0,0 +1,32 @@ +package org.openslx.libvirt.domain.device; + +/** + * Collection of helper methods to maintain a Libvirt related hostdev XML element. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class HostdevUtils +{ + /** + * Appends the HEX prefix to a specified hostdev address component without any HEX prefix. + * + * @param component address component without any HEX prefix. + * @return address component with the HEX prefix. + */ + public static String appendHexPrefix( String component ) + { + return "0x" + component; + } + + /** + * Removes a possible HEX prefix of a specified hostdev address component. + * + * @param component address component with possible HEX prefix. + * @return address component without any HEX prefix. + */ + public static String removeHexPrefix( String component ) + { + return component.replaceFirst( "0x", "" ); + } +} diff --git a/src/main/java/org/openslx/libvirt/domain/device/Shmem.java b/src/main/java/org/openslx/libvirt/domain/device/Shmem.java new file mode 100644 index 0000000..3c48c95 --- /dev/null +++ b/src/main/java/org/openslx/libvirt/domain/device/Shmem.java @@ -0,0 +1,178 @@ +package org.openslx.libvirt.domain.device; + +import java.math.BigInteger; + +import org.openslx.libvirt.domain.DomainUtils; +import org.openslx.libvirt.xml.LibvirtXmlNode; + +/** + * A shared memory device node in a Libvirt domain XML document. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class Shmem extends Device +{ + /** + * Creates an empty sound device. + */ + public Shmem() + { + super(); + } + + /** + * Creates a shared memory device representing an existing Libvirt XML shared memory device + * element. + * + * @param xmlNode existing Libvirt XML shared memory device element. + */ + public Shmem( LibvirtXmlNode xmlNode ) + { + super( xmlNode ); + } + + /** + * Returns the model of the shared memory device. + * + * @return model of the shared memory device. + */ + public Model getModel() + { + String model = this.getXmlElementAttributeValue( "model", "type" ); + return Model.fromString( model ); + } + + /** + * Sets the model for the shared memory device. + * + * @param model model for the shared memory device. + */ + public void setModel( Model model ) + { + this.setXmlElementAttributeValue( "model", "type", model.toString() ); + } + + /** + * Returns the name of the shared memory device. + * + * @return name of the shared memory device. + */ + public String getName() + { + return this.getXmlElementAttributeValue( "name" ); + } + + /** + * Sets the name for the shared memory device. + * + * @param name name for the shared memory device. + */ + public void setName( String name ) + { + this.setXmlElementAttributeValue( "name", name ); + } + + /** + * Returns the memory size of the shared memory device. + * + * @return memory size of the shared memory device in bytes. + */ + public BigInteger getSize() + { + final String unit = this.getXmlElementAttributeValue( "size", "unit" ); + final String size = this.getXmlElementValue( "size" ); + + return DomainUtils.decodeMemory( size, unit ); + } + + /** + * Sets the memory size for the shared memory device. + * + * @param size memory size for the shared memory device in bytes. + */ + public void setSize( BigInteger memory ) + { + final String unit = "M"; + final String size = DomainUtils.encodeMemory( memory, unit ); + + this.setXmlElementAttributeValue( "size", "unit", unit ); + this.setXmlElementValue( "size", size ); + } + + /** + * Creates a non-existent shared memory device as Libvirt XML device element. + * + * @param xmlNode Libvirt XML node of the Libvirt XML device that is created. + * @return created shared memory device instance. + */ + public static Shmem createInstance( LibvirtXmlNode xmlNode ) + { + return Shmem.newInstance( xmlNode ); + } + + /** + * Creates a shared memory device representing an existing Libvirt XML shared memory device + * element. + * + * @param xmlNode existing Libvirt XML shared memory device element. + * @return shared memory device instance. + */ + public static Shmem newInstance( LibvirtXmlNode xmlNode ) + { + return new Shmem( xmlNode ); + } + + /** + * Model of shared memory device. + * + * @author Manuel Bentele + * @version 1.0 + */ + public enum Model + { + // @formatter:off + IVSHMEM ( "ivshmem" ), + IVSHMEM_PLAIN ( "ivshmem-plain" ), + IVSHMEM_DOORBELL( "ivshmem-doorbell" ); + // @formatter:on + + /** + * Name of the shared memory device model. + */ + private String model; + + /** + * Creates shared memory device model. + * + * @param type valid name of the shared memory device model in a Libvirt domain XML document. + */ + Model( String model ) + { + this.model = model; + } + + @Override + public String toString() + { + return this.model; + } + + /** + * Creates shared memory device model from its name with error check. + * + * @param model name of the shared memory device model in a Libvirt domain XML document. + * @return valid shared memory 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 index 0f8861f..f3ee13f 100644 --- a/src/main/java/org/openslx/libvirt/domain/device/Video.java +++ b/src/main/java/org/openslx/libvirt/domain/device/Video.java @@ -71,7 +71,7 @@ public class Video extends Device if ( model != null ) { if ( model == Model.VIRTIO ) { // only set acceleration on supported Virtio GPUs - this.setXmlElementAttributeValue( "model/acceleration", "accel2d", acceleration ); + this.setXmlElementAttributeValueYesNo( "model/acceleration", "accel2d", acceleration ); } else { String errorMsg = new String( "Video card model '" + model.toString() + "' does not support enabled 2D hardware acceleration." ); @@ -101,7 +101,7 @@ public class Video extends Device if ( model == Model.VIRTIO ) { // only set acceleration on supported Virtio GPUs - this.setXmlElementAttributeValue( "model/acceleration", "accel3d", acceleration ); + this.setXmlElementAttributeValueYesNo( "model/acceleration", "accel3d", acceleration ); } else { String errorMsg = new String( "Video card model '" + model.toString() + "' does not support enabled 3D hardware acceleration." ); @@ -110,6 +110,15 @@ public class Video extends Device } /** + * Disables the video device by setting the model to {@link Model#NONE}. + */ + public void disable() + { + this.removeXmlElementChilds(); + this.setModel( Model.NONE ); + } + + /** * Creates a non-existent video device as Libvirt XML device element. * * @param xmlNode Libvirt XML node of the Libvirt XML device that is created. diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java index 40d7b86..5834e58 100644 --- a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java +++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlEditable.java @@ -93,6 +93,14 @@ public interface LibvirtXmlEditable public void removeXmlElement( String expression ); /** + * Removes all child elements of the current XML root element. + */ + public default void removeXmlElementChilds() + { + this.removeXmlElementChilds( null ); + } + + /** * Removes all child elements of a XML element selected by a XPath expression. * * @param expression XPath expression to select XML element. @@ -114,9 +122,9 @@ public interface LibvirtXmlEditable /** * 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>. + * If the text value of the XML attribute equals to <i>yes</i> or <i>on</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> or <i>off</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 @@ -124,16 +132,17 @@ public interface LibvirtXmlEditable */ public default boolean getXmlElementAttributeValueAsBool( String attributeName ) { - return "yes".equals( this.getXmlElementAttributeValue( attributeName ) ); + final String attributeValue = this.getXmlElementAttributeValue( attributeName ); + return "yes".equals( attributeValue ) || "on".equals( attributeValue ); } /** * Returns the binary choice of a XML attribute from a XML element selected by a 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>. + * If the text value of the XML attribute equals to <i>yes</i> or <i>on</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> or <i>off</i>, the returned {@link boolean} value is set to <i>false</i>. * * @param expression XPath expression to select XML element. * @param attributeName name to select XML attribute of the current XML root element. @@ -142,7 +151,8 @@ public interface LibvirtXmlEditable */ public default boolean getXmlElementAttributeValueAsBool( String expression, String attributeName ) { - return "yes".equals( this.getXmlElementAttributeValue( expression, attributeName ) ); + final String attributeValue = this.getXmlElementAttributeValue( expression, attributeName ); + return "yes".equals( attributeValue ) || "on".equals( attributeValue ); } /** @@ -177,7 +187,7 @@ public interface LibvirtXmlEditable * @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 ) + public default void setXmlElementAttributeValueYesNo( String attributeName, boolean value ) { final String valueYesNo = value ? "yes" : "no"; this.setXmlElementAttributeValue( attributeName, valueYesNo ); @@ -196,13 +206,49 @@ public interface LibvirtXmlEditable * @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 ) + public default void setXmlElementAttributeValueYesNo( String expression, String attributeName, boolean value ) { final String valueYesNo = value ? "yes" : "no"; this.setXmlElementAttributeValue( expression, attributeName, valueYesNo ); } /** + * 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>on</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>off</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 setXmlElementAttributeValueOnOff( String attributeName, boolean value ) + { + final String valueOnOff = value ? "on" : "off"; + this.setXmlElementAttributeValue( attributeName, valueOnOff ); + } + + /** + * Sets the binary choice value of a XML attribute from a XML element selected by a 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>on</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>off</i>. + * + * @param expression 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 setXmlElementAttributeValueOnOff( String expression, String attributeName, boolean value ) + { + final String valueOnOff = value ? "on" : "off"; + this.setXmlElementAttributeValue( expression, attributeName, valueOnOff ); + } + + /** * Sets the text value of a XML attribute from a XML element selected by a XPath expression. * * @param expression XPath expression to select XML element. diff --git a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java index 6d00271..2110f7a 100644 --- a/src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java +++ b/src/main/java/org/openslx/libvirt/xml/LibvirtXmlNode.java @@ -184,9 +184,7 @@ public class LibvirtXmlNode implements LibvirtXmlCreatable, LibvirtXmlEditable { String completeExpression = null; - if ( expression == null ) { - completeExpression = XPATH_EXPRESSION_CURRENT_NODE; - } else if ( expression.isEmpty() ) { + if ( expression == null || expression.isEmpty() ) { completeExpression = XPATH_EXPRESSION_CURRENT_NODE; } else { completeExpression = XPATH_EXPRESSION_CURRENT_NODE + XPATH_EXPRESSION_SEPARATOR + expression; @@ -271,8 +269,9 @@ public class LibvirtXmlNode implements LibvirtXmlCreatable, LibvirtXmlEditable Node node = this.getXmlElement( expression ); if ( node != null ) { - for ( int i = 0; i < node.getChildNodes().getLength(); i++ ) { - Node child = node.getChildNodes().item( 0 ); + final NodeList childs = node.getChildNodes(); + while ( childs.getLength() > 0 ) { + Node child = childs.item( 0 ); node.removeChild( child ); } } @@ -281,13 +280,7 @@ public class LibvirtXmlNode implements LibvirtXmlCreatable, LibvirtXmlEditable @Override public String getXmlElementAttributeValue( String expression, String attributeName ) { - Node node = null; - - if ( expression != null && !expression.isEmpty() ) { - node = this.getXmlElement( expression ); - } else { - node = this.xmlBaseNode; - } + Node node = this.getXmlElement( expression ); if ( node == null ) { return null; diff --git a/src/test/java/org/openslx/libvirt/domain/device/HostdevPciDeviceAddressTest.java b/src/test/java/org/openslx/libvirt/domain/device/HostdevPciDeviceAddressTest.java new file mode 100644 index 0000000..377e126 --- /dev/null +++ b/src/test/java/org/openslx/libvirt/domain/device/HostdevPciDeviceAddressTest.java @@ -0,0 +1,79 @@ +package org.openslx.libvirt.domain.device; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +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.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class HostdevPciDeviceAddressTest +{ + @Test + @DisplayName( "Test that a PCI device address instance is not created if invalid values are specified" ) + public void testHostdevPciDeviceAddressInstanceInvalid() + { + assertThrows( IllegalArgumentException.class, + () -> new HostdevPciDeviceAddress( Integer.MIN_VALUE, 0x03, 0x0a, 0x7 ) ); + assertThrows( IllegalArgumentException.class, + () -> new HostdevPciDeviceAddress( 0x72ab, 0x0c, 0x1a, Integer.MAX_VALUE ) ); + } + + @Test + @DisplayName( "Test that a PCI device address instance is parsed from a valid String" ) + public void testHostdevPciDeviceAddressValueOfValid() + { + final HostdevPciDeviceAddress pciDeviceAddr = HostdevPciDeviceAddress.valueOf( "002b:2a:1f.1" ); + + assertNotNull( pciDeviceAddr ); + assertEquals( 0x002b, pciDeviceAddr.getPciDomain() ); + assertEquals( "002b", pciDeviceAddr.getPciDomainAsString() ); + assertEquals( 0x2a, pciDeviceAddr.getPciBus() ); + assertEquals( "2a", pciDeviceAddr.getPciBusAsString() ); + assertEquals( 0x1f, pciDeviceAddr.getPciDevice() ); + assertEquals( "1f", pciDeviceAddr.getPciDeviceAsString() ); + assertEquals( 0x1, pciDeviceAddr.getPciFunction() ); + assertEquals( "1", pciDeviceAddr.getPciFunctionAsString() ); + } + + @Test + @DisplayName( "Test that no PCI device address instance is parsed from an invalid String" ) + public void testHostdevPciDeviceAddressValueOfInvalid() + { + final HostdevPciDeviceAddress pciDeviceAddr = HostdevPciDeviceAddress.valueOf( "0000b2ac1f31" ); + + assertNull( pciDeviceAddr ); + } + + @Test + @DisplayName( "Test that two PCI device address instances are equal" ) + public void testHostdevPciDeviceAddressEquals() + { + final HostdevPciDeviceAddress pciDeviceAddr1 = new HostdevPciDeviceAddress( 0x0000, 0x2a, 0x1f, 0x1 ); + final HostdevPciDeviceAddress pciDeviceAddr2 = new HostdevPciDeviceAddress( 0x0000, 0x2a, 0x1f, 0x1 ); + + assertTrue( pciDeviceAddr1.equals( pciDeviceAddr2 ) ); + } + + @Test + @DisplayName( "Test that two PCI device address instances are not equal" ) + public void testHostdevPciDeviceAddressNotEquals() + { + final HostdevPciDeviceAddress pciDeviceAddr1 = new HostdevPciDeviceAddress( 0x0000, 0x2a, 0x1f, 0x1 ); + final HostdevPciDeviceAddress pciDeviceAddr2 = new HostdevPciDeviceAddress( 0x0000, 0x2a, 0x1f, 0x2 ); + + assertFalse( pciDeviceAddr1.equals( pciDeviceAddr2 ) ); + } + + @Test + @DisplayName( "Test that a PCI device address can be dumped to a String" ) + public void testHostdevPciDeviceAddressToString() + { + final HostdevPciDeviceAddress pciDeviceAddr = new HostdevPciDeviceAddress( 0x0000, 0x2a, 0x1f, 0x1 ); + + assertEquals( "0000:2a:1f.1", pciDeviceAddr.toString() ); + } +} diff --git a/src/test/java/org/openslx/libvirt/domain/device/HostdevPciDeviceDescriptionTest.java b/src/test/java/org/openslx/libvirt/domain/device/HostdevPciDeviceDescriptionTest.java new file mode 100644 index 0000000..7a740ac --- /dev/null +++ b/src/test/java/org/openslx/libvirt/domain/device/HostdevPciDeviceDescriptionTest.java @@ -0,0 +1,75 @@ +package org.openslx.libvirt.domain.device; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +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.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class HostdevPciDeviceDescriptionTest +{ + @Test + @DisplayName( "Test that a PCI device description instance is not created if invalid values are specified" ) + public void testHostdevPciDeviceDescriptionInstanceInvalid() + { + assertThrows( IllegalArgumentException.class, + () -> new HostdevPciDeviceDescription( Integer.MIN_VALUE, 0x293a ) ); + assertThrows( IllegalArgumentException.class, + () -> new HostdevPciDeviceDescription( 0x8086, Integer.MAX_VALUE ) ); + } + + @Test + @DisplayName( "Test that a PCI device description instance is parsed from a valid String" ) + public void testHostdevPciDeviceDescriptionValueOfValid() + { + final HostdevPciDeviceDescription pciDeviceDesc = HostdevPciDeviceDescription.valueOf( "8086:293a" ); + + assertNotNull( pciDeviceDesc ); + assertEquals( 0x8086, pciDeviceDesc.getVendorId() ); + assertEquals( "8086", pciDeviceDesc.getVendorIdAsString() ); + assertEquals( 0x293a, pciDeviceDesc.getDeviceId() ); + assertEquals( "293a", pciDeviceDesc.getDeviceIdAsString() ); + } + + @Test + @DisplayName( "Test that no PCI device description instance is parsed from an invalid String" ) + public void testHostdevPciDeviceDescriptionValueOfInvalid() + { + final HostdevPciDeviceDescription pciDeviceDesc = HostdevPciDeviceDescription.valueOf( "bba93e215" ); + + assertNull( pciDeviceDesc ); + } + + @Test + @DisplayName( "Test that two PCI device description instances are equal" ) + public void testHostdevPciDeviceDescriptionEquals() + { + final HostdevPciDeviceDescription pciDeviceDesc1 = new HostdevPciDeviceDescription( 0x8086, 0x293a ); + final HostdevPciDeviceDescription pciDeviceDesc2 = new HostdevPciDeviceDescription( 0x8086, 0x293a ); + + assertTrue( pciDeviceDesc1.equals( pciDeviceDesc2 ) ); + } + + @Test + @DisplayName( "Test that two PCI device description instances are not equal" ) + public void testHostdevPciDeviceDescriptionNotEquals() + { + final HostdevPciDeviceDescription pciDeviceDesc1 = new HostdevPciDeviceDescription( 0x8086, 0x293a ); + final HostdevPciDeviceDescription pciDeviceDesc2 = new HostdevPciDeviceDescription( 0x293a, 0x8086 ); + + assertFalse( pciDeviceDesc1.equals( pciDeviceDesc2 ) ); + } + + @Test + @DisplayName( "Test that a PCI device description can be dumped to a String" ) + public void testHostdevPciDeviceDescriptionToString() + { + final HostdevPciDeviceDescription pciDeviceDesc = new HostdevPciDeviceDescription( 0x00b1, 0x293a ); + + assertEquals( "00b1:293a", pciDeviceDesc.toString() ); + } +} |