summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2025-03-27 14:10:08 +0100
committerSimon Rettberg2025-03-27 14:10:08 +0100
commita306c5617fd1d4e09739655fa7070a300dac2b13 (patch)
tree8576afa93831213dbbebb81d7a796cf59b5416d0
parentFix comment (diff)
downloadmaster-sync-shared-a306c56.tar.gz
master-sync-shared-a306c56.tar.xz
master-sync-shared-a306c56.zip
[libvirt] Make sure VMs have at least 5 spicevmc usb ports
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java9
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/Device.java62
-rw-r--r--src/main/java/org/openslx/libvirt/domain/device/RedirDevice.java13
-rw-r--r--src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java65
-rw-r--r--src/main/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToStatelessClient.java2
-rw-r--r--src/test/java/org/openslx/libvirt/domain/DomainTest.java2
-rw-r--r--src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java4
7 files changed, 123 insertions, 34 deletions
diff --git a/src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java b/src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java
index 1798027..1be42e5 100644
--- a/src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java
+++ b/src/main/java/org/openslx/libvirt/domain/device/ControllerUsb.java
@@ -1,6 +1,7 @@
package org.openslx.libvirt.domain.device;
import org.openslx.libvirt.xml.LibvirtXmlNode;
+import org.openslx.util.Util;
/**
* A USB controller device node in a Libvirt domain XML document.
@@ -136,4 +137,12 @@ public class ControllerUsb extends Controller
return null;
}
}
+
+ /**
+ * Get number of ports this controller provides.
+ */
+ public int getPortCount()
+ {
+ return Util.parseInt( this.getXmlElementAttributeValue( "ports" ), -1 );
+ }
}
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 d743522..f391663 100644
--- a/src/main/java/org/openslx/libvirt/domain/device/Device.java
+++ b/src/main/java/org/openslx/libvirt/domain/device/Device.java
@@ -1,5 +1,6 @@
package org.openslx.libvirt.domain.device;
+import org.apache.commons.lang3.NotImplementedException;
import org.openslx.libvirt.xml.LibvirtXmlNode;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -48,7 +49,7 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
* @param deviceType type of the Libvirt XML device element.
* @return created Libvirt XML device node.
*/
- private static LibvirtXmlNode createDeviceElement( LibvirtXmlNode xmlParentNode, Type deviceType )
+ private static LibvirtXmlNode createDeviceElement( LibvirtXmlNode xmlParentNode, DeviceClass deviceType )
{
// create XML element as part of the Libvirt XML document
Document xmlDocument = xmlParentNode.getXmlDocument();
@@ -72,38 +73,41 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
Device createdDevice = null;
if ( device instanceof Controller ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.CONTROLLER );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.CONTROLLER );
createdDevice = Controller.createInstance( Controller.class.cast( device ), xmlNode );
} else if ( device instanceof Disk ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.DISK );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.DISK );
createdDevice = Disk.createInstance( Disk.class.cast( device ), xmlNode );
} else if ( device instanceof FileSystem ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.FILESYSTEM );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.FILESYSTEM );
createdDevice = FileSystem.createInstance( xmlNode );
} else if ( device instanceof Hostdev ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.HOSTDEV );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.HOSTDEV );
createdDevice = Hostdev.createInstance( Hostdev.class.cast( device ), xmlNode );
} else if ( device instanceof Interface ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.INTERFACE );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.INTERFACE );
createdDevice = Interface.createInstance( Interface.class.cast( device ), xmlNode );
} else if ( device instanceof Graphics ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.GRAPHICS );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.GRAPHICS );
createdDevice = Graphics.createInstance( Graphics.class.cast( device ), xmlNode );
} else if ( device instanceof Parallel ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.PARALLEL );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.PARALLEL );
createdDevice = Parallel.createInstance( xmlNode );
} else if ( device instanceof Serial ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.SERIAL );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.SERIAL );
createdDevice = Serial.createInstance( xmlNode );
} else if ( device instanceof Shmem ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.SHMEM );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.SHMEM );
createdDevice = Shmem.createInstance( xmlNode );
} else if ( device instanceof Sound ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.SOUND );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.SOUND );
createdDevice = Sound.createInstance( xmlNode );
} else if ( device instanceof Video ) {
- LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, Type.VIDEO );
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.VIDEO );
createdDevice = Video.createInstance( xmlNode );
+ } else if ( device instanceof RedirDevice ) {
+ LibvirtXmlNode xmlNode = Device.createDeviceElement( xmlParentNode, DeviceClass.REDIRDEV );
+ createdDevice = RedirDevice.createInstance( xmlNode );
}
return createdDevice;
@@ -124,10 +128,10 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
return null;
} else {
Device device = null;
- Type type = Type.fromString( element.getNodeName() );
+ DeviceClass type = DeviceClass.fromString( element.getNodeName() );
if ( type == null ) {
- return null;
+ return new Device( xmlNode );
}
switch ( type ) {
@@ -167,6 +171,8 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
case VIDEO:
device = Video.newInstance( xmlNode );
break;
+ default:
+ throw new NotImplementedException( "Class not implemented" );
}
return device;
@@ -260,7 +266,23 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
return HostdevUsbDeviceAddress.valueOf( bus, port );
}
-
+
+ /**
+ * Returns which bus this device is connected to, or null if unknown
+ */
+ public BusType getDeviceBusType()
+ {
+ return BusType.fromString( this.getXmlElementAttributeValue( "bus" ) );
+ }
+
+ /**
+ * Get class of device, i.e. enum value representing the XML tag
+ */
+ public DeviceClass getDeviceClass()
+ {
+ return DeviceClass.fromString( this.getXmlBaseNode().getLocalName() );
+ }
+
/**
* Returns this devices USB bus/port address, or null if it doesn't have an explicit one
* set, or if the address type isn't USB.
@@ -275,7 +297,6 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
public void setUsbTarget( HostdevUsbDeviceAddress address )
{
this.setUsbAddress( "address", address );
- this.setXmlElementAttributeValue( "address", "type", "pci" );
}
/**
@@ -284,7 +305,7 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
* @author Manuel Bentele
* @version 1.0
*/
- enum Type
+ public enum DeviceClass
{
// @formatter:off
CONTROLLER( "controller" ),
@@ -311,7 +332,7 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
*
* @param type valid name of the virtual machine device type in a Libvirt domain XML document.
*/
- Type( String type )
+ DeviceClass( String type )
{
this.type = type;
}
@@ -328,9 +349,9 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
* @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 )
+ public static DeviceClass fromString( String type )
{
- for ( Type t : Type.values() ) {
+ for ( DeviceClass t : DeviceClass.values() ) {
if ( t.type.equalsIgnoreCase( type ) ) {
return t;
}
@@ -339,4 +360,5 @@ public class Device extends LibvirtXmlNode implements HostdevAddressableTarget<H
return null;
}
}
+
}
diff --git a/src/main/java/org/openslx/libvirt/domain/device/RedirDevice.java b/src/main/java/org/openslx/libvirt/domain/device/RedirDevice.java
index 2656bbe..d841413 100644
--- a/src/main/java/org/openslx/libvirt/domain/device/RedirDevice.java
+++ b/src/main/java/org/openslx/libvirt/domain/device/RedirDevice.java
@@ -42,14 +42,6 @@ public class RedirDevice extends Device
{
this.setXmlElementAttributeValue( "type", type.toString() );
}
-
- /**
- * Get bus type.
- */
- public BusType getBus()
- {
- return BusType.fromString( this.getXmlElementAttributeValue( "bus" ) );
- }
/**
* Creates a non-existent video device as Libvirt XML device element.
@@ -126,4 +118,9 @@ public class RedirDevice extends Device
return null;
}
}
+
+ public void setBusType( BusType usb )
+ {
+ this.setXmlElementAttributeValue( "bus", usb.toString() );
+ }
}
diff --git a/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java
index d714fc4..4952d3d 100644
--- a/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java
+++ b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java
@@ -13,6 +13,7 @@ import org.openslx.libvirt.domain.Domain;
import org.openslx.libvirt.domain.DomainUtils;
import org.openslx.libvirt.domain.device.BusType;
import org.openslx.libvirt.domain.device.ControllerUsb;
+import org.openslx.libvirt.domain.device.Device;
import org.openslx.libvirt.domain.device.Disk;
import org.openslx.libvirt.domain.device.Disk.StorageType;
import org.openslx.libvirt.domain.device.DiskCdrom;
@@ -20,8 +21,10 @@ 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.HostdevUsbDeviceAddress;
import org.openslx.libvirt.domain.device.Interface;
import org.openslx.libvirt.domain.device.RedirDevice;
+import org.openslx.libvirt.domain.device.RedirDevice.SrcType;
import org.openslx.libvirt.domain.device.Sound;
import org.openslx.libvirt.domain.device.Video;
import org.openslx.libvirt.libosinfo.LibOsInfo;
@@ -47,6 +50,7 @@ import org.openslx.virtualization.virtualizer.VirtualizerQemu;
*/
public class VirtualizationConfigurationQemu extends VirtualizationConfiguration
{
+
/**
* Name of the network bridge for the LAN.
*/
@@ -117,7 +121,7 @@ public class VirtualizationConfigurationQemu extends VirtualizationConfiguration
try {
// read and parse Libvirt domain XML configuration document
- this.vmConfig = new Domain( new String( vmContent, StandardCharsets.UTF_8 ) );
+ this.vmConfig = new Domain( new String( vmContent, 0, length, StandardCharsets.UTF_8 ) );
} catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) {
throw new VirtualizationConfigurationException( e.getLocalizedMessage() );
}
@@ -127,6 +131,61 @@ public class VirtualizationConfigurationQemu extends VirtualizationConfiguration
}
/**
+ * See if a USB controller is present and if so, make sure we have
+ * a bunch of spicevmc ports for dynamic pass-through.
+ *
+ * @param onlyIfAtLeastOne Only add more spicevmc usb devices if we have at least one existing
+ */
+ private void preconfigureUsb( boolean onlyIfAtLeastOne )
+ {
+ final int MAX_BUSES = 4; // We assume max. 4 USB controllers
+ final int MAX_DEV_PER_BUS = 16; // ...and max. 16 ports on a controller
+ final int MIN_USB_REDIR_DEVS = 5; // 5 additional USB devices ought to be enough for everyone
+ int devs = 0;
+ byte busPort[][] = new byte[ MAX_BUSES ][];
+
+ for ( int i = 0; i < busPort.length; ++i ) {
+ busPort[i] = new byte[ MAX_DEV_PER_BUS ];
+ }
+ // Check how many we got, and what they are connected to
+ for ( Device d : vmConfig.getDevices() ) {
+ // is spicevmc?
+ if ( d.getDeviceBusType() == BusType.USB && d.getDeviceClass() == Device.DeviceClass.REDIRDEV
+ && "spicevmc".equals( d.getXmlElementAttributeValue( "type" ) ) ) {
+ devs++;
+ }
+ // Which bus/port?
+ HostdevUsbDeviceAddress addr = d.getUsbTarget();
+ if ( addr == null )
+ continue;
+ int bus = addr.getUsbBus();
+ int port = addr.getUsbPort();
+ if ( bus >= 0 && port > 0 && bus < busPort.length && port < busPort[bus].length ) {
+ busPort[bus][port] = 1;
+ }
+ }
+ if ( devs >= MIN_USB_REDIR_DEVS )
+ return;
+ for ( ControllerUsb c : vmConfig.getUsbControllerDevices() ) {
+ int bus = c.getIndex();
+ if ( bus < 0 || bus >= busPort.length )
+ continue;
+ int ports = c.getPortCount();
+ for ( int port = 1; port < ports; ++port ) {
+ if ( busPort[bus][port] == 0 ) {
+ // Free port on this controller, use
+ RedirDevice dev = (RedirDevice)vmConfig.addDevice( new RedirDevice() );
+ dev.setSrcType( SrcType.SPICEVMC );
+ dev.setBusType( BusType.USB );
+ dev.setUsbTarget( new HostdevUsbDeviceAddress( bus, port ) );
+ if ( ++devs >= MIN_USB_REDIR_DEVS )
+ return; // Enough
+ }
+ }
+ }
+ }
+
+ /**
* Parses Libvirt domain XML configuration to initialize QEMU metadata.
*/
private void parseVmConfig()
@@ -832,7 +891,7 @@ public class VirtualizationConfigurationQemu extends VirtualizationConfiguration
@Override
public void transformNonPersistent() throws VirtualizationConfigurationException
{
- // NOT implemented yet
+ this.preconfigureUsb( false );
}
@Override
@@ -914,7 +973,7 @@ public class VirtualizationConfigurationQemu extends VirtualizationConfiguration
// attach any new USB devices.
ArrayList<RedirDevice> list = vmConfig.getRedirectDevices();
for (RedirDevice dev : list ) {
- if ( dev.getBus() == BusType.USB ) {
+ if ( dev.getDeviceBusType() == BusType.USB ) {
dev.remove();
}
}
diff --git a/src/main/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToStatelessClient.java b/src/main/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToStatelessClient.java
index 2d3e861..8703769 100644
--- a/src/main/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToStatelessClient.java
+++ b/src/main/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToStatelessClient.java
@@ -73,6 +73,8 @@ public class ConfigurationLogicDozModServerToStatelessClient
this.validateInputs( config, args );
// apply settings to run virtualized system in a stateless manner
+ // call this one early on as methods further down might customize
+ // remove things we do here...
try {
config.transformNonPersistent();
} catch ( VirtualizationConfigurationException e ) {
diff --git a/src/test/java/org/openslx/libvirt/domain/DomainTest.java b/src/test/java/org/openslx/libvirt/domain/DomainTest.java
index d73abe0..5fccd1c 100644
--- a/src/test/java/org/openslx/libvirt/domain/DomainTest.java
+++ b/src/test/java/org/openslx/libvirt/domain/DomainTest.java
@@ -412,7 +412,7 @@ public class DomainTest
public void testGetDevices()
{
Domain vm = DomainTest.getDomain( "qemu-kvm_default-ubuntu-20-04-vm.xml" );
- assertEquals( 24, vm.getDevices().size() );
+ assertEquals( 33, vm.getDevices().size() );
}
@Test
diff --git a/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java b/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java
index 04dc118..5505509 100644
--- a/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java
+++ b/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java
@@ -490,11 +490,11 @@ public class VirtualizationConfigurationQemuTest
final Domain vmLibvirtDomainConfig = VirtualizationConfigurationQemuTest
.getPrivateDomainFromQemuMetaData( vmConfig );
- assertEquals( vmLibvirtDomainConfig.getRedirectDevices().size(), 2 );
+ assertEquals( 2, vmLibvirtDomainConfig.getRedirectDevices().size() );
vmConfig.disableUsb();
- assertEquals( vmLibvirtDomainConfig.getRedirectDevices().size(), 0 );
+ assertEquals( 0, vmLibvirtDomainConfig.getRedirectDevices().size() );
assertDoesNotThrow( () -> vmConfig.validate() );
}