summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBox.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBox.java')
-rw-r--r--src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBox.java601
1 files changed, 601 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBox.java b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBox.java
new file mode 100644
index 0000000..153fffa
--- /dev/null
+++ b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBox.java
@@ -0,0 +1,601 @@
+package org.openslx.virtualization.configuration;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.openslx.bwlp.thrift.iface.OperatingSystem;
+import org.openslx.thrifthelper.TConst;
+import org.openslx.util.Util;
+import org.openslx.virtualization.Version;
+import org.openslx.virtualization.configuration.VirtualizationConfigurationVirtualboxFileFormat.MatchMode;
+import org.openslx.virtualization.hardware.ConfigurationGroups;
+import org.openslx.virtualization.hardware.Ethernet;
+import org.openslx.virtualization.hardware.SoundCard;
+import org.openslx.virtualization.hardware.Usb;
+import org.openslx.virtualization.hardware.VirtOptionValue;
+import org.openslx.virtualization.virtualizer.VirtualizerVirtualBox;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class VirtualizationConfigurationVirtualBox extends VirtualizationConfiguration
+{
+ /**
+ * File name extension for VirtualBox virtualization configuration files..
+ */
+ public static final String FILE_NAME_EXTENSION = "vbox";
+
+ private static final Logger LOGGER = LogManager.getLogger( VirtualizationConfigurationVirtualBox.class );
+
+ private final VirtualizationConfigurationVirtualboxFileFormat config;
+
+ public static enum EthernetType
+ {
+ NAT( "vboxnet1" ), BRIDGED( "vboxnet0" ), HOST_ONLY( "vboxnet2" );
+
+ public final String vnet;
+
+ private EthernetType( String vnet )
+ {
+ this.vnet = vnet;
+ }
+ }
+
+ public VirtualizationConfigurationVirtualBox( List<OperatingSystem> osList, File file )
+ throws IOException, VirtualizationConfigurationException
+ {
+ super( new VirtualizerVirtualBox(), osList );
+ this.config = new VirtualizationConfigurationVirtualboxFileFormat( file );
+ init();
+ }
+
+ public VirtualizationConfigurationVirtualBox( List<OperatingSystem> osList, byte[] vmContent, int length )
+ throws IOException, VirtualizationConfigurationException
+ {
+ super( new VirtualizerVirtualBox(), osList );
+ this.config = new VirtualizationConfigurationVirtualboxFileFormat( vmContent, length );
+ init();
+ }
+
+ private void init()
+ {
+ displayName = config.getDisplayName();
+ setOs( config.getOsName() );
+ this.isMachineSnapshot = config.isMachineSnapshot();
+ for ( HardDisk hardDisk : config.getHdds() ) {
+ hdds.add( hardDisk );
+ }
+ }
+
+ private void disableEnhancedNetworkAdapters()
+ {
+ final NodeList disableAdapters = this.config.findNodes( "/VirtualBox/Machine/Hardware/Network/Adapter[not(@slot='0')]" );
+
+ if ( disableAdapters != null ) {
+ for ( int i = 0; i < disableAdapters.getLength(); i++ ) {
+ final Element disableAdapter = (Element)disableAdapters.item( i );
+ disableAdapter.setAttribute( "enabled", "false" );
+ }
+ }
+ }
+
+ private void removeEnhancedNetworkAdapters()
+ {
+ final NodeList removeAdapters = this.config.findNodes( "/VirtualBox/Machine/Hardware/Network/Adapter[not(@slot='0')]" );
+
+ if ( removeAdapters != null ) {
+ for ( int i = 0; i < removeAdapters.getLength(); i++ ) {
+ final Node removeAdapter = removeAdapters.item( i );
+ removeAdapter.getParentNode().removeChild( removeAdapter );
+ }
+ }
+ }
+
+ @Override
+ public void transformEditable() throws VirtualizationConfigurationException
+ {
+ this.disableEnhancedNetworkAdapters();
+ }
+
+ @Override
+ public void transformPrivacy() throws VirtualizationConfigurationException
+ {
+ config.addPlaceHolders();
+ }
+
+ @Override
+ public byte[] getConfigurationAsByteArray()
+ {
+ return config.toString( true ).getBytes( StandardCharsets.UTF_8 );
+ }
+
+ @Override
+ public boolean addEmptyHddTemplate()
+ {
+ return this.addHddTemplate( VirtualizationConfigurationVirtualboxFileFormat.DUMMY_VALUE,
+ VirtualizationConfigurationVirtualboxFileFormat.DUMMY_VALUE,
+ VirtualizationConfigurationVirtualboxFileFormat.DUMMY_VALUE );
+ }
+
+ @Override
+ public boolean addHddTemplate( String diskImage, String hddMode, String redoDir )
+ {
+ config.changeAttribute( "/VirtualBox/Machine/MediaRegistry/HardDisks/HardDisk", "location", diskImage, MatchMode.FIRST_ONLY );
+ config.changeAttribute( "/VirtualBox/Machine", "snapshotFolder", redoDir, MatchMode.FIRST_ONLY );
+ return true;
+ }
+
+ @Override
+ public boolean addHddTemplate( File diskImage, String hddMode, String redoDir )
+ {
+ String diskImagePath = diskImage.getName();
+ config.changeAttribute( "/VirtualBox/Machine/MediaRegistry/HardDisks/HardDisk", "location", diskImagePath, MatchMode.FIRST_ONLY );
+
+ UUID newhdduuid = UUID.randomUUID();
+
+ // patching the new uuid in the vbox config file here
+ String vboxUUid = "{" + newhdduuid.toString() + "}";
+ config.changeAttribute( "/VirtualBox/Machine/MediaRegistry/HardDisks/HardDisk", "uuid", vboxUUid, MatchMode.FIRST_ONLY );
+ config.changeAttribute( config.storageControllersPath() + "/StorageController/AttachedDevice/Image", "uuid",
+ vboxUUid, MatchMode.FIRST_ONLY );
+
+ // the order of the UUID is BIG_ENDIAN but we need to change the order of the first 8 Bytes
+ // to be able to write them to the vdi file... the PROBLEM here is that the first 8
+ // are in LITTLE_ENDIAN order in pairs of 4-2-2 not the whole 8 so just changing the
+ // order when we are adding them to the bytebuffer won't help
+ //
+ // the following is a workaround that works
+ ByteBuffer buffer = ByteBuffer.wrap( new byte[ 16 ] );
+ buffer.putLong( newhdduuid.getMostSignificantBits() );
+ buffer.putLong( newhdduuid.getLeastSignificantBits() );
+ byte[] oldOrder = buffer.array();
+ // make a coppy here because the last 8 Bytes don't need to change position
+ byte[] bytesToWrite = Arrays.copyOf( oldOrder, oldOrder.length );
+ // use an offset int[] to help with the shuffle
+ int[] offsets = { 3, 2, 1, 0, 5, 4, 7, 6 };
+ for ( int index = 0; index < 8; index++ ) {
+ bytesToWrite[index] = oldOrder[offsets[index]];
+ }
+ try ( RandomAccessFile file = new RandomAccessFile( diskImage, "rw" ) ) {
+ file.seek( 392 );
+ file.write( bytesToWrite, 0, 16 );
+ } catch ( Exception e ) {
+ LOGGER.warn( "could not patch new uuid in the vdi", e );
+ }
+
+ // we need a new machine uuid
+ UUID newMachineUuid = UUID.randomUUID();
+ if ( newMachineUuid.equals( newhdduuid ) ) {
+ LOGGER.warn( "The new Machine UUID is the same as the new HDD UUID; tying again...this vm might not start" );
+ newMachineUuid = UUID.randomUUID();
+ }
+ String machineUUid = "{" + newMachineUuid.toString() + "}";
+ return config.changeAttribute( "/VirtualBox/Machine", "uuid", machineUUid, MatchMode.EXACTLY_ONE );
+ }
+
+ @Override
+ public boolean addDefaultNat()
+ {
+ final boolean status;
+
+ final Node adapterSlot0 = config.findNodes( "/VirtualBox/Machine/Hardware/Network/Adapter[@slot='0']" ).item( 0 );
+ if ( adapterSlot0 != null ) {
+ // remove all child node to wipe existing networking mode
+ final NodeList adapterSlot0SettingNodes = adapterSlot0.getChildNodes();
+ while ( adapterSlot0.getChildNodes().getLength() > 0 ) {
+ adapterSlot0.removeChild( adapterSlot0SettingNodes.item( 0 ) );
+ }
+
+ // add networking mode 'NAT'
+ if ( config.addNewNode( "/VirtualBox/Machine/Hardware/Network/Adapter[@slot='0']", "NAT" ) == null ) {
+ LOGGER.error( "Failed to set network adapter to NAT." );
+ status = false;
+ } else {
+ status = config.changeAttribute( "/VirtualBox/Machine/Hardware/Network/Adapter[@slot='0']", "MACAddress",
+ "080027B86D12", MatchMode.EXACTLY_ONE );
+ }
+ } else {
+ status = false;
+ }
+
+ return status;
+ }
+
+ @Override
+ public void setOs( String vendorOsId )
+ {
+ config.changeAttribute( "/VirtualBox/Machine", "OSType", vendorOsId, MatchMode.EXACTLY_ONE );
+
+ final OperatingSystem os = VirtualizationConfigurationUtils.getOsOfVirtualizerFromList( this.osList,
+ TConst.VIRT_VIRTUALBOX, vendorOsId );
+ this.setOs( os );
+ }
+
+ @Override
+ public boolean addDisplayName( String name )
+ {
+ return config.changeAttribute( "/VirtualBox/Machine", "name", name, MatchMode.EXACTLY_ONE );
+ }
+
+ @Override
+ public boolean addRam( int mem )
+ {
+ return config.changeAttribute( "/VirtualBox/Machine/Hardware/Memory", "RAMSize",
+ Integer.toString( mem ), MatchMode.EXACTLY_ONE );
+ }
+
+ @Override
+ public void addFloppy( int index, String image, boolean readOnly )
+ {
+ Element floppyController = null;
+ NodeList matches = config.findNodes( config.storageControllersPath() + "/StorageController[@name='Floppy']" );
+ if ( matches == null || matches.getLength() == 0 ) {
+ floppyController = (Element)config.addNewNode( config.storageControllersPath(), "StorageController" );
+ if ( floppyController == null ) {
+ LOGGER.error( "Failed to add <Image> to floppy device." );
+ return;
+ }
+ floppyController.setAttribute( "name", "Floppy" );
+ floppyController.setAttribute( "type", "I82078" );
+ floppyController.setAttribute( "PortCount", "1" );
+ floppyController.setAttribute( "useHostIOCache", "true" );
+ floppyController.setAttribute( "Bootable", "false" );
+ }
+ // virtualbox only allows one controller per type
+ if ( matches.getLength() > 1 ) {
+ LOGGER.error( "Multiple floppy controllers detected, this should never happen! " );
+ return;
+ }
+ // so if we had any matches, we know we have exactly one
+ if ( floppyController == null )
+ floppyController = (Element)matches.item( 0 );
+
+ // add the floppy device
+ Element floppyDevice = (Element)config.addNewNode( floppyController, "AttachedDevice" );
+ if ( floppyDevice == null ) {
+ LOGGER.error( "Failed to add <Image> to floppy device." );
+ return;
+ }
+ floppyDevice.setAttribute( "type", "Floppy" );
+ floppyDevice.setAttribute( "hotpluggable", "false" );
+ floppyDevice.setAttribute( "port", "0" );
+ floppyDevice.setAttribute( "device", Integer.toString( index ) );
+
+ // finally add the image to it, if one was given
+ if ( image != null ) {
+ Element floppyImage = (Element)config.addNewNode( floppyDevice, "Image" );
+ if ( floppyImage == null ) {
+ LOGGER.error( "Failed to add <Image> to floppy device." );
+ return;
+ }
+ floppyImage.setAttribute( "uuid",
+ VirtualizationConfigurationVirtualboxFileFormat.DUMMY_VALUE );
+ // register the image in the media registry
+ Element floppyImages = (Element)config.addNewNode( "/VirtualBox/Machine/MediaRegistry", "FloppyImages" );
+ if ( floppyImages == null ) {
+ LOGGER.error( "Failed to add <FloppyImages> to media registry." );
+ return;
+ }
+ Element floppyImageReg = (Element)config.addNewNode( "/VirtualBox/Machine/MediaRegistry/FloppyImages",
+ "Image" );
+ if ( floppyImageReg == null ) {
+ LOGGER.error( "Failed to add <Image> to floppy images in the media registry." );
+ return;
+ }
+ floppyImageReg.setAttribute( "uuid",
+ VirtualizationConfigurationVirtualboxFileFormat.DUMMY_VALUE );
+ floppyImageReg.setAttribute( "location",
+ VirtualizationConfigurationVirtualboxFileFormat.DUMMY_VALUE );
+ }
+ }
+
+ @Override
+ public boolean addCdrom( String image )
+ {
+ // TODO - done in run-virt currently
+ return false;
+ }
+
+ @Override
+ public boolean addCpuCoreCount( int nrOfCores )
+ {
+ return config.changeAttribute( "/VirtualBox/Machine/Hardware/CPU", "count",
+ Integer.toString( nrOfCores ), MatchMode.EXACTLY_ONE );
+ }
+
+ class VBoxSoundCardModel extends VirtOptionValue
+ {
+
+ public VBoxSoundCardModel( String id, String displayName )
+ {
+ super( id, displayName );
+ }
+
+ @Override
+ public void apply()
+ {
+ // XXX I guess this "present" hack will be nicer with enum too
+ if ( Util.isEmptyString( this.id ) ) {
+ config.changeAttribute( "/VirtualBox/Machine/Hardware/AudioAdapter", "enabled", "false", MatchMode.MULTIPLE );
+ return;
+ }
+ config.changeAttribute( "/VirtualBox/Machine/Hardware/AudioAdapter", "enabled", "true", MatchMode.FIRST_ONLY );
+ config.changeAttribute( "/VirtualBox/Machine/Hardware/AudioAdapter", "controller", this.id, MatchMode.FIRST_ONLY );
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ Element x = (Element)config.findNodes( "/VirtualBox/Machine/Hardware/AudioAdapter" ).item( 0 );
+ if ( !x.hasAttribute( "enabled" )
+ || ( x.hasAttribute( "enabled" ) && x.getAttribute( "enabled" ).equals( "false" ) ) ) {
+ return Util.isEmptyString( this.id ); // XXX enum
+ }
+ String val = "AC97";
+ if ( x.hasAttribute( "controller" ) ) {
+ val = x.getAttribute( "controller" );
+ }
+ return val.equals( this.id );
+ }
+
+ }
+
+ class VBoxAccel3D extends VirtOptionValue
+ {
+
+ public VBoxAccel3D( String id, String displayName )
+ {
+ super( id, displayName );
+ }
+
+ @Override
+ public void apply()
+ {
+ config.changeAttribute( "/VirtualBox/Machine/Hardware/Display", "accelerate3D", this.id, MatchMode.EXACTLY_ONE );
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ Element x = (Element)config.findNodes( "/VirtualBox/Machine/Hardware/Display" ).item( 0 );
+ String val = "false";
+ if ( x.hasAttribute( "accelerate3D" ) ) {
+ val = x.getAttribute( "accelerate3D" );
+ }
+ return val.equalsIgnoreCase( this.id );
+ }
+
+ }
+
+ /**
+ * Function does nothing for Virtual Box;
+ * Virtual Box accepts per default only one hardware version and is hidden from the user
+ */
+ @Override
+ public void setVirtualizerVersion( Version type )
+ {
+ }
+
+ public Version getConfigurationVersion()
+ {
+ return this.config.getVersion();
+ }
+
+ @Override
+ public Version getVirtualizerVersion()
+ {
+ // Virtual Box uses only one virtual hardware version and can't be changed
+ return null;
+ }
+
+ class VBoxNicModel extends VirtOptionValue
+ {
+
+ private final int cardIndex;
+
+ public VBoxNicModel( int cardIndex, String id, String displayName )
+ {
+ super( id, displayName );
+ this.cardIndex = cardIndex;
+ }
+
+ @Override
+ public void apply()
+ {
+ String index = Integer.toString( this.cardIndex );
+ String dev = this.id;
+ boolean present = true;
+ if ( "".equals( this.id ) ) {
+ // none type needs to have a valid value; it takes the value of pcnetcpi2;
+ // if value is left null or empty vm will not start because value is not valid
+ dev = "Am79C970A";
+ present = false;
+ }
+ config.changeAttribute( "/VirtualBox/Machine/Hardware/Network/Adapter[@slot='" + index + "']", "enabled",
+ Boolean.toString( present ), MatchMode.EXACTLY_ONE );
+ config.changeAttribute( "/VirtualBox/Machine/Hardware/Network/Adapter[@slot='" + index + "']", "type",
+ dev, MatchMode.EXACTLY_ONE );
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ Element x = (Element)config.findNodes( "/VirtualBox/Machine/Hardware/Network/Adapter" ).item( 0 );
+ if ( !x.hasAttribute( "enabled" )
+ || ( x.hasAttribute( "enabled" ) && x.getAttribute( "enabled" ).equalsIgnoreCase( "false" ) ) ) {
+ return Util.isEmptyString( this.id );
+ }
+ // Has NIC
+ if ( !x.hasAttribute( "type" ) ) {
+ return "Am79C973".equals( this.id );
+ }
+ return x.getAttribute( "type" ).equals( this.id );
+ }
+
+ }
+
+ public void registerVirtualHW()
+ {
+ List<VirtOptionValue> list;
+ // none type needs to have a valid value; it takes the value of AC97; if value is left null or empty vm will not start because value is not valid
+ // TODO: Maybe just remove the entire section from the XML? Same for ethernet...
+ list = new ArrayList<>();
+ list.add( new VBoxSoundCardModel( "AC97", SoundCard.NONE ) );
+ list.add( new VBoxSoundCardModel( "SB16", SoundCard.SOUND_BLASTER ) );
+ list.add( new VBoxSoundCardModel( "HDA", SoundCard.HD_AUDIO ) );
+ list.add( new VBoxSoundCardModel( "AC97", SoundCard.AC ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.SOUND_CARD_MODEL, list ) );
+
+ list = new ArrayList<>();
+ list.add( new VBoxAccel3D( "true", "3D" ) );
+ list.add( new VBoxAccel3D( "false", "2D" ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.GFX_TYPE, list ) );
+
+ list = new ArrayList<>();
+ list.add( new VBoxNicModel( 0, "", Ethernet.NONE ) );
+ list.add( new VBoxNicModel( 0, "Am79C970A", Ethernet.PCNETPCI2 ) );
+ list.add( new VBoxNicModel( 0, "Am79C973", Ethernet.PCNETFAST3 ) );
+ list.add( new VBoxNicModel( 0, "82540EM", Ethernet.PRO1000MTD ) );
+ list.add( new VBoxNicModel( 0, "82543GC", Ethernet.PRO1000TS ) );
+ list.add( new VBoxNicModel( 0, "82545EM", Ethernet.PRO1000MTS ) );
+ list.add( new VBoxNicModel( 0, "virtio", Ethernet.PARAVIRT ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.NIC_MODEL, list ) );
+
+ list = new ArrayList<>();
+ list.add( new VBoxUsbSpeed( null, Usb.NONE ) );
+ list.add( new VBoxUsbSpeed( "OHCI", Usb.USB1_1 ) );
+ list.add( new VBoxUsbSpeed( "EHCI", Usb.USB2_0 ) );
+ list.add( new VBoxUsbSpeed( "XHCI", Usb.USB3_0 ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.USB_SPEED, list ) );
+ }
+
+ @Override
+ public boolean addEthernet( VirtualizationConfiguration.EtherType type )
+ {
+ Node hostOnlyInterfaceNode = config.addNewNode( "/VirtualBox/Machine/Hardware/Network/Adapter[@slot='0']",
+ "HostOnlyInterface" );
+ if ( hostOnlyInterfaceNode == null ) {
+ LOGGER.error( "Failed to create node for HostOnlyInterface." );
+ return false;
+ }
+ return config.addAttributeToNode( hostOnlyInterfaceNode, "name", EthernetType.valueOf( type.name() ).vnet );
+ }
+
+ @Override
+ public void transformNonPersistent() throws VirtualizationConfigurationException
+ {
+ // Cannot disable suspend
+ // https://forums.virtualbox.org/viewtopic.php?f=6&t=77169
+ // https://forums.virtualbox.org/viewtopic.php?f=8&t=80338
+ // But some other stuff that won't make sense in non-persistent mode
+ config.setExtraData( "GUI/LastCloseAction", "PowerOff" );
+ // Could use "Default" instead of "Last" above, but you won't get any confirmation dialog in that case
+ config.setExtraData( "GUI/RestrictedRuntimeHelpMenuActions", "All" );
+ config.setExtraData( "GUI/RestrictedRuntimeMachineMenuActions", "TakeSnapshot,Pause,SaveState" );
+ config.setExtraData( "GUI/RestrictedRuntimeMenus", "Help" );
+ config.setExtraData( "GUI/PreventSnapshotOperations", "true" );
+ config.setExtraData( "GUI/PreventApplicationUpdate", "true" );
+ config.setExtraData( "GUI/RestrictedCloseActions", "SaveState,PowerOffRestoringSnapshot,Detach" );
+
+ this.removeEnhancedNetworkAdapters();
+ }
+
+ class VBoxUsbSpeed extends VirtOptionValue
+ {
+
+ public VBoxUsbSpeed( String id, String displayName )
+ {
+ super( id, displayName );
+ }
+
+ @Override
+ public void apply()
+ {
+ // Wipe existing ones
+ config.removeNodes( "/VirtualBox/Machine/Hardware", "USB" );
+ if ( Util.isEmptyString( this.id ) ) {
+ // Add marker so we know it's not an old config and we really want no USB
+ Element node = config.createNodeRecursive( "/VirtualBox/OpenSLX/USB" );
+ if ( node != null ) {
+ node.setAttribute( "disabled", "true" );
+ }
+ return; // NO USB
+ }
+ Element node = config.createNodeRecursive( "/VirtualBox/Machine/Hardware/USB/Controllers/Controller" );
+ node.setAttribute( "type", this.id );
+ node.setAttribute( "name", this.id );
+ if ( this.id.equals( "EHCI" ) ) { // XXX "mechanically" ported, could make a special class for this special case
+ // If EHCI (2.0) is selected, VBox adds an OHCI controller too...
+ node = config.addNewNode( "/VirtualBox/Machine/Hardware/USB/Controllers", "Controller" );
+ node.setAttribute( "type", "OHCI" );
+ node.setAttribute( "name", "OHCI" );
+ }
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ NodeList nodes = config.findNodes( "/VirtualBox/Machine/Hardware/USB/Controllers/Controller/@type" );
+ LOGGER.info( "Found USB attribute nodes:" + nodes.getLength() );
+ /* Again, we need an ugly special case here for USB 2.0, which creates two entries in the
+ * XML, one EHCI and one OHCI. If we simply look for "our" type and return true, both
+ * OHCI and EHCI would return true, and what ends up getting selected in the UI is more or
+ * less undefined. So we need to put more brains in here and special-case the whole EHCI/OHCI
+ * detection, and not return true if this.id is OHCI, and we have a controller node with type
+ * OHCI, but also another node with type EHCI...
+ */
+ boolean ohci = false, ehci = false;
+ for ( int i = 0; i < nodes.getLength(); ++i ) {
+ if ( nodes.item( i ).getNodeType() != Node.ATTRIBUTE_NODE ) {
+ LOGGER.info( "Not ATTRIBUTE type (" + nodes.item( i ).getClass().getSimpleName() + ")" );
+ continue;
+ }
+ String type = ( (Attr)nodes.item( i ) ).getValue();
+ LOGGER.info( "Found USB node with type " + type );
+ if ( type.equals( "EHCI" ) ) {
+ ehci = true;
+ } else if ( type.equals( "OHCI" ) ) {
+ ohci = true;
+ } else if ( type.equals( this.id ) )
+ return true;
+ }
+ if ( ehci && "EHCI".equals( this.id ) )
+ return true;
+ if ( ohci && !ehci && "OHCI".equals( this.id ) )
+ return true;
+ if ( config.findNodes( "/VirtualBox/OpenSLX/USB/@disabled" ).getLength() > 0 ) {
+ return Util.isEmptyString( this.id );
+ }
+ return false;
+ }
+
+ }
+
+ @Override
+ public String getFileNameExtension()
+ {
+ return VirtualizationConfigurationVirtualBox.FILE_NAME_EXTENSION;
+ }
+
+ @Override
+ public void validate() throws VirtualizationConfigurationException
+ {
+ this.config.validate();
+ }
+
+ @Override
+ public void disableUsb()
+ {
+ new VBoxUsbSpeed( null, Usb.NONE ).apply();
+ }
+}