diff options
Diffstat (limited to 'src/main/java/org/openslx/util/vm')
-rw-r--r-- | src/main/java/org/openslx/util/vm/DiskImage.java | 86 | ||||
-rw-r--r-- | src/main/java/org/openslx/util/vm/QemuConfig.java | 7 | ||||
-rw-r--r-- | src/main/java/org/openslx/util/vm/QemuMetaData.java | 93 | ||||
-rw-r--r-- | src/main/java/org/openslx/util/vm/VboxConfig.java | 184 | ||||
-rw-r--r-- | src/main/java/org/openslx/util/vm/VboxMetaData.java | 91 | ||||
-rw-r--r-- | src/main/java/org/openslx/util/vm/VmMetaData.java | 626 | ||||
-rw-r--r-- | src/main/java/org/openslx/util/vm/VmwareMetaData.java | 1100 |
7 files changed, 1272 insertions, 915 deletions
diff --git a/src/main/java/org/openslx/util/vm/DiskImage.java b/src/main/java/org/openslx/util/vm/DiskImage.java index ca016f2..30fea99 100644 --- a/src/main/java/org/openslx/util/vm/DiskImage.java +++ b/src/main/java/org/openslx/util/vm/DiskImage.java @@ -71,9 +71,14 @@ public class DiskImage LOGGER.debug( "Validating disk file: " + disk.getAbsolutePath() ); try ( RandomAccessFile file = new RandomAccessFile( disk, "r" ) ) { // vmdk - if ( file.readInt() == VMDK_MAGIC ) { - file.seek( 512 ); - byte[] buffer = new byte[ 2048 ]; + boolean isVmdkMagic = ( file.readInt() == VMDK_MAGIC ); + if ( isVmdkMagic || file.length() < 4096 ) { + if ( isVmdkMagic ) { + file.seek( 512 ); + } else { + file.seek( 0 ); + } + byte[] buffer = new byte[ (int)Math.min( 2048, file.length() ) ]; file.readFully( buffer ); VmwareConfig config; try { @@ -82,20 +87,23 @@ public class DiskImage config = null; } if ( config != null ) { - subFormat = config.get( "createType" ); + String sf = config.get( "createType" ); String parent = config.get( "parentCID" ); - this.isStandalone = isStandaloneCreateType( subFormat, parent ); - this.isCompressed = subFormat != null && subFormat.equalsIgnoreCase( "streamOptimized" ); - this.isSnapshot = parent != null && !parent.equalsIgnoreCase( "ffffffff" ); - this.format = ImageFormat.VMDK; - String hwv = config.get( "ddb.virtualHWVersion" ); - if ( hwv == null ) { - this.hwVersion = 10; - } else { - this.hwVersion = Util.parseInt( hwv, 10 ); + if ( sf != null || parent != null ) { + subFormat = sf; + this.isStandalone = isStandaloneCreateType( subFormat, parent ); + this.isCompressed = subFormat != null && subFormat.equalsIgnoreCase( "streamOptimized" ); + this.isSnapshot = parent != null && !parent.equalsIgnoreCase( "ffffffff" ); + this.format = ImageFormat.VMDK; + String hwv = config.get( "ddb.virtualHWVersion" ); + if ( hwv == null ) { + this.hwVersion = 10; + } else { + this.hwVersion = Util.parseInt( hwv, 10 ); + } + this.diskDescription = null; + return; } - this.diskDescription = null; - return; } } // Format spec from: https://forums.virtualbox.org/viewtopic.php?t=8046 @@ -152,30 +160,30 @@ public class DiskImage return; } - file.seek(0); - //TODO: Standalone & Snapshot - if (file.readInt() == QEMU_MAGIC) { - //skip the next 14 ints as they don't interest us - // - QFI version (4 bytes) - // - backing file offset (8 bytes) - // - backing (8 bytes) - // - crypt method (4 bytes) - // - l1 size (4 bytes) - // - l1 table offset (8 bytes) - // - refcount table offset (8 bytes) - // - refcount cluster (4 bytes) - file.skipBytes( 14 * 4 ); - this.isSnapshot = file.readInt() > 0; - //skip the next 14 ints as they don't interest us - file.seek(374); - this.isCompressed = file.read() == 1; - this.diskDescription = null; - this.format = ImageFormat.QCOW2; - this.isStandalone = true; - this.subFormat = null; - this.hwVersion = 0; - return; - } + file.seek(0); + //TODO: Standalone & Snapshot + if (file.readInt() == QEMU_MAGIC) { + //skip the next 14 ints as they don't interest us + // - QFI version (4 bytes) + // - backing file offset (8 bytes) + // - backing (8 bytes) + // - crypt method (4 bytes) + // - l1 size (4 bytes) + // - l1 table offset (8 bytes) + // - refcount table offset (8 bytes) + // - refcount cluster (4 bytes) + file.skipBytes( 14 * 4 ); + this.isSnapshot = file.readInt() > 0; + //skip the next 14 ints as they don't interest us + file.seek(374); + this.isCompressed = file.read() == 1; + this.diskDescription = null; + this.format = ImageFormat.QCOW2; + this.isStandalone = true; + this.subFormat = null; + this.hwVersion = 0; + return; + } } throw new UnknownImageFormatException(); } diff --git a/src/main/java/org/openslx/util/vm/QemuConfig.java b/src/main/java/org/openslx/util/vm/QemuConfig.java index ceb3285..89df62c 100644 --- a/src/main/java/org/openslx/util/vm/QemuConfig.java +++ b/src/main/java/org/openslx/util/vm/QemuConfig.java @@ -297,8 +297,8 @@ public final class QemuConfig { case "scsi": bus = DriveBusType.SCSI; break; - case "virtio": - bus = DriveBusType.VIRTIO; +// case "virtio": + // bus = DriveBusType.VIRTIO; case "sata": //not available for Qemu. Others : sd, mtd, floppy, pflash break; @@ -340,9 +340,6 @@ public final class QemuConfig { case SCSI: controllerDevice = "scsi-generic"; break; - case VIRTIO: - controllerDevice = "virtio-9p-device"; - break; default: controllerDevice = "scsi-generic"; break; diff --git a/src/main/java/org/openslx/util/vm/QemuMetaData.java b/src/main/java/org/openslx/util/vm/QemuMetaData.java index 8855f9c..814bfa4 100644 --- a/src/main/java/org/openslx/util/vm/QemuMetaData.java +++ b/src/main/java/org/openslx/util/vm/QemuMetaData.java @@ -51,8 +51,19 @@ class QemuSoundCardMeta { value = val; } } +class QemuUSBMeta { -public final class QemuMetaData extends VmMetaData<QemuSoundCardMeta, QemuDDAccelMeta, QemuHWVersionMeta, QemuEthernetDevTypeMeta> { + public final String value; + public final int speed; + + public QemuUSBMeta(String value, int speed) { + + this.value = value; + this.speed = speed; + } +} + +public final class QemuMetaData extends VmMetaData<QemuSoundCardMeta, QemuDDAccelMeta, QemuHWVersionMeta, QemuEthernetDevTypeMeta, QemuUSBMeta> { private static final Logger LOGGER = Logger.getLogger(QemuMetaData.class); @@ -288,6 +299,7 @@ public final class QemuMetaData extends VmMetaData<QemuSoundCardMeta, QemuDDAcce @Override public void setEthernetDevType(int cardIndex, VmMetaData.EthernetDevType type) { + /* //TODO QemuEthernetDevTypeMeta dev = networkCards.get(type); header = new LinkedHashMap<>(); @@ -297,6 +309,7 @@ public final class QemuMetaData extends VmMetaData<QemuSoundCardMeta, QemuDDAcce //option.put("netdev", "net" + cardIndex); config.set(header, option); netdevCounter++; + */ } @Override @@ -363,25 +376,45 @@ public final class QemuMetaData extends VmMetaData<QemuSoundCardMeta, QemuDDAcce return virtualizer; } - @Override - public void enableUsb(boolean enabled) { - option = new TreeMap<>(); - if (enabled) { - option.put("usb", "on"); - } else { - option.put("usb", "off"); - } - - header = new LinkedHashMap<>(); - header.put(MACHINE.getHeader(), MACHINE.getID()); - config.set(header, option); - } - - @Override - public boolean disableSuspend() { - return false; - } - +// @Override +// public void enableUsb(boolean enabled) { +// option = new TreeMap<>(); +// if (enabled) { +// option.put("usb", "on"); +// } else { +// option.put("usb", "off"); +// } +// +// header = new LinkedHashMap<>(); +// header.put(MACHINE.getHeader(), MACHINE.getID()); +// config.set(header, option); +// } + +// @Override +// public boolean disableSuspend() { +// return false; +// } + @Override + public boolean tweakForNonPersistent() { + return false; + } + public void setMaxUsbSpeed( VmMetaData.UsbSpeed speed ) + { + // TODO: Actual speed setting? + if ( speed == null || speed == VmMetaData.UsbSpeed.NONE ) { + option.remove( "usb" ); + } else { + option.put( "usb", "" ); + } + } + + @Override + public VmMetaData.UsbSpeed getMaxUsbSpeed() + { + if (option.containsKey( "usb" ) ) + return VmMetaData.UsbSpeed.USB2_0; // TODO + return VmMetaData.UsbSpeed.NONE; + } @Override public void registerVirtualHW() { soundCards.put(VmMetaData.SoundCardType.NONE, new QemuSoundCardMeta(false, null)); @@ -390,16 +423,20 @@ public final class QemuMetaData extends VmMetaData<QemuSoundCardMeta, QemuDDAcce soundCards.put(VmMetaData.SoundCardType.ES, new QemuSoundCardMeta(true, "es1370")); soundCards.put(VmMetaData.SoundCardType.SOUND_BLASTER, new QemuSoundCardMeta(true, "sb16")); - ddacc.put(DDAcceleration.OFF, new QemuDDAccelMeta(false)); - ddacc.put(DDAcceleration.ON, new QemuDDAccelMeta(true)); + ddacc.put(VmMetaData.DDAcceleration.OFF, new QemuDDAccelMeta(false)); + ddacc.put(VmMetaData.DDAcceleration.ON, new QemuDDAccelMeta(true)); - hwversion.put(HWVersion.DEFAULT, new QemuHWVersionMeta(0)); + hwversion.put(VmMetaData.HWVersion.DEFAULT, new QemuHWVersionMeta(0)); - networkCards.put(EthernetDevType.VIRTIO, new QemuEthernetDevTypeMeta("virtio-net-pci")); - networkCards.put(EthernetDevType.E1000, new QemuEthernetDevTypeMeta("e1000")); - networkCards.put(EthernetDevType.PCNET32, new QemuEthernetDevTypeMeta("pcnet")); - networkCards.put(EthernetDevType.RTL8139, new QemuEthernetDevTypeMeta("rtl8139")); + usbSpeeds.put(VmMetaData.UsbSpeed.NONE, new QemuUSBMeta(null, 0)); + usbSpeeds.put(VmMetaData.UsbSpeed.USB1_1, new QemuUSBMeta("usb", 1)); + usbSpeeds.put(VmMetaData.UsbSpeed.USB2_0, new QemuUSBMeta("ehci", 2)); + usbSpeeds.put(VmMetaData.UsbSpeed.USB3_0, new QemuUSBMeta("usb_xhci", 3)); - } + //networkCards.put(VmMetaData.EthernetDevType.VIRTIO, new QemuEthernetDevTypeMeta("virtio-net-pci")); + networkCards.put(VmMetaData.EthernetDevType.E1000, new QemuEthernetDevTypeMeta("e1000")); + networkCards.put(VmMetaData.EthernetDevType.PCNET32, new QemuEthernetDevTypeMeta("pcnet")); + //networkCards.put(VmMetaData.EthernetDevType.RTL8139, new QemuEthernetDevTypeMeta("rtl8139")); + } } diff --git a/src/main/java/org/openslx/util/vm/VboxConfig.java b/src/main/java/org/openslx/util/vm/VboxConfig.java index 9d6e6f1..6153abf 100644 --- a/src/main/java/org/openslx/util/vm/VboxConfig.java +++ b/src/main/java/org/openslx/util/vm/VboxConfig.java @@ -6,6 +6,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.List; import javax.xml.XMLConstants; import javax.xml.transform.stream.StreamSource; @@ -47,7 +48,6 @@ public class VboxConfig "/VirtualBox/Machine/Hardware/GuestProperties", "/VirtualBox/Machine/Hardware/VideoCapture", "/VirtualBox/Machine/Hardware/HID", - "/VirtualBox/Machine/Hardware/USB", "/VirtualBox/Machine/Hardware/LPT", "/VirtualBox/Machine/Hardware/SharedFolders", "/VirtualBox/Machine/Hardware/Network/Adapter[@enabled='true']/*", @@ -143,6 +143,7 @@ public class VboxConfig try { ensureHardwareUuid(); setOsType(); + fixUsb(); // Since we now support selecting specific speed if ( checkForPlaceholders() ) { return; } @@ -154,6 +155,33 @@ public class VboxConfig return; } } + + private void fixUsb() + { + NodeList list = findNodes( "/VirtualBox/Machine/Hardware/USB/Controllers/Controller" ); + if ( list != null && list.getLength() != 0 ) { + LOGGER.info( "USB present, not fixing anything" ); + return; + } + // If there's no USB section, this can mean two things: + // 1) Old config that would always default to USB 2.0 for "USB enabled" or nothing for disabled + // 2) New config with USB disabled + list = findNodes( "/VirtualBox/OpenSLX/USB[@disabled]" ); + if ( list != null && list.getLength() != 0 ) { + LOGGER.info( "USB explicitly disabled" ); + return; // Explicitly marked as disabled, do nothing + } + // We assume case 1) and add USB 2.0 + LOGGER.info( "Fixing USB: Adding USB 2.0" ); + Element controller; + Element node = createNodeRecursive( "/VirtualBox/Machine/Hardware/USB/Controllers" ); + controller = addNewNode( node, "Controller" ); + controller.setAttribute( "name", "OHCI" ); + controller.setAttribute( "type", "OHCI" ); + controller = addNewNode( node, "Controller" ); + controller.setAttribute( "name", "EHCI" ); + controller.setAttribute( "type", "EHCI" ); + } /** * Saves the machine's uuid as hardware uuid to prevent VMs from @@ -308,24 +336,42 @@ public class VboxConfig } /** - * Search disk drives within the DOM using this class + * Search for attached hard drives and determine their controller and their path. * * @throws XPathExpressionException */ public void setHdds() throws XPathExpressionException { - XPathExpression hddsExpr = XmlHelper.XPath.compile( "/VirtualBox/Machine/MediaRegistry/HardDisks/*" ); + XPathExpression hddsExpr = XmlHelper.XPath.compile( "/VirtualBox/Machine/StorageControllers/StorageController/AttachedDevice[@type='HardDisk']/Image" ); NodeList nodes = (NodeList)hddsExpr.evaluate( this.doc, XPathConstants.NODESET ); + if ( nodes == null ) { + LOGGER.error( "Failed to find attached hard drives." ); + return; + } for ( int i = 0; i < nodes.getLength(); i++ ) { Element hddElement = (Element)nodes.item( i ); if ( hddElement == null ) continue; - String fileName = hddElement.getAttribute( "location" ); - String type = hddElement.getAttribute( "type" ); + String uuid = hddElement.getAttribute( "uuid" ); + if ( uuid.isEmpty() ) + continue; + // got uuid, check if it was registered + XPathExpression hddsRegistered = XmlHelper.XPath.compile( "/VirtualBox/Machine/MediaRegistry/HardDisks/HardDisk[@uuid='" + uuid + "']" ); + NodeList hddsRegisteredNodes = (NodeList)hddsRegistered.evaluate( this.doc, XPathConstants.NODESET ); + if ( hddsRegisteredNodes == null || hddsRegisteredNodes.getLength() != 1 ) { + LOGGER.error( "Found hard disk with uuid '" + uuid + "' which does not appear (unique) in the Media Registry. Skipping." ); + continue; + } + Element hddElementReg = (Element)hddsRegisteredNodes.item( 0 ); + if ( hddElementReg == null ) + continue; + String fileName = hddElementReg.getAttribute( "location" ); + String type = hddElementReg.getAttribute( "type" ); if ( !type.equals( "Normal" ) && !type.equals( "Writethrough" ) ) { LOGGER.warn( "Type of the disk file is neither 'Normal' nor 'Writethrough' but: " + type ); LOGGER.warn( "This makes the image not directly modificable, which might lead to problems when editing it locally." ); } + // search if it is also attached to a controller Node hddDevice = hddElement.getParentNode(); if ( hddDevice == null ) { LOGGER.error( "HDD node had a null parent, shouldn't happen" ); @@ -336,18 +382,19 @@ public class VboxConfig LOGGER.error( "HDD node had a null parent, shouldn't happen" ); continue; } - String controllerDevice = hddController.getAttribute( "type" ); - String bus = hddController.getAttribute( "name" ); + String controllerMode = hddController.getAttribute( "type" ); + String controllerType = hddController.getAttribute( "name" ); DriveBusType busType = null; - if ( bus.equals( "IDE" ) ) { + if ( controllerType.equals( "IDE" ) ) { busType = DriveBusType.IDE; - } else if ( bus.equals( "SCSI" ) ) { + } else if ( controllerType.equals( "SCSI" ) ) { busType = DriveBusType.SCSI; - } else if ( bus.equals( "SATA" ) ) { + } else if ( controllerType.equals( "SATA" ) ) { busType = DriveBusType.SATA; - } - // add them together - hddsArray.add( new HardDisk( controllerDevice, busType, fileName ) ); + } else + continue; + LOGGER.info( "Adding hard disk with controller: " + busType + " (" + controllerMode + ") from file '" + fileName + "'." ); + hddsArray.add( new HardDisk( controllerMode, busType, fileName ) ); } } @@ -362,35 +409,6 @@ public class VboxConfig } /** - * Enable USB by adding the element /VirtualBox/Machine/Hardware/USB - * and adding controllers for OHCI and EHCI (thus enabling USB 2.0). - */ - public void enableUsb() - { - addNewNode( "/VirtualBox/Machine/Hardware", "USB" ); - addNewNode( "/VirtualBox/Machine/Hardware/USB", "Controllers" ); - // OHCI for USB 1.0 - Node ohci = addNewNode( "/VirtualBox/Machine/Hardware/USB/Controllers", "Controller" ); - addAttributeToNode( ohci, "name", "OHCI" ); - addAttributeToNode( ohci, "type", "OHCI" ); - // EHCI for USB 2.0 - Node ehci = addNewNode( "/VirtualBox/Machine/Hardware/USB/Controllers", "Controller" ); - addAttributeToNode( ehci, "name", "EHCI" ); - addAttributeToNode( ehci, "type", "EHCI" ); - } - - /** - * Removes all USB elements - */ - public void disableUsb() - { - NodeList usbList = findNodes( "/VirtualBox/Machine/Hardware/USB" ); - for ( int i = 0; i < usbList.getLength(); i++ ) { - removeNode( usbList.item( 0 ) ); - } - } - - /** * Detect if the vbox file has any machine snapshot by looking at * the existance of '/VirtualBox/Machine/Snapshot' elements. * @@ -485,6 +503,58 @@ public class VboxConfig return addNewNode( possibleParents.item( 0 ), childName ); } + public Element createNodeRecursive( String xPath ) + { + String[] nodeNames = xPath.split( "/" ); + Node parent = this.doc; + Element latest = null; + for ( int nodeIndex = 0; nodeIndex < nodeNames.length; ++nodeIndex ) { + if ( nodeNames[nodeIndex].length() == 0 ) + continue; + Node node = skipNonElementNodes( parent.getFirstChild() ); + while ( node != null ) { + if ( node.getNodeType() == Node.ELEMENT_NODE && nodeNames[nodeIndex].equals( node.getNodeName() ) ) + break; // Found existing + // Check next on same level + node = skipNonElementNodes( node.getNextSibling() ); + } + if ( node == null ) { + node = doc.createElement( nodeNames[nodeIndex] ); + parent.appendChild( node ); + } + parent = node; + latest = (Element)node; + } + return latest; + } + + private Element skipNonElementNodes( Node nn ) + { + while ( nn != null && nn.getNodeType() != Node.ELEMENT_NODE ) { + nn = nn.getNextSibling(); + } + return (Element)nn; + } + + public void setExtraData( String key, String value ) + { + NodeList nl = findNodes( "/VirtualBox/Machine/ExtraData/ExtraDataItem[@name='" + key + "']" ); + Element e = null; + for ( int i = 0; i < nl.getLength(); ++i ) { + Node n = nl.item( i ); + if ( n.getNodeType() == Node.ELEMENT_NODE ) { + e = (Element)n; + break; + } + } + if ( e == null ) { + Element p = createNodeRecursive( "/VirtualBox/Machine/ExtraData" ); + e = addNewNode( p, "ExtraDataItem" ); + e.setAttribute( "name", key ); + } + e.setAttribute( "value", value ); + } + /** * Creates a new element to the given parent node. * @@ -492,12 +562,12 @@ public class VboxConfig * @param childName name of the new element to create * @return the newly created node */ - public Node addNewNode( Node parent, String childName ) + public Element addNewNode( Node parent, String childName ) { if ( parent == null || parent.getNodeType() != Node.ELEMENT_NODE ) { return null; } - Node newNode = null; + Element newNode = null; try { newNode = doc.createElement( childName ); parent.appendChild( newNode ); @@ -531,4 +601,30 @@ public class VboxConfig { return XmlHelper.getXmlFromDocument( doc, prettyPrint ); } + + /** + * Remove all nodes with name childName from parentPath + * @param parentPath XPath to parent node of where child nodes are to be deleted + * @param childName Name of nodes to delete + */ + public void removeNodes( String parentPath, String childName ) + { + NodeList parentNodes = findNodes( parentPath ); + // XPath might match multiple nodes + for ( int i = 0; i < parentNodes.getLength(); ++i ) { + Node parent = parentNodes.item( i ); + List<Node> delList = new ArrayList<>( 0 ); + // Iterate over child nodes + for ( Node child = parent.getFirstChild(); child != null; child = child.getNextSibling() ) { + if ( childName.equals( child.getNodeName() ) ) { + // Remember all to be deleted (don't delete while iterating) + delList.add( child ); + } + } + // Now delete them all + for ( Node child : delList ) { + parent.removeChild( child ); + } + } + } } diff --git a/src/main/java/org/openslx/util/vm/VboxMetaData.java b/src/main/java/org/openslx/util/vm/VboxMetaData.java index 00ca8ad..da5189e 100644 --- a/src/main/java/org/openslx/util/vm/VboxMetaData.java +++ b/src/main/java/org/openslx/util/vm/VboxMetaData.java @@ -7,6 +7,7 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; +import java.util.Map.Entry; import java.util.UUID; import org.apache.log4j.Logger; @@ -14,6 +15,7 @@ import org.openslx.bwlp.thrift.iface.OperatingSystem; import org.openslx.bwlp.thrift.iface.Virtualizer; import org.openslx.thrifthelper.TConst; import org.openslx.util.vm.VboxConfig.PlaceHolder; +import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -62,7 +64,18 @@ class VBoxEthernetDevTypeMeta } } -public class VboxMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta, VBoxHWVersionMeta, VBoxEthernetDevTypeMeta> +class VBoxUsbSpeedMeta +{ + public final String value; + public final int speed; + public VBoxUsbSpeedMeta( String value, int speed ) + { + this.value = value; + this.speed = speed; + } +} + +public class VboxMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta, VBoxHWVersionMeta, VBoxEthernetDevTypeMeta, VBoxUsbSpeedMeta> { private static final Logger LOGGER = Logger.getLogger( VboxMetaData.class ); @@ -114,16 +127,6 @@ public class VboxMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta, } @Override - public void enableUsb( boolean enabled ) - { - if ( !enabled ) { - config.disableUsb(); - } else { - config.enableUsb(); - } - } - - @Override public void applySettingsForLocalEdit() { // TODO Auto-generated method stub @@ -415,6 +418,7 @@ public class VboxMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta, public void registerVirtualHW() { // 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... soundCards.put( VmMetaData.SoundCardType.NONE, new VBoxSoundCardMeta( false, "AC97" ) ); soundCards.put( VmMetaData.SoundCardType.SOUND_BLASTER, new VBoxSoundCardMeta( true, "SB16" ) ); soundCards.put( VmMetaData.SoundCardType.HD_AUDIO, new VBoxSoundCardMeta( true, "HDA" ) ); @@ -433,6 +437,11 @@ public class VboxMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta, networkCards.put( VmMetaData.EthernetDevType.PRO1000TS, new VBoxEthernetDevTypeMeta( true, "82543GC" ) ); networkCards.put( VmMetaData.EthernetDevType.PRO1000MTS, new VBoxEthernetDevTypeMeta( true, "82545EM" ) ); networkCards.put( VmMetaData.EthernetDevType.PARAVIRT, new VBoxEthernetDevTypeMeta( true, "virtio" ) ); + + usbSpeeds.put( VmMetaData.UsbSpeed.NONE, new VBoxUsbSpeedMeta( null, 0 ) ); + usbSpeeds.put( VmMetaData.UsbSpeed.USB1_1, new VBoxUsbSpeedMeta( "OHCI", 1 ) ); + usbSpeeds.put( VmMetaData.UsbSpeed.USB2_0, new VBoxUsbSpeedMeta( "EHCI", 2 ) ); + usbSpeeds.put( VmMetaData.UsbSpeed.USB3_0, new VBoxUsbSpeedMeta( "XHCI", 3 ) ); } @Override @@ -447,12 +456,66 @@ public class VboxMetaData extends VmMetaData<VBoxSoundCardMeta, VBoxDDAccelMeta, } @Override - public boolean disableSuspend() + public boolean tweakForNonPersistent() { - // TODO how?? - // short answer is: you can't + // 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" ); return true; } + + @Override + public void setMaxUsbSpeed( VmMetaData.UsbSpeed speed ) + { + // Wipe existing ones + config.removeNodes( "/VirtualBox/Machine/Hardware", "USB" ); + if ( speed == null || speed == VmMetaData.UsbSpeed.NONE ) { + // 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" ); + VBoxUsbSpeedMeta vboxSpeed = usbSpeeds.get( speed ); + node.setAttribute( "type", vboxSpeed.value ); + node.setAttribute( "name", vboxSpeed.value ); + if ( speed == UsbSpeed.USB2_0 ) { + // If EHCI (2.0) is selected, VBox adds an OHCI controller too... + node.setAttribute( "type", "OHCI" ); + node.setAttribute( "name", "OHCI" ); + } + } + + @Override + public VmMetaData.UsbSpeed getMaxUsbSpeed() + { + NodeList nodes = config.findNodes( "/VirtualBox/Machine/Hardware/USB/Controllers/Controller/@type" ); + int maxSpeed = 0; + VmMetaData.UsbSpeed maxItem = VmMetaData.UsbSpeed.NONE; + for ( int i = 0; i < nodes.getLength(); ++i ) { + if ( nodes.item( i ).getNodeType() != Node.ATTRIBUTE_NODE ) { + LOGGER.info( "Not ATTRIBUTE type" ); + continue; + } + String type = ((Attr)nodes.item( i )).getValue(); + for ( Entry<VmMetaData.UsbSpeed, VBoxUsbSpeedMeta> s : usbSpeeds.entrySet() ) { + if ( s.getValue().speed > maxSpeed && type.equals( s.getValue().value ) ) { + maxSpeed = s.getValue().speed; + maxItem = s.getKey(); + } + } + } + return maxItem; + } } diff --git a/src/main/java/org/openslx/util/vm/VmMetaData.java b/src/main/java/org/openslx/util/vm/VmMetaData.java index c17df89..714a7e4 100644 --- a/src/main/java/org/openslx/util/vm/VmMetaData.java +++ b/src/main/java/org/openslx/util/vm/VmMetaData.java @@ -18,96 +18,133 @@ import org.openslx.bwlp.thrift.iface.Virtualizer; * Describes a configured virtual machine. This class is parsed from a machine * description, like a *.vmx for VMware machines. */ -public abstract class VmMetaData<T, U, V, W> { - +public abstract class VmMetaData<T, U, V, W, X> +{ private static final Logger LOGGER = Logger.getLogger(VmMetaData.class); /* * Helper types - */ - protected Map<SoundCardType, T> soundCards = new HashMap<>(); - protected Map<DDAcceleration, U> ddacc = new HashMap<>(); - protected Map<HWVersion, V> hwversion = new HashMap<>(); - protected Map<EthernetDevType, W> networkCards = new HashMap<>(); - - /** - * Virtual sound cards types - */ - public static enum SoundCardType { - NONE("None"), DEFAULT("(default)"), SOUND_BLASTER("Sound Blaster 16"), ES("ES 1371"), HD_AUDIO("Intel Integrated HD Audio"), AC("Intel ICH Audio Codec 97"); - - public final String displayName; - - private SoundCardType(String dName) { - this.displayName = dName; - } - } - - /** - * 3D acceleration types - */ - public static enum DDAcceleration { - OFF("Off"), ON("On"); - - public final String displayName; - - private DDAcceleration(String dName) { - this.displayName = dName; - } - } - - /** - * Virtual hardware version - currently only in use for VMPlayer - */ - public static enum HWVersion { - NONE("(invalid)"), THREE(" 3 (Workstation 4/5, Player 1)"), FOUR(" 4 (Workstation 4/5, Player 1/2, Fusion 1)"), SIX(" 6 (Workstation 6)"), SEVEN( - " 7 (Workstation 6.5/7, Player 3, Fusion 2/3)"), EIGHT(" 8 (Workstation 8, Player/Fusion 4)"), NINE(" 9 (Workstation 9, Player/Fusion 5)"), TEN( - "10 (Workstation 10, Player/Fusion 6)"), ELEVEN( - "11 (Workstation 11, Player/Fusion 7)"), TWELVE("12 (Workstation/Player 12, Fusion 8)"), DEFAULT("default"); - - public final String displayName; - - private HWVersion(String dName) { - this.displayName = dName; - } - } - - /** - * Virtual network cards - */ - public static enum EthernetDevType { - AUTO("(default)"), PCNET32("AMD PCnet32"), E1000("Intel E1000 (PCI)"), E1000E("Intel E1000e (PCI-Express)"), VMXNET("VMXnet"), VMXNET3("VMXnet 3"), PCNETPCI2( - "PCnet-PCI II"), PCNETFAST3("PCnet-FAST III"), PRO1000MTD("Intel PRO/1000 MT Desktop"), PRO1000TS( - "Intel PRO/1000 T Server"), PRO1000MTS("Intel PRO/1000 MT Server"), PARAVIRT("Paravirtualized Network"), VIRTIO("Virtual High Performance Ethernet card"), RTL8139("Realtek Fast Ethernet "), NONE("No Network Card"); - - public final String displayName; - - private EthernetDevType(String dName) { - this.displayName = dName; - } - } - - public static enum DriveBusType { - SCSI, IDE, SATA, VIRTIO; - } - - public static class HardDisk { - - public final String chipsetDriver; - public final DriveBusType bus; - public final String diskImage; - - public HardDisk(String chipsetDriver, DriveBusType bus, String diskImage) { - this.chipsetDriver = chipsetDriver; - this.bus = bus; - this.diskImage = diskImage; - } - } - - public static enum EtherType { - NAT, BRIDGED, HOST_ONLY; - } - /* + */ + protected Map<SoundCardType, T> soundCards = new HashMap<>(); + protected Map<DDAcceleration, U> ddacc = new HashMap<>(); + protected Map<HWVersion, V> hwversion = new HashMap<>(); + protected Map<EthernetDevType, W> networkCards = new HashMap<>(); + protected Map<UsbSpeed, X> usbSpeeds = new HashMap<>(); + + /** + * Virtual sound cards types + */ + public static enum SoundCardType + { + NONE( "None" ), DEFAULT( "(default)" ), SOUND_BLASTER( "Sound Blaster 16" ), ES( "ES 1371" ), HD_AUDIO( "Intel Integrated HD Audio" ), AC( "Intel ICH Audio Codec 97" ); + + public final String displayName; + + private SoundCardType( String dName ) + { + this.displayName = dName; + } + } + + /** + * 3D acceleration types + */ + public static enum DDAcceleration + { + OFF( "Off" ), ON( "On" ); + + public final String displayName; + + private DDAcceleration( String dName ) + { + this.displayName = dName; + } + } + + /** + * Virtual hardware version - currently only in use for VMPlayer + */ + public static enum HWVersion + { + NONE( "(invalid)" ), + THREE( " 3 (Workstation 4/5, Player 1)" ), + FOUR( " 4 (Workstation 4/5, Player 1/2, Fusion 1)" ), + SIX( " 6 (Workstation 6)" ), + SEVEN( " 7 (Workstation 6.5/7, Player 3, Fusion 2/3)" ), + EIGHT( " 8 (Workstation 8, Player/Fusion 4)" ), + NINE( " 9 (Workstation 9, Player/Fusion 5)" ), + TEN( "10 (Workstation 10, Player/Fusion 6)" ), + ELEVEN( "11 (Workstation 11, Player/Fusion 7)" ), + TWELVE( "12 (Workstation/Player 12, Fusion 8)" ), + FOURTEEN( "14 (Workstation/Player 14, Fusion 10)"), + FIFTEEN( "15 (Workstation/Player 15, Fusion 11)"), + FIFTEEN_ONE( "16 (Workstation/Player 15.1, Fusion 11.1)"), + DEFAULT( "default" ); + + public final String displayName; + + private HWVersion( String dName ) + { + this.displayName = dName; + } + } + + /** + * Virtual network cards + */ + public static enum EthernetDevType + { + AUTO( "(default)" ), PCNET32( "AMD PCnet32" ), E1000( "Intel E1000 (PCI)" ), E1000E( "Intel E1000e (PCI-Express)" ), VMXNET( "VMXnet" ), VMXNET3( "VMXnet 3" ), PCNETPCI2( + "PCnet-PCI II" ), PCNETFAST3( "PCnet-FAST III" ), PRO1000MTD( "Intel PRO/1000 MT Desktop" ), PRO1000TS( + "Intel PRO/1000 T Server" ), PRO1000MTS( "Intel PRO/1000 MT Server" ), PARAVIRT( "Paravirtualized Network" ), NONE( "No Network Card" ); + + public final String displayName; + + private EthernetDevType( String dName ) + { + this.displayName = dName; + } + } + + public static enum UsbSpeed + { + NONE( "None" ), + USB1_1( "USB 1.1" ), + USB2_0( "USB 2.0" ), + USB3_0( "USB 3.0" ); + + public final String displayName; + + private UsbSpeed( String dName ) + { + this.displayName = dName; + } + } + + public static enum DriveBusType + { + SCSI, IDE, SATA; + } + + public static class HardDisk + { + public final String chipsetDriver; + public final DriveBusType bus; + public final String diskImage; + + public HardDisk( String chipsetDriver, DriveBusType bus, String diskImage ) + { + this.chipsetDriver = chipsetDriver; + this.bus = bus; + this.diskImage = diskImage; + } + } + + public static enum EtherType + { + NAT, BRIDGED, HOST_ONLY; + } + /* * Members */ @@ -123,53 +160,67 @@ public abstract class VmMetaData<T, U, V, W> { /* * Getters for virtual hardware - */ - public List<SoundCardType> getSupportedSoundCards() { - ArrayList<SoundCardType> availables = new ArrayList<SoundCardType>(soundCards.keySet()); - Collections.sort(availables); - return availables; - } - - public List<DDAcceleration> getSupportedDDAccs() { - ArrayList<DDAcceleration> availables = new ArrayList<DDAcceleration>(ddacc.keySet()); - Collections.sort(availables); - return availables; - } - - public List<HWVersion> getSupportedHWVersions() { - ArrayList<HWVersion> availables = new ArrayList<HWVersion>(hwversion.keySet()); - Collections.sort(availables); - return availables; - } - - public List<EthernetDevType> getSupportedEthernetDevices() { - ArrayList<EthernetDevType> availables = new ArrayList<EthernetDevType>(networkCards.keySet()); - Collections.sort(availables); - return availables; - } - - /** - * Get operating system of this VM. - */ - public OperatingSystem getOs() { - return os; - } - - /** - * Get all hard disks of this VM. - */ - public List<HardDisk> getHdds() { - return Collections.unmodifiableList(hdds); - } - - /** - * Get display name of VM. - */ - public String getDisplayName() { - return displayName; - } - - /* + */ + public List<SoundCardType> getSupportedSoundCards() + { + ArrayList<SoundCardType> availables = new ArrayList<SoundCardType>( soundCards.keySet() ); + Collections.sort( availables ); + return availables; + } + + public List<DDAcceleration> getSupportedDDAccs() + { + ArrayList<DDAcceleration> availables = new ArrayList<DDAcceleration>( ddacc.keySet() ); + Collections.sort( availables ); + return availables; + } + + public List<HWVersion> getSupportedHWVersions() + { + ArrayList<HWVersion> availables = new ArrayList<HWVersion>( hwversion.keySet() ); + Collections.sort( availables ); + return availables; + } + + public List<EthernetDevType> getSupportedEthernetDevices() + { + ArrayList<EthernetDevType> availables = new ArrayList<EthernetDevType>( networkCards.keySet() ); + Collections.sort( availables ); + return availables; + } + + public List<UsbSpeed> getSupportedUsbSpeeds() + { + ArrayList<UsbSpeed> availables = new ArrayList<>( usbSpeeds.keySet() ); + Collections.sort( availables ); + return availables; + } + + /** + * Get operating system of this VM. + */ + public OperatingSystem getOs() + { + return os; + } + + /** + * Get all hard disks of this VM. + */ + public List<HardDisk> getHdds() + { + return Collections.unmodifiableList( hdds ); + } + + /** + * Get display name of VM. + */ + public String getDisplayName() + { + return displayName; + } + + /* * Getter for isMachineSnapshot */ public boolean isMachineSnapshot() { @@ -191,156 +242,161 @@ public abstract class VmMetaData<T, U, V, W> { /* * Methods - */ - public VmMetaData(List<OperatingSystem> osList) { - this.osList = osList; - } - - /** - * Called from subclass to set the OS. If the OS cannot be determined from - * the given parameters, it will not be set. - * - * @param virtId virtualizer, eg "vmware" for VMware - * @param virtOsId the os identifier used by the virtualizer, eg. - * windows7-64 for 64bit Windows 7 on VMware - */ - protected final void setOs(String virtId, String virtOsId) { - OperatingSystem lazyMatch = null; - for (OperatingSystem os : osList) { - if (os.getVirtualizerOsId() == null) { - continue; - } - for (Entry<String, String> entry : os.getVirtualizerOsId().entrySet()) { - if (!entry.getValue().equals(virtOsId)) { - continue; - } - if (entry.getKey().equals(virtId)) { - this.os = os; - return; - } else { - lazyMatch = os; - } - } - } - this.os = lazyMatch; - } - - /** - * Apply config options that are desired when locally editing a VM. for - * vmware, this disables automatic DPI scaling of the guest. - */ - public abstract void applySettingsForLocalEdit(); - - /** - * Returns a VmMetaData instance of the given machine description given as - * file - * - * @param osList List of supported operating systems - * @param file VM's machine description file to get the metadata instance - * from - * @return VmMetaData object representing the relevant parts of the given - * machine description - */ - public static VmMetaData<?, ?, ?, ?> getInstance(List<OperatingSystem> osList, File file) - throws IOException { - try { - return new VmwareMetaData(osList, file); - } catch (UnsupportedVirtualizerFormatException e) { - LOGGER.info("Not a VMware file", e); - } - try { - return new VboxMetaData(osList, file); - } catch (UnsupportedVirtualizerFormatException e) { - LOGGER.info("Not a VirtualBox file", e); - } - try { - return new QemuMetaData(osList, file); - } catch (Exception e) { - LOGGER.info("Not a QEmu file", e); - } - LOGGER.error("Could not detect any known virtualizer format"); - return null; - } - - /** - * Returns a VmMetaData instance of the given machine description given as a - * byte array - * - * @param osList List of supported operating systems - * @param vmContent VM's machine description as byte array (e.g. stored in - * DB) - * @param length length of the byte array given as vmContent - * @return VmMetaData object representing the relevant parts of the given - * machine description - * @throws IOException - */ - public static VmMetaData<?, ?, ?, ?> getInstance(List<OperatingSystem> osList, byte[] vmContent, int length) throws IOException { - try { - return new VmwareMetaData(osList, vmContent, length); - } catch (UnsupportedVirtualizerFormatException e) { - LOGGER.info("Not a VMware file", e); - } - try { - return new VboxMetaData(osList, vmContent, length); - } catch (UnsupportedVirtualizerFormatException e) { - LOGGER.info("Not a VirtualBox file", e); - } - try { - return new QemuMetaData(osList, vmContent, length); - } catch (UnsupportedVirtualizerFormatException e) { - LOGGER.info("Not a VirtualBox file", e); - } - // TODO QEmu -- hack above expects qcow2 file, so we can't do anything here yet - LOGGER.error("Could not detect any known virtualizer format"); - return null; - } - - public abstract boolean addHddTemplate(File diskImage, String hddMode, String redoDir); - - public abstract boolean addHddTemplate(String diskImagePath, String hddMode, String redoDir); - - public abstract boolean addDefaultNat(); - - public abstract void setOs(String vendorOsId); - - public abstract boolean addDisplayName(String name); - - public abstract boolean addRam(int mem); - - public abstract void addFloppy(int index, String image, boolean readOnly); - - public abstract boolean addCdrom(String image); - - public abstract boolean addCpuCoreCount(int nrOfCores); - - public abstract void setSoundCard(SoundCardType type); - - public abstract SoundCardType getSoundCard(); - - public abstract void setDDAcceleration(DDAcceleration type); - - public abstract DDAcceleration getDDAcceleration(); - - public abstract void setHWVersion(HWVersion type); - - public abstract HWVersion getHWVersion(); - - public abstract void setEthernetDevType(int cardIndex, EthernetDevType type); - - public abstract EthernetDevType getEthernetDevType(int cardIndex); - - public abstract byte[] getDefinitionArray(); - - public abstract boolean addEthernet(EtherType type); - - public abstract Virtualizer getVirtualizer(); - - public abstract void enableUsb(boolean enabled); - - public abstract boolean disableSuspend(); - - /** - * Function used to register virtual devices - */ - public abstract void registerVirtualHW(); + */ + + public VmMetaData( List<OperatingSystem> osList ) + { + this.osList = osList; + } + + /** + * Called from subclass to set the OS. If the OS cannot be determined from the + * given parameters, it will not be set. + * + * @param virtId + * virtualizer, eg "vmware" for VMware + * @param virtOsId + * the os identifier used by the virtualizer, eg. windows7-64 for + * 64bit Windows 7 on VMware + */ + protected final void setOs( String virtId, String virtOsId ) + { + OperatingSystem lazyMatch = null; + for ( OperatingSystem os : osList ) { + if ( os.getVirtualizerOsId() == null ) + continue; + for ( Entry<String, String> entry : os.getVirtualizerOsId().entrySet() ) { + if ( !entry.getValue().equals( virtOsId ) ) + continue; + if ( entry.getKey().equals( virtId ) ) { + this.os = os; + return; + } else { + lazyMatch = os; + } + } + } + this.os = lazyMatch; + } + + /** + * Apply config options that are desired when locally editing a VM. for vmware, + * this disables automatic DPI scaling of the guest. + */ + public abstract void applySettingsForLocalEdit(); + + /** + * Returns a VmMetaData instance of the given machine description given as file + * + * @param osList List of supported operating systems + * @param file VM's machine description file to get the metadata instance from + * @return VmMetaData object representing the relevant parts of the given machine description + */ + public static VmMetaData<?, ?, ?, ?, ?> getInstance( List<OperatingSystem> osList, File file ) + throws IOException + { + try { + return new VmwareMetaData( osList, file ); + } catch ( UnsupportedVirtualizerFormatException e ) { + LOGGER.info( "Not a VMware file", e ); + } + try { + return new VboxMetaData( osList, file ); + } catch ( UnsupportedVirtualizerFormatException e ) { + LOGGER.info( "Not a VirtualBox file", e ); + } + try { + return new QemuMetaData( osList, file ); + } catch ( Exception e ) { + LOGGER.info( "Not a QEmu file", e ); + } + LOGGER.error( "Could not detect any known virtualizer format" ); + return null; + } + + /** + * Returns a VmMetaData instance of the given machine description given as a byte array + * + * @param osList List of supported operating systems + * @param vmContent VM's machine description as byte array (e.g. stored in DB) + * @param length length of the byte array given as vmContent + * @return VmMetaData object representing the relevant parts of the given machine description + * @throws IOException + */ + public static VmMetaData<?, ?, ?, ?, ?> getInstance( List<OperatingSystem> osList, byte[] vmContent, int length ) throws IOException + { + Map<String, Exception> exceptions = new HashMap<>(); + try { + return new VmwareMetaData( osList, vmContent, length ); + } catch ( UnsupportedVirtualizerFormatException e ) { + exceptions.put( "Not a VMware file", e ); + } + try { + return new VboxMetaData( osList, vmContent, length ); + } catch ( UnsupportedVirtualizerFormatException e ) { + exceptions.put( "Not a VirtualBox file", e ); + } + try { + return new QemuMetaData( osList, vmContent, length ); + } catch ( UnsupportedVirtualizerFormatException e ) { + exceptions.put( "Not a QEMU file", e ); + } + // TODO QEmu -- hack above expects qcow2 file, so we can't do anything here yet + LOGGER.error( "Could not detect any known virtualizer format" ); + for ( Entry<String, Exception> e : exceptions.entrySet() ) { + LOGGER.error( e.getKey(), e.getValue() ); + } + return null; + } + + public abstract boolean addHddTemplate( File diskImage, String hddMode, String redoDir ); + + public abstract boolean addHddTemplate( String diskImagePath, String hddMode, String redoDir ); + + public abstract boolean addDefaultNat(); + + public abstract void setOs( String vendorOsId ); + + public abstract boolean addDisplayName( String name ); + + public abstract boolean addRam( int mem ); + + public abstract void addFloppy( int index, String image, boolean readOnly ); + + public abstract boolean addCdrom( String image ); + + public abstract boolean addCpuCoreCount( int nrOfCores ); + + public abstract void setSoundCard( SoundCardType type ); + + public abstract SoundCardType getSoundCard(); + + public abstract void setDDAcceleration( DDAcceleration type ); + + public abstract DDAcceleration getDDAcceleration(); + + public abstract void setHWVersion( HWVersion type ); + + public abstract HWVersion getHWVersion(); + + public abstract void setEthernetDevType( int cardIndex, EthernetDevType type ); + + public abstract EthernetDevType getEthernetDevType( int cardIndex ); + + public abstract void setMaxUsbSpeed( UsbSpeed speed ); + + public abstract UsbSpeed getMaxUsbSpeed(); + + public abstract byte[] getDefinitionArray(); + + public abstract boolean addEthernet( EtherType type ); + + public abstract Virtualizer getVirtualizer(); + + public abstract boolean tweakForNonPersistent(); + + /** + * Function used to register virtual devices + */ + public abstract void registerVirtualHW(); } diff --git a/src/main/java/org/openslx/util/vm/VmwareMetaData.java b/src/main/java/org/openslx/util/vm/VmwareMetaData.java index c54ded8..fab90de 100644 --- a/src/main/java/org/openslx/util/vm/VmwareMetaData.java +++ b/src/main/java/org/openslx/util/vm/VmwareMetaData.java @@ -55,505 +55,605 @@ class VmWareEthernetDevTypeMeta { } } -public class VmwareMetaData extends VmMetaData<VmWareSoundCardMeta, VmWareDDAccelMeta, VmWareHWVersionMeta, VmWareEthernetDevTypeMeta> { - - private static final Logger LOGGER = Logger.getLogger(VmwareMetaData.class); - - private static final Virtualizer virtualizer = new Virtualizer(TConst.VIRT_VMWARE, "VMware"); - - private static final Pattern hddKey = Pattern.compile("^(ide\\d|scsi\\d|sata\\d):?(\\d)?\\.(.*)", Pattern.CASE_INSENSITIVE); - - // Lowercase list of allowed settings for upload (as regex) - private static final Pattern[] whitelist; - - private final VmwareConfig config; - - // Init static members - static { - String[] list = {"^guestos", "^uuid\\.bios", "^config\\.version", "^ehci\\.", "^mks\\.enable3d", "^virtualhw\\.", "^sound\\.", "\\.pcislotnumber$", "^pcibridge", - "\\.virtualdev$", "^tools\\.syncTime$", "^time\\.synchronize", "^bios\\.bootDelay", "^rtc\\.", "^xhci\\."}; - whitelist = new Pattern[list.length]; - for (int i = 0; i < list.length; ++i) { - whitelist[i] = Pattern.compile(list[i].toLowerCase()); - } - } - - public static enum EthernetType { - NAT("vmnet1"), BRIDGED("vmnet0"), HOST_ONLY("vmnet2"); - - public final String vmnet; - - private EthernetType(String vnet) { - this.vmnet = vnet; - } - } - - private final Map<String, Controller> disks = new HashMap<>(); - - public VmwareMetaData(List<OperatingSystem> osList, File file) throws IOException, UnsupportedVirtualizerFormatException { - super(osList); - this.config = new VmwareConfig(file); - init(); - } - - public VmwareMetaData(List<OperatingSystem> osList, byte[] vmxContent, int length) throws UnsupportedVirtualizerFormatException { - super(osList); - this.config = new VmwareConfig(vmxContent, length); // still unfiltered - init(); // now filtered - } - - private void init() { - registerVirtualHW(); - - for (Entry<String, ConfigEntry> entry : config.entrySet()) { - handleLoadEntry(entry); - } - // if we find this tag, we already went through the hdd's - so we're done. - if (config.get("#SLX_HDD_BUS") != null) { - return; - } - // Now find the HDDs and add to list - for (Entry<String, Controller> cEntry : disks.entrySet()) { - Controller controller = cEntry.getValue(); - String controllerType = cEntry.getKey(); - if (!controller.present) { - continue; - } - for (Entry<String, Device> dEntry : controller.devices.entrySet()) { - Device device = dEntry.getValue(); - if (!device.present) { - continue; // Not present - } - if (device.deviceType != null && !device.deviceType.toLowerCase().endsWith("disk")) { - continue; // Not a HDD - } - DriveBusType bus = null; - if (controllerType.startsWith("ide")) { - bus = DriveBusType.IDE; - } else if (controllerType.startsWith("scsi")) { - bus = DriveBusType.SCSI; - } else if (controllerType.startsWith("sata")) { - bus = DriveBusType.SATA; - } - hdds.add(new HardDisk(controller.virtualDev, bus, device.filename)); - } - } - // TODO check if this machine is in a paused/suspended state - this.isMachineSnapshot = false; - - // Add HDD to cleaned vmx - if (!hdds.isEmpty()) { - HardDisk hdd = hdds.get(0); - addFiltered("#SLX_HDD_BUS", hdd.bus.toString()); - if (hdd.chipsetDriver != null) { - addFiltered("#SLX_HDD_CHIP", hdd.chipsetDriver); - } - } - } - - private void addFiltered(String key, String value) { - config.set(key, value).filtered(true); - } - - private boolean isSetAndTrue(String key) { - String value = config.get(key); - return value != null && value.equalsIgnoreCase("true"); - } - - private void handleLoadEntry(Entry<String, ConfigEntry> entry) { - String lowerKey = entry.getKey().toLowerCase(); - // Cleaned vmx construction - for (Pattern exp : whitelist) { - if (exp.matcher(lowerKey).find()) { - entry.getValue().filtered(true); - break; - } - } - // - // Dig Usable meta data - String value = entry.getValue().getValue(); - if (lowerKey.equals("guestos")) { - setOs(value); - return; - } - if (lowerKey.equals("displayname")) { - displayName = value; - return; - } - Matcher hdd = hddKey.matcher(entry.getKey()); - if (hdd.find()) { - handleHddEntry(hdd.group(1).toLowerCase(), hdd.group(2), hdd.group(3), value); - } - } - - private void handleHddEntry(String controllerStr, String deviceStr, String property, String value) { - Controller controller = disks.get(controllerStr); - if (controller == null) { - controller = new Controller(); - disks.put(controllerStr, controller); - } - if (deviceStr == null || deviceStr.isEmpty()) { - // Controller property - if (property.equalsIgnoreCase("present")) { - controller.present = Boolean.parseBoolean(value); - } else if (property.equalsIgnoreCase("virtualDev")) { - controller.virtualDev = value; - } - return; - } - // Device property - Device device = controller.devices.get(deviceStr); - if (device == null) { - device = new Device(); - controller.devices.put(deviceStr, device); - } - if (property.equalsIgnoreCase("deviceType")) { - device.deviceType = value; - } else if (property.equalsIgnoreCase("filename")) { - device.filename = value; - } else if (property.equalsIgnoreCase("present")) { - device.present = Boolean.parseBoolean(value); - } - } - - @Override - public boolean addHddTemplate(File diskImage, String hddMode, String redoDir) { - return addHddTemplate(diskImage.getName(), hddMode, redoDir); - } - - @Override - public boolean addHddTemplate(String diskImagePath, String hddMode, String redoDir) { - if (diskImagePath.isEmpty()) { - LOGGER.error("Empty disk image path given!"); - return false; - } - DriveBusType bus; - try { - bus = DriveBusType.valueOf(config.get("#SLX_HDD_BUS")); - } catch (Exception e) { - LOGGER.warn("Unknown bus type: " + config.get("#SLX_HDD_BUS") + ". Cannot add hdd config."); - return false; - } - String chipset = config.get("#SLX_HDD_CHIP"); - String prefix; - switch (bus) { - case IDE: - prefix = "ide0:0"; - addFiltered("ide0.present", "TRUE"); - break; - case SATA: - // Cannot happen?... use lsisas1068 - case SCSI: - prefix = "scsi0:0"; - addFiltered("scsi0.present", "TRUE"); - if (chipset != null) { - addFiltered("scsi0.virtualDev", chipset); - } - break; - default: - LOGGER.warn("Unknown HDD bus type: " + bus.toString()); - return false; - } - // Gen - addFiltered(prefix + ".present", "TRUE"); - addFiltered(prefix + ".deviceType", "disk"); - addFiltered(prefix + ".fileName", diskImagePath); - if (hddMode != null) { - addFiltered(prefix + ".mode", hddMode); - addFiltered(prefix + ".redo", ""); - addFiltered(prefix + ".redoLogDir", redoDir); - } - config.remove("#SLX_HDD_BUS"); - config.remove("#SLX_HDD_CHIP"); - return true; - } - - public boolean addDefaultNat() { - addFiltered("ethernet0.present", "TRUE"); - addFiltered("ethernet0.connectionType", "nat"); - return true; - } - - public boolean addEthernet(VmMetaData.EtherType type) { - boolean ret = false; - int index = 0; - for (;; ++index) { - if (config.get("ethernet" + index + ".present") == null) { - break; - } - } - switch (type) { - case NAT: - ret = addEthernet(index, EthernetType.NAT); - break; - case BRIDGED: - ret = addEthernet(index, EthernetType.BRIDGED); - break; - case HOST_ONLY: - ret = addEthernet(index, EthernetType.HOST_ONLY); - break; - default: - // Should not come to this... - break; - } - return ret; - } - - public boolean addEthernet(int index, EthernetType type) { - String ether = "ethernet" + index; - addFiltered(ether + ".present", "TRUE"); - addFiltered(ether + ".connectionType", "custom"); - addFiltered(ether + ".vnet", type.vmnet); - if (config.get(ether + ".virtualDev") == null) { - String dev = config.get("ethernet0.virtualDev"); - if (dev != null) { - addFiltered(ether + ".virtualDev", dev); - } - } - return true; - } - - public void addFloppy(int index, String image, boolean readOnly) { - String pre = "floppy" + index; - addFiltered(pre + ".present", "TRUE"); - if (image == null) { - addFiltered(pre + ".startConnected", "FALSE"); - addFiltered(pre + ".fileType", "device"); - config.remove(pre + ".fileName"); - config.remove(pre + ".readonly"); - addFiltered(pre + ".autodetect", "TRUE"); - } else { - addFiltered(pre + ".startConnected", "TRUE"); - addFiltered(pre + ".fileType", "file"); - addFiltered(pre + ".fileName", image); - addFiltered(pre + ".readonly", vmBoolean(readOnly)); - config.remove(pre + ".autodetect"); - } - } - - public boolean addCdrom(String image) { - for (String port : new String[]{"ide0:0", "ide0:1", "ide1:0", "ide1:1", "scsi0:1"}) { - if (!isSetAndTrue(port + ".present")) { - addFiltered(port + ".present", "TRUE"); - if (image == null) { - addFiltered(port + ".autodetect", "TRUE"); - addFiltered(port + ".deviceType", "cdrom-raw"); - config.remove(port + ".fileName"); - } else { - config.remove(port + ".autodetect"); - addFiltered(port + ".deviceType", "cdrom-image"); - addFiltered(port + ".fileName", image); - } - return true; - } - } - return false; - } - - private static String vmBoolean(boolean var) { - return Boolean.toString(var).toUpperCase(); - } - - private static String vmInteger(int val) { - return Integer.toString(val); - } - - @Override - public boolean disableSuspend() { - addFiltered("suspend.disabled", "TRUE"); - return true; - } - - @Override - public boolean addDisplayName(String name) { - addFiltered("displayName", name); - return true; - } - - @Override - public boolean addRam(int mem) { - addFiltered("memsize", Integer.toString(mem)); - return true; - } - - public void setOs(String vendorOsId) { - addFiltered("guestOS", vendorOsId); - setOs(TConst.VIRT_VMWARE, vendorOsId); - } - - @Override - public byte[] getFilteredDefinitionArray() { - return config.toString(true, false).getBytes(StandardCharsets.UTF_8); - } - - public byte[] getDefinitionArray() { - return config.toString(false, false).getBytes(StandardCharsets.UTF_8); - } - - @Override - public Virtualizer getVirtualizer() { - return virtualizer; - } - - private static class Device { - - public boolean present = false; - public String deviceType = null; - public String filename = null; - - @Override - public String toString() { - return filename + " is " + deviceType + " (present: " + present + ")"; - } - } - - private static class Controller { - - public boolean present = true; // Seems to be implicit, seen at least for IDE... - public String virtualDev = null; - Map<String, Device> devices = new HashMap<>(); - - @Override - public String toString() { - return virtualDev + " is (present: " + present + "): " + devices.toString(); - } - } - - @Override - public void enableUsb(boolean enabled) { - addFiltered("usb.present", vmBoolean(enabled)); - addFiltered("ehci.present", vmBoolean(enabled)); - } - - @Override - public void applySettingsForLocalEdit() { - addFiltered("gui.applyHostDisplayScalingToGuest", "FALSE"); - } - - public String getValue(String key) { - return config.get(key); - } - - public void setSoundCard(VmMetaData.SoundCardType type) { - VmWareSoundCardMeta soundCardMeta = soundCards.get(type); - addFiltered("sound.present", vmBoolean(soundCardMeta.isPresent)); - if (soundCardMeta.value != null) { - addFiltered("sound.virtualDev", soundCardMeta.value); - } else { - config.remove("sound.virtualDev"); - } - } - - public VmMetaData.SoundCardType getSoundCard() { - if (!isSetAndTrue("sound.present") || !isSetAndTrue("sound.autodetect")) { - return VmMetaData.SoundCardType.NONE; - } - String current = config.get("sound.virtualDev"); - if (current != null) { - VmWareSoundCardMeta soundCardMeta = null; - for (VmMetaData.SoundCardType type : VmMetaData.SoundCardType.values()) { - soundCardMeta = soundCards.get(type); - if (soundCardMeta != null) { - if (current.equals(soundCardMeta.value)) { - return type; - } - } - } - } - return VmMetaData.SoundCardType.DEFAULT; - } - - public void setDDAcceleration(VmMetaData.DDAcceleration type) { - VmWareDDAccelMeta ddaMeta = ddacc.get(type); - addFiltered("mks.enable3d", vmBoolean(ddaMeta.isPresent)); - } - - public VmMetaData.DDAcceleration getDDAcceleration() { - if (isSetAndTrue("mks.enable3d")) { - return VmMetaData.DDAcceleration.ON; - } else { - return VmMetaData.DDAcceleration.OFF; - } - } - - public void setHWVersion(VmMetaData.HWVersion type) { - VmWareHWVersionMeta hwVersionMeta = hwversion.get(type); - addFiltered("virtualHW.version", vmInteger(hwVersionMeta.version)); - } - - public VmMetaData.HWVersion getHWVersion() { - int currentValue = Util.parseInt(config.get("virtualHW.version"), -1); - VmWareHWVersionMeta hwVersionMeta = null; - for (VmMetaData.HWVersion ver : VmMetaData.HWVersion.values()) { - hwVersionMeta = hwversion.get(ver); - if (hwVersionMeta == null) { - continue; - } - if (currentValue == hwVersionMeta.version) { - return ver; - } - } - return HWVersion.NONE; - } - - public void setEthernetDevType(int cardIndex, VmMetaData.EthernetDevType type) { - VmWareEthernetDevTypeMeta ethernetDevTypeMeta = networkCards.get(type); - if (ethernetDevTypeMeta.value != null) { - addFiltered("ethernet" + cardIndex + ".virtualDev", ethernetDevTypeMeta.value); - } else { - config.remove("ethernet" + cardIndex + ".virtualDev"); - } - } - - public EthernetDevType getEthernetDevType(int cardIndex) { - String temp = config.get("ethernet" + cardIndex + ".virtualDev"); - if (temp != null) { - VmWareEthernetDevTypeMeta ethernetDevTypeMeta = null; - for (EthernetDevType type : VmMetaData.EthernetDevType.values()) { - ethernetDevTypeMeta = networkCards.get(type); - if (ethernetDevTypeMeta == null) { - continue; - } - if (temp.equals(ethernetDevTypeMeta.value)) { - return type; - } - } - } - return EthernetDevType.AUTO; - } - - @Override - public boolean addCpuCoreCount(int numCores) { - // TODO actually add the cpu core count to the machine description - return false; - } +class VmwareUsbSpeed +{ + public final String keyName; + public final int speedNumeric; + + public VmwareUsbSpeed( int speed, String key ) + { + this.keyName = key + ".present"; + this.speedNumeric = speed; + } +} - public void registerVirtualHW() { - soundCards.put(VmMetaData.SoundCardType.NONE, new VmWareSoundCardMeta(false, null)); - soundCards.put(VmMetaData.SoundCardType.DEFAULT, new VmWareSoundCardMeta(true, null)); - soundCards.put(VmMetaData.SoundCardType.SOUND_BLASTER, new VmWareSoundCardMeta(true, "sb16")); - soundCards.put(VmMetaData.SoundCardType.ES, new VmWareSoundCardMeta(true, "es1371")); - soundCards.put(VmMetaData.SoundCardType.HD_AUDIO, new VmWareSoundCardMeta(true, "hdaudio")); - - ddacc.put(VmMetaData.DDAcceleration.OFF, new VmWareDDAccelMeta(false)); - ddacc.put(VmMetaData.DDAcceleration.ON, new VmWareDDAccelMeta(true)); - - hwversion.put(VmMetaData.HWVersion.NONE, new VmWareHWVersionMeta(0)); - hwversion.put(VmMetaData.HWVersion.THREE, new VmWareHWVersionMeta(3)); - hwversion.put(VmMetaData.HWVersion.FOUR, new VmWareHWVersionMeta(4)); - hwversion.put(VmMetaData.HWVersion.SIX, new VmWareHWVersionMeta(6)); - hwversion.put(VmMetaData.HWVersion.SEVEN, new VmWareHWVersionMeta(7)); - hwversion.put(VmMetaData.HWVersion.EIGHT, new VmWareHWVersionMeta(8)); - hwversion.put(VmMetaData.HWVersion.NINE, new VmWareHWVersionMeta(9)); - hwversion.put(VmMetaData.HWVersion.TEN, new VmWareHWVersionMeta(10)); - hwversion.put(VmMetaData.HWVersion.ELEVEN, new VmWareHWVersionMeta(11)); - hwversion.put(VmMetaData.HWVersion.TWELVE, new VmWareHWVersionMeta(12)); - - networkCards.put(VmMetaData.EthernetDevType.AUTO, new VmWareEthernetDevTypeMeta(null)); - networkCards.put(VmMetaData.EthernetDevType.PCNET32, new VmWareEthernetDevTypeMeta("vlance")); - networkCards.put(VmMetaData.EthernetDevType.E1000, new VmWareEthernetDevTypeMeta("e1000")); - networkCards.put(VmMetaData.EthernetDevType.E1000E, new VmWareEthernetDevTypeMeta("e1000e")); - networkCards.put(VmMetaData.EthernetDevType.VMXNET, new VmWareEthernetDevTypeMeta("vmxnet")); - networkCards.put(VmMetaData.EthernetDevType.VMXNET3, new VmWareEthernetDevTypeMeta("vmxnet3")); - } +public class VmwareMetaData extends VmMetaData<VmWareSoundCardMeta, VmWareDDAccelMeta, VmWareHWVersionMeta, VmWareEthernetDevTypeMeta, VmwareUsbSpeed> +{ + + private static final Logger LOGGER = Logger.getLogger( VmwareMetaData.class ); + + private static final Virtualizer virtualizer = new Virtualizer( TConst.VIRT_VMWARE, "VMware" ); + + private static final Pattern hddKey = Pattern.compile( "^(ide\\d|scsi\\d|sata\\d):?(\\d)?\\.(.*)", Pattern.CASE_INSENSITIVE ); + + // Lowercase list of allowed settings for upload (as regex) + private static final Pattern[] whitelist; + + private final VmwareConfig config; + + // Init static members + static { + String[] list = { "^guestos", "^uuid\\.bios", "^config\\.version", "^ehci[.:]", "^mks\\.enable3d", "^virtualhw\\.", + "^sound[.:]", "\\.pcislotnumber$", "^pcibridge", "\\.virtualdev$", "^tools\\.syncTime$", "^time\\.synchronize", + "^bios\\.bootDelay", "^rtc\\.", "^xhci[.:]", "^usb_xhci[.:]", "\\.deviceType$", "\\.port$", "\\.parent$", "^usb[.:]", + "^firmware" }; + whitelist = new Pattern[ list.length ]; + for ( int i = 0; i < list.length; ++i ) { + whitelist[i] = Pattern.compile( list[i].toLowerCase() ); + } + } + + public static enum EthernetType + { + NAT( "vmnet1" ), BRIDGED( "vmnet0" ), HOST_ONLY( "vmnet2" ); + + public final String vmnet; + + private EthernetType( String vnet ) + { + this.vmnet = vnet; + } + } + + private final Map<String, Controller> disks = new HashMap<>(); + + public VmwareMetaData( List<OperatingSystem> osList, File file ) throws IOException, UnsupportedVirtualizerFormatException + { + super( osList ); + this.config = new VmwareConfig( file ); + init(); + } + + public VmwareMetaData( List<OperatingSystem> osList, byte[] vmxContent, int length ) throws UnsupportedVirtualizerFormatException + { + super( osList ); + this.config = new VmwareConfig( vmxContent, length ); // still unfiltered + init(); // now filtered + } + + private void init() + { + registerVirtualHW(); + + for ( Entry<String, ConfigEntry> entry : config.entrySet() ) { + handleLoadEntry( entry ); + } + // Fix accidentally filtered USB config if we see EHCI is present + if ( isSetAndTrue( "ehci.present" ) && !isSetAndTrue( "usb.present" ) ) { + addFiltered( "usb.present", "TRUE" ); + } + // if we find this tag, we already went through the hdd's - so we're done. + if ( config.get( "#SLX_HDD_BUS" ) != null ) { + return; + } + // Now find the HDDs and add to list + for ( Entry<String, Controller> cEntry : disks.entrySet() ) { + Controller controller = cEntry.getValue(); + String controllerType = cEntry.getKey(); + if ( !controller.present ) + continue; + for ( Entry<String, Device> dEntry : controller.devices.entrySet() ) { + Device device = dEntry.getValue(); + if ( !device.present ) + continue; // Not present + if ( device.deviceType != null && !device.deviceType.toLowerCase().endsWith( "disk" ) ) + continue; // Not a HDD + DriveBusType bus = null; + if ( controllerType.startsWith( "ide" ) ) { + bus = DriveBusType.IDE; + } else if ( controllerType.startsWith( "scsi" ) ) { + bus = DriveBusType.SCSI; + } else if ( controllerType.startsWith( "sata" ) ) { + bus = DriveBusType.SATA; + } + hdds.add( new HardDisk( controller.virtualDev, bus, device.filename ) ); + } + } + // TODO check if this machine is in a paused/suspended state + this.isMachineSnapshot = false; + + // Add HDD to cleaned vmx + if ( !hdds.isEmpty() ) { + HardDisk hdd = hdds.get( 0 ); + addFiltered( "#SLX_HDD_BUS", hdd.bus.toString() ); + if ( hdd.chipsetDriver != null ) { + addFiltered( "#SLX_HDD_CHIP", hdd.chipsetDriver ); + } + } + } + + private void addFiltered( String key, String value ) + { + config.set( key, value ).filtered( true ); + } + + private boolean isSetAndTrue( String key ) + { + String value = config.get( key ); + return value != null && value.equalsIgnoreCase( "true" ); + } + + private void handleLoadEntry( Entry<String, ConfigEntry> entry ) + { + String lowerKey = entry.getKey().toLowerCase(); + // Cleaned vmx construction + for ( Pattern exp : whitelist ) { + if ( exp.matcher( lowerKey ).find() ) { + entry.getValue().filtered( true ); + break; + } + } + // + // Dig Usable meta data + String value = entry.getValue().getValue(); + if ( lowerKey.equals( "guestos" ) ) { + setOs( value ); + return; + } + if ( lowerKey.equals( "displayname" ) ) { + displayName = value; + return; + } + Matcher hdd = hddKey.matcher( entry.getKey() ); + if ( hdd.find() ) { + handleHddEntry( hdd.group( 1 ).toLowerCase(), hdd.group( 2 ), hdd.group( 3 ), value ); + } + } + + private void handleHddEntry( String controllerStr, String deviceStr, String property, String value ) + { + Controller controller = disks.get( controllerStr ); + if ( controller == null ) { + controller = new Controller(); + disks.put( controllerStr, controller ); + } + if ( deviceStr == null || deviceStr.isEmpty() ) { + // Controller property + if ( property.equalsIgnoreCase( "present" ) ) { + controller.present = Boolean.parseBoolean( value ); + } else if ( property.equalsIgnoreCase( "virtualDev" ) ) { + controller.virtualDev = value; + } + return; + } + // Device property + Device device = controller.devices.get( deviceStr ); + if ( device == null ) { + device = new Device(); + controller.devices.put( deviceStr, device ); + } + if ( property.equalsIgnoreCase( "deviceType" ) ) { + device.deviceType = value; + } else if ( property.equalsIgnoreCase( "filename" ) ) { + device.filename = value; + } else if ( property.equalsIgnoreCase( "present" ) ) { + device.present = Boolean.parseBoolean( value ); + } + } + + @Override + public boolean addHddTemplate( File diskImage, String hddMode, String redoDir ) + { + return addHddTemplate( diskImage.getName(), hddMode, redoDir ); + } + + @Override + public boolean addHddTemplate( String diskImagePath, String hddMode, String redoDir ) + { + if ( diskImagePath.isEmpty() ) { + LOGGER.error( "Empty disk image path given!" ); + return false; + } + DriveBusType bus; + try { + bus = DriveBusType.valueOf( config.get( "#SLX_HDD_BUS" ) ); + } catch ( Exception e ) { + LOGGER.warn( "Unknown bus type: " + config.get( "#SLX_HDD_BUS" ) + ". Cannot add hdd config." ); + return false; + } + String chipset = config.get( "#SLX_HDD_CHIP" ); + String prefix; + switch ( bus ) { + case IDE: + prefix = "ide0:0"; + addFiltered( "ide0.present", "TRUE" ); + break; + case SATA: + // Cannot happen?... use lsisas1068 + case SCSI: + prefix = "scsi0:0"; + addFiltered( "scsi0.present", "TRUE" ); + if ( chipset != null ) { + addFiltered( "scsi0.virtualDev", chipset ); + } + break; + default: + LOGGER.warn( "Unknown HDD bus type: " + bus.toString() ); + return false; + } + // Gen + addFiltered( prefix + ".present", "TRUE" ); + addFiltered( prefix + ".deviceType", "disk" ); + addFiltered( prefix + ".fileName", diskImagePath ); + if ( hddMode != null ) { + addFiltered( prefix + ".mode", hddMode ); + addFiltered( prefix + ".redo", "" ); + addFiltered( prefix + ".redoLogDir", redoDir ); + } + config.remove( "#SLX_HDD_BUS" ); + config.remove( "#SLX_HDD_CHIP" ); + return true; + } + + public boolean addDefaultNat() + { + addFiltered( "ethernet0.present", "TRUE" ); + addFiltered( "ethernet0.connectionType", "nat" ); + return true; + } + + public boolean addEthernet( VmMetaData.EtherType type ) + { + boolean ret = false; + int index = 0; + for ( ;; ++index ) { + if ( config.get( "ethernet" + index + ".present" ) == null ) + break; + } + switch ( type ) { + case NAT: + ret = addEthernet( index, EthernetType.NAT ); + break; + case BRIDGED: + ret = addEthernet( index, EthernetType.BRIDGED ); + break; + case HOST_ONLY: + ret = addEthernet( index, EthernetType.HOST_ONLY ); + break; + default: + // Should not come to this... + break; + } + return ret; + } + + public boolean addEthernet( int index, EthernetType type ) + { + String ether = "ethernet" + index; + addFiltered( ether + ".present", "TRUE" ); + addFiltered( ether + ".connectionType", "custom" ); + addFiltered( ether + ".vnet", type.vmnet ); + if ( config.get( ether + ".virtualDev" ) == null ) { + String dev = config.get( "ethernet0.virtualDev" ); + if ( dev != null ) { + addFiltered( ether + ".virtualDev", dev ); + } + } + return true; + } + + public void addFloppy( int index, String image, boolean readOnly ) + { + String pre = "floppy" + index; + addFiltered( pre + ".present", "TRUE" ); + if ( image == null ) { + addFiltered( pre + ".startConnected", "FALSE" ); + addFiltered( pre + ".fileType", "device" ); + config.remove( pre + ".fileName" ); + config.remove( pre + ".readonly" ); + addFiltered( pre + ".autodetect", "TRUE" ); + } else { + addFiltered( pre + ".startConnected", "TRUE" ); + addFiltered( pre + ".fileType", "file" ); + addFiltered( pre + ".fileName", image ); + addFiltered( pre + ".readonly", vmBoolean( readOnly ) ); + config.remove( pre + ".autodetect" ); + } + } + + public boolean addCdrom( String image ) + { + for ( String port : new String[] { "ide0:0", "ide0:1", "ide1:0", "ide1:1", "scsi0:1" } ) { + if ( !isSetAndTrue( port + ".present" ) ) { + addFiltered( port + ".present", "TRUE" ); + if ( image == null ) { + addFiltered( port + ".autodetect", "TRUE" ); + addFiltered( port + ".deviceType", "cdrom-raw" ); + config.remove( port + ".fileName" ); + } else { + config.remove( port + ".autodetect" ); + addFiltered( port + ".deviceType", "cdrom-image" ); + addFiltered( port + ".fileName", image ); + } + return true; + } + } + return false; + } + + private static String vmBoolean( boolean var ) + { + return Boolean.toString( var ).toUpperCase(); + } + + private static String vmInteger( int val ) + { + return Integer.toString( val ); + } + + @Override + public boolean tweakForNonPersistent() + { + addFiltered( "suspend.disabled", "TRUE" ); + return true; + } + + @Override + public boolean addDisplayName( String name ) + { + addFiltered( "displayName", name ); + return true; + } + + @Override + public boolean addRam( int mem ) + { + addFiltered( "memsize", Integer.toString( mem ) ); + return true; + } + + public void setOs( String vendorOsId ) + { + addFiltered( "guestOS", vendorOsId ); + setOs( TConst.VIRT_VMWARE, vendorOsId ); + } + + @Override + public byte[] getFilteredDefinitionArray() + { + return config.toString( true, false ).getBytes( StandardCharsets.UTF_8 ); + } + + public byte[] getDefinitionArray() + { + return config.toString( false, false ).getBytes( StandardCharsets.UTF_8 ); + } + + @Override + public Virtualizer getVirtualizer() + { + return virtualizer; + } + + private static class Device + { + public boolean present = false; + public String deviceType = null; + public String filename = null; + + @Override + public String toString() + { + return filename + " is " + deviceType + " (present: " + present + ")"; + } + } + + private static class Controller + { + public boolean present = true; // Seems to be implicit, seen at least for IDE... + public String virtualDev = null; + Map<String, Device> devices = new HashMap<>(); + + @Override + public String toString() + { + return virtualDev + " is (present: " + present + "): " + devices.toString(); + } + } + + @Override + public void applySettingsForLocalEdit() + { + addFiltered( "gui.applyHostDisplayScalingToGuest", "FALSE" ); + } + + public String getValue( String key ) + { + return config.get( key ); + } + + public void setSoundCard( VmMetaData.SoundCardType type ) + { + VmWareSoundCardMeta soundCardMeta = soundCards.get( type ); + addFiltered( "sound.present", vmBoolean( soundCardMeta.isPresent ) ); + if ( soundCardMeta.value != null ) { + addFiltered( "sound.virtualDev", soundCardMeta.value ); + } else { + config.remove( "sound.virtualDev" ); + } + } + + public VmMetaData.SoundCardType getSoundCard() + { + if ( !isSetAndTrue( "sound.present" ) || !isSetAndTrue( "sound.autodetect" ) ) { + return VmMetaData.SoundCardType.NONE; + } + String current = config.get( "sound.virtualDev" ); + if ( current != null ) { + VmWareSoundCardMeta soundCardMeta = null; + for ( VmMetaData.SoundCardType type : VmMetaData.SoundCardType.values() ) { + soundCardMeta = soundCards.get( type ); + if ( soundCardMeta != null ) { + if ( current.equals( soundCardMeta.value ) ) { + return type; + } + } + } + } + return VmMetaData.SoundCardType.DEFAULT; + } + + public void setDDAcceleration( VmMetaData.DDAcceleration type ) + { + VmWareDDAccelMeta ddaMeta = ddacc.get( type ); + addFiltered( "mks.enable3d", vmBoolean( ddaMeta.isPresent ) ); + } + + public VmMetaData.DDAcceleration getDDAcceleration() + { + if ( isSetAndTrue( "mks.enable3d" ) ) { + return VmMetaData.DDAcceleration.ON; + } else { + return VmMetaData.DDAcceleration.OFF; + } + } + + public void setHWVersion( VmMetaData.HWVersion type ) + { + VmWareHWVersionMeta hwVersionMeta = hwversion.get( type ); + addFiltered( "virtualHW.version", vmInteger( hwVersionMeta.version ) ); + } + + public VmMetaData.HWVersion getHWVersion() + { + int currentValue = Util.parseInt( config.get( "virtualHW.version" ), -1 ); + VmWareHWVersionMeta hwVersionMeta = null; + for ( VmMetaData.HWVersion ver : VmMetaData.HWVersion.values() ) { + hwVersionMeta = hwversion.get( ver ); + if ( hwVersionMeta == null ) { + continue; + } + if ( currentValue == hwVersionMeta.version ) { + return ver; + } + } + return HWVersion.NONE; + } + + public void setEthernetDevType( int cardIndex, VmMetaData.EthernetDevType type ) + { + VmWareEthernetDevTypeMeta ethernetDevTypeMeta = networkCards.get( type ); + if ( ethernetDevTypeMeta.value != null ) { + addFiltered( "ethernet" + cardIndex + ".virtualDev", ethernetDevTypeMeta.value ); + } else { + config.remove( "ethernet" + cardIndex + ".virtualDev" ); + } + } + + public VmMetaData.EthernetDevType getEthernetDevType( int cardIndex ) + { + String temp = config.get( "ethernet" + cardIndex + ".virtualDev" ); + if ( temp != null ) { + VmWareEthernetDevTypeMeta ethernetDevTypeMeta = null; + for ( VmMetaData.EthernetDevType type : VmMetaData.EthernetDevType.values() ) { + ethernetDevTypeMeta = networkCards.get( type ); + if ( ethernetDevTypeMeta == null ) { + continue; + } + if ( temp.equals( ethernetDevTypeMeta.value ) ) { + return type; + } + } + } + return VmMetaData.EthernetDevType.AUTO; + } + + @Override + public void setMaxUsbSpeed( VmMetaData.UsbSpeed newSpeed ) + { + if ( newSpeed == null ) { + newSpeed = VmMetaData.UsbSpeed.NONE; + } + VmwareUsbSpeed newSpeedMeta = usbSpeeds.get( newSpeed ); + if ( newSpeedMeta == null ) { + throw new RuntimeException( "USB Speed " + newSpeed.name() + " not registered with VMware" ); + } + for ( VmwareUsbSpeed meta : usbSpeeds.values() ) { + if ( meta == null ) + continue; // Should not happen + if ( meta.keyName == null ) + continue; // "No USB" has no config entry, obviously + if ( meta.speedNumeric <= newSpeedMeta.speedNumeric ) { + // Enable desired speed class, plus all lower ones + addFiltered( meta.keyName, "TRUE" ); + } else { + // This one is higher – remove + config.remove( meta.keyName ); + } + } + // VMware 14+ needs this to use USB 3.0 devices at USB 3.0 ports in VMs configured for < 3.0 + if ( newSpeedMeta.speedNumeric > 0 && newSpeedMeta.speedNumeric < 3 ) { + addFiltered( "usb.mangleUsb3Speed", "TRUE" ); + } + } + + @Override + public VmMetaData.UsbSpeed getMaxUsbSpeed() + { + int max = 0; + VmMetaData.UsbSpeed maxEnum = VmMetaData.UsbSpeed.NONE; + for ( Entry<VmMetaData.UsbSpeed, VmwareUsbSpeed> entry : usbSpeeds.entrySet() ) { + VmwareUsbSpeed v = entry.getValue(); + if ( v.speedNumeric > max && isSetAndTrue( v.keyName ) ) { + max = v.speedNumeric; + maxEnum = entry.getKey(); + } + } + return maxEnum; + } + + @Override + public boolean addCpuCoreCount( int numCores ) + { + // TODO actually add the cpu core count to the machine description + return false; + } + + public void registerVirtualHW() + { + soundCards.put( VmMetaData.SoundCardType.NONE, new VmWareSoundCardMeta( false, null ) ); + soundCards.put( VmMetaData.SoundCardType.DEFAULT, new VmWareSoundCardMeta( true, null ) ); + soundCards.put( VmMetaData.SoundCardType.SOUND_BLASTER, new VmWareSoundCardMeta( true, "sb16" ) ); + soundCards.put( VmMetaData.SoundCardType.ES, new VmWareSoundCardMeta( true, "es1371" ) ); + soundCards.put( VmMetaData.SoundCardType.HD_AUDIO, new VmWareSoundCardMeta( true, "hdaudio" ) ); + + ddacc.put( VmMetaData.DDAcceleration.OFF, new VmWareDDAccelMeta( false ) ); + ddacc.put( VmMetaData.DDAcceleration.ON, new VmWareDDAccelMeta( true ) ); + + hwversion.put( VmMetaData.HWVersion.NONE, new VmWareHWVersionMeta( 0 ) ); + hwversion.put( VmMetaData.HWVersion.THREE, new VmWareHWVersionMeta( 3 ) ); + hwversion.put( VmMetaData.HWVersion.FOUR, new VmWareHWVersionMeta( 4 ) ); + hwversion.put( VmMetaData.HWVersion.SIX, new VmWareHWVersionMeta( 6 ) ); + hwversion.put( VmMetaData.HWVersion.SEVEN, new VmWareHWVersionMeta( 7 ) ); + hwversion.put( VmMetaData.HWVersion.EIGHT, new VmWareHWVersionMeta( 8 ) ); + hwversion.put( VmMetaData.HWVersion.NINE, new VmWareHWVersionMeta( 9 ) ); + hwversion.put( VmMetaData.HWVersion.TEN, new VmWareHWVersionMeta( 10 ) ); + hwversion.put( VmMetaData.HWVersion.ELEVEN, new VmWareHWVersionMeta( 11 ) ); + hwversion.put( VmMetaData.HWVersion.TWELVE, new VmWareHWVersionMeta( 12 ) ); + hwversion.put( VmMetaData.HWVersion.FOURTEEN, new VmWareHWVersionMeta( 14 ) ); + hwversion.put( VmMetaData.HWVersion.FIFTEEN, new VmWareHWVersionMeta( 15 ) ); + hwversion.put( VmMetaData.HWVersion.FIFTEEN_ONE, new VmWareHWVersionMeta( 16 ) ); + + networkCards.put( VmMetaData.EthernetDevType.AUTO, new VmWareEthernetDevTypeMeta( null ) ); + networkCards.put( VmMetaData.EthernetDevType.PCNET32, new VmWareEthernetDevTypeMeta( "vlance" ) ); + networkCards.put( VmMetaData.EthernetDevType.E1000, new VmWareEthernetDevTypeMeta( "e1000" ) ); + networkCards.put( VmMetaData.EthernetDevType.E1000E, new VmWareEthernetDevTypeMeta( "e1000e" ) ); + networkCards.put( VmMetaData.EthernetDevType.VMXNET, new VmWareEthernetDevTypeMeta( "vmxnet" ) ); + networkCards.put( VmMetaData.EthernetDevType.VMXNET3, new VmWareEthernetDevTypeMeta( "vmxnet3" ) ); + + usbSpeeds.put( VmMetaData.UsbSpeed.NONE, new VmwareUsbSpeed( 0, null )); + usbSpeeds.put( VmMetaData.UsbSpeed.USB1_1, new VmwareUsbSpeed( 1, "usb" ) ); + usbSpeeds.put( VmMetaData.UsbSpeed.USB2_0, new VmwareUsbSpeed( 2, "ehci" ) ); + usbSpeeds.put( VmMetaData.UsbSpeed.USB3_0, new VmwareUsbSpeed( 3, "usb_xhci" ) ); + } } |