From 8a0d508f5a04f930e9945131db4f70591fd02e12 Mon Sep 17 00:00:00 2001 From: Victor Mocanu Date: Mon, 16 Apr 2018 17:14:45 +0200 Subject: [vbox] add support for VirtualBox --- src/main/java/org/openslx/util/vm/DiskImage.java | 117 +++- .../java/org/openslx/util/vm/QemuMetaData.java | 219 ++++++++ .../vm/UnsupportedVirtualizerFormatException.java | 9 + src/main/java/org/openslx/util/vm/VboxConfig.java | 611 +++++++++++++++++++++ .../java/org/openslx/util/vm/VboxMetaData.java | 436 +++++++++++++++ src/main/java/org/openslx/util/vm/VmMetaData.java | 267 ++++++++- .../java/org/openslx/util/vm/VmwareConfig.java | 40 +- .../java/org/openslx/util/vm/VmwareMetaData.java | 298 +++++----- 8 files changed, 1804 insertions(+), 193 deletions(-) create mode 100644 src/main/java/org/openslx/util/vm/QemuMetaData.java create mode 100644 src/main/java/org/openslx/util/vm/UnsupportedVirtualizerFormatException.java create mode 100644 src/main/java/org/openslx/util/vm/VboxConfig.java create mode 100644 src/main/java/org/openslx/util/vm/VboxMetaData.java (limited to 'src') diff --git a/src/main/java/org/openslx/util/vm/DiskImage.java b/src/main/java/org/openslx/util/vm/DiskImage.java index 80eec2a..622c1a4 100644 --- a/src/main/java/org/openslx/util/vm/DiskImage.java +++ b/src/main/java/org/openslx/util/vm/DiskImage.java @@ -5,16 +5,19 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; +import org.apache.log4j.Logger; import org.openslx.bwlp.thrift.iface.Virtualizer; import org.openslx.util.Util; public class DiskImage { - /** * Big endian representation of the 4 bytes 'KDMV' */ private static final int VMDK_MAGIC = 0x4b444d56; + private static final String VDI_PREFIX = "<<< "; + private static final String VDI_SUFFIX = "Disk Image >>>"; + private static final String QEMU = "QFI"; public enum ImageFormat { @@ -42,6 +45,8 @@ public class DiskImage return VMDK; if ( virtId.equals( "virtualbox" ) ) return VDI; + if ( virtId.equals( "qemukvm" ) ) + return QCOW2; return null; } } @@ -53,32 +58,94 @@ public class DiskImage public final String subFormat; public final int hwVersion; - public DiskImage( File disk ) throws FileNotFoundException, IOException, - UnknownImageFormatException + public ImageFormat getImageFormat() + { + return format; + } + + public DiskImage( File disk ) throws FileNotFoundException, IOException, UnknownImageFormatException { - // For now we only support VMDK... try ( RandomAccessFile file = new RandomAccessFile( disk, "r" ) ) { - if ( file.readInt() != VMDK_MAGIC ) - throw new UnknownImageFormatException(); - file.seek( 512 ); - byte[] buffer = new byte[ 2048 ]; - file.readFully( buffer ); - VmwareConfig config = new VmwareConfig( buffer, findNull( buffer ) ); - subFormat = 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 ); + // vmdk + if ( file.readInt() == VMDK_MAGIC ) { + file.seek( 512 ); + byte[] buffer = new byte[ 2048 ]; + file.readFully( buffer ); + VmwareConfig config; + try { + config = new VmwareConfig( buffer, findNull( buffer ) ); + } catch ( UnsupportedVirtualizerFormatException e ) { + config = null; + } + if ( config != null ) { + subFormat = 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 ); + } + return; + } + } + // vdi + file.seek( 0 ); + byte[] prefixBuffer = new byte[ VDI_PREFIX.length() ]; + file.readFully( prefixBuffer ); + String prefixString = new String( prefixBuffer ); + if ( VDI_PREFIX.equals( prefixString ) ) { + + byte[] localBuffer = new byte[ 1 ]; + byte[] suffixBuffer = new byte[ VDI_SUFFIX.length() - 1 ]; + // 30 in this case would be the remaining length of the vdi header + // the longest string to date would be "<<< QEMU VM Virtual Disk Image >>>" + // if the loop doesn't find the first letter of the VID_SUFFIX then we have another format on our hands and should throw exception + for ( int i = 0; i < 30; i++ ) { + file.readFully( localBuffer ); + String localString = new String( localBuffer ); + + if ( !localString.equals( VDI_SUFFIX.substring( 0, 1 ) ) ) { + continue; + } + file.readFully( suffixBuffer ); + String suffixString = new String( suffixBuffer ); + if ( suffixString.equals( VDI_SUFFIX.substring( 1 ) ) ) { + // TODO still don't know where they are found in a .vdi file + this.isStandalone = true; + this.isCompressed = false; + this.isSnapshot = false; + this.format = ImageFormat.VDI; + this.subFormat = ""; + this.hwVersion = 0; + return; + } else { + // this will ensure the search doesn't stop at the first D we find + file.seek( i + VDI_PREFIX.length() + 1 ); + } + } + } + //qcow + file.seek( 0 ); + byte[] qcowBuffer = new byte[ QEMU.length() ]; + file.readFully( qcowBuffer ); + String qcowString = new String( qcowBuffer ); + if ( QEMU.equals( qcowString ) ) { + // dummy values + this.isStandalone = true; + this.isCompressed = false; + this.isSnapshot = false; + this.format = ImageFormat.QCOW2; + this.subFormat = ""; + this.hwVersion = 0; + return; } } + throw new UnknownImageFormatException(); } private int findNull( byte[] buffer ) @@ -96,13 +163,11 @@ public class DiskImage return false; if ( parent != null && !parent.equalsIgnoreCase( "ffffffff" ) ) return false; - return type.equalsIgnoreCase( "streamOptimized" ) - || type.equalsIgnoreCase( "monolithicSparse" ); + return type.equalsIgnoreCase( "streamOptimized" ) || type.equalsIgnoreCase( "monolithicSparse" ); } public static class UnknownImageFormatException extends Exception { private static final long serialVersionUID = -6647935235475007171L; } - } diff --git a/src/main/java/org/openslx/util/vm/QemuMetaData.java b/src/main/java/org/openslx/util/vm/QemuMetaData.java new file mode 100644 index 0000000..f470376 --- /dev/null +++ b/src/main/java/org/openslx/util/vm/QemuMetaData.java @@ -0,0 +1,219 @@ +package org.openslx.util.vm; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.OperatingSystem; +import org.openslx.bwlp.thrift.iface.Virtualizer; + +public class QemuMetaData extends VmMetaData +{ + + private Map arguments = new HashMap(); + // the above map's elements will take the place of in the config string + private static String config = "qemu-system-i386 -enable-kvm \n\r qemu-system-x86_64 -enable-kvm"; + private static final Logger LOGGER = Logger.getLogger( QemuMetaData.class ); + + private static final Virtualizer virtualizer = new Virtualizer( "qemukvm", "QEMU-KVM" ); + + public QemuMetaData( List osList, File file ) + { + super( osList ); + displayName = file.getName().substring( 0, file.getName().indexOf( "." ) ); + setOs( "qemukvm", "anyOs" ); + hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, file.getAbsolutePath() ) ); + makeStartSequence(); + } + + public QemuMetaData( List osList, byte[] vmContent ) + { + super( osList ); + config = new String( vmContent ); + displayName = "QemuVM"; + setOs( "qemukvm", "anyOs" ); + } + + // initiates the arguments map with a default working sequence that will later be used in the definition array + public void makeStartSequence() + { + arguments.put( "cpu", "host" ); + arguments.put( "smp", "2" ); + arguments.put( "m", "1024" ); + arguments.put( "vga", "std" ); + } + + private String configWithArgs() + { + String tempString = ""; + for ( String key : arguments.keySet() ) { + tempString += "-" + key + " " + arguments.get( key ) + " "; + } + return config.replaceAll( "", tempString ); + } + + @Override + public byte[] getFilteredDefinitionArray() + { + return configWithArgs().getBytes( StandardCharsets.UTF_8 ); + } + + @Override + public void applySettingsForLocalEdit() + { + } + + @Override + public boolean addHddTemplate( File diskImage, String hddMode, String redoDir ) + { + String tempS = config.replaceAll( "", diskImage.getAbsolutePath() ); + config = tempS; + hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, diskImage.getAbsolutePath() ) ); + return true; + } + + @Override + public boolean addHddTemplate( String diskImagePath, String hddMode, String redoDir ) + { + String tempS = config.replaceAll( "", diskImagePath ); + config = tempS; + hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, diskImagePath ) ); + return true; + } + + @Override + public boolean addDefaultNat() + { + return true; + } + + @Override + public void setOs( String vendorOsId ) + { + // TODO Auto-generated method stub + + } + + @Override + public boolean addDisplayName( String name ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean addRam( int mem ) + { + this.arguments.put( "m", Integer.toString( mem ) ); + return true; + } + + @Override + public void addFloppy( int index, String image, boolean readOnly ) + { + // TODO Auto-generated method stub + + } + + @Override + public boolean addCdrom( String image ) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean addCpuCoreCount( int nrOfCores ) + { + this.arguments.put( "smp", Integer.toString( nrOfCores ) ); + return true; + } + + @Override + public void setSoundCard( VmMetaData.SoundCardType type ) + { + } + + @Override + public VmMetaData.SoundCardType getSoundCard() + { + return null; + } + + @Override + public void setDDAcceleration( VmMetaData.DDAcceleration type ) + { + } + + @Override + public VmMetaData.DDAcceleration getDDAcceleration() + { + return null; + } + + @Override + public void setHWVersion( VmMetaData.HWVersion type ) + { + } + + @Override + public VmMetaData.HWVersion getHWVersion() + { + return null; + } + + @Override + public void setEthernetDevType( int cardIndex, VmMetaData.EthernetDevType type ) + { + } + + @Override + public VmMetaData.EthernetDevType getEthernetDevType( int cardIndex ) + { + return null; + } + + @Override + public byte[] getDefinitionArray() + { + return configWithArgs().getBytes( StandardCharsets.UTF_8 ); + } + + @Override + public boolean addEthernet( VmMetaData.EtherType type ) + { + return false; + } + + @Override + public Virtualizer getVirtualizer() + { + return virtualizer; + } + + @Override + public void enableUsb( boolean enabled ) + { + // TODO test this properly + if ( enabled ) { + arguments.put( "usb", "" ); + } else { + arguments.remove( "usb" ); + } + } + + @Override + public boolean disableSuspend() + { + return false; + } + + @Override + public void registerVirtualHW() + { + } + +} diff --git a/src/main/java/org/openslx/util/vm/UnsupportedVirtualizerFormatException.java b/src/main/java/org/openslx/util/vm/UnsupportedVirtualizerFormatException.java new file mode 100644 index 0000000..08c9673 --- /dev/null +++ b/src/main/java/org/openslx/util/vm/UnsupportedVirtualizerFormatException.java @@ -0,0 +1,9 @@ +package org.openslx.util.vm; + +@SuppressWarnings( "serial" ) +public class UnsupportedVirtualizerFormatException extends Exception +{ + public UnsupportedVirtualizerFormatException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/org/openslx/util/vm/VboxConfig.java b/src/main/java/org/openslx/util/vm/VboxConfig.java new file mode 100644 index 0000000..99ef5be --- /dev/null +++ b/src/main/java/org/openslx/util/vm/VboxConfig.java @@ -0,0 +1,611 @@ +package org.openslx.util.vm; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.apache.log4j.Logger; +import org.openslx.util.vm.VmMetaData.DriveBusType; +import org.openslx.util.vm.VmMetaData.HardDisk; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Class representing a .vbox machine description file + */ +public class VboxConfig +{ + private static final Logger LOGGER = Logger.getLogger( VboxConfig.class ); + XPath xPath = XPathFactory.newInstance().newXPath(); + + private Document doc = null; + private String displayNameExpression = "/VirtualBox/Machine/@name"; + private String displayName = new String(); + + private String osTypeExpression = "/VirtualBox/Machine/@OSType"; + private String osName = new String(); + + private String hddsExpression = "/VirtualBox/Machine/MediaRegistry/HardDisks/*"; + private ArrayList hddsArray = new ArrayList(); + + // a black list of sorts of tags that need to be removed from the .vbox file + private static String[] blackList = { "SharedFolders", "HID", "USB", "ExtraData", "Adapter", "GuestProperties", "LPT", "StorageController", "FloppyImages", "DVDImages", + "AttachedDevice" }; + + public static enum PlaceHolder + { + FLOPPYUUID( "%OpenSLX_FloppyUUID%" ), FLOPPYLOCATION( "%OpenSLX_Floppy_Location%" ), CPU( "%OpenSLX_CPU%" ), MEMORY( "%OpenSLX_MEMORY%" ), MACHINEUUID( + "%OpenSLX_MUUID%" ), NETWORKMAC( "%OpenSLX_Networkcard_MACAddress%" ), HDDLOCATION( "%OpenSLX_HDD_Location%" ), HDDUUID( "%OpenSLX_HDDUUID_" ); + private final String holderName; + + private PlaceHolder( String name ) + { + this.holderName = name; + } + + public String holderName() + { + return holderName; + } + } + + /** + * constructor with input xml file + * used to set the doc variable of this class when creating vm + * + * @param file as File - the input xml File + * @throws IOException + * @throws UnsupportedVirtualizerFormatException + */ + public VboxConfig( File file ) throws IOException, UnsupportedVirtualizerFormatException + { + try { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + + doc = dBuilder.parse( file ); + + // TODO - does this test suffice?? + if ( !doc.getDocumentElement().getNodeName().equals( "VirtualBox" ) ) { + throw new UnsupportedVirtualizerFormatException( "No root node." ); + } + + } catch ( ParserConfigurationException | SAXException | IOException e ) { + LOGGER.warn( "Could not parse .Vbox", e ); + throw new UnsupportedVirtualizerFormatException( "Could not create VBoxConfig!" ); + } + } + + /** + * constructor with input string from server + * used to set the doc variable of this class when rebuilding the doc + * + * @param filtered as String - sent from server + * @param length + * @throws IOException + */ + public VboxConfig( byte[] filtered, int length ) throws IOException + { + try { + String filteredString = new String( filtered ); + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + // TODO following two settings should handle the formatting of the xml + // but did not work correctly according to Victor... to test. + //dbFactory.setValidating( true ); + //dbFactory.setIgnoringElementContentWhitespace( true ); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + InputSource is = new InputSource( new StringReader( filteredString ) ); + + doc = dBuilder.parse( is ); + + } catch ( ParserConfigurationException | SAXException e ) { + + LOGGER.warn( "Could not recreate the dom", e ); + } + } + + /** + * getter for the xmldoc + * + * @return definition document + */ + public Document getConfigDoc() + { + return doc; + } + + /** + * initialization function + * reads the doc, sets Machine name, os type, sets the hdds, adds placeholders, removes unwanted/ + * unneeded nodes + */ + public void init() + { + if ( doc.getChildNodes().item( 0 ).getNodeType() == 8 ) { + doc.removeChild( doc.getChildNodes().item( 0 ) ); + } + try { + setMachineName(); + ensureHardwareUuid(); + setOsType(); + if ( checkForPlaceholders() ) { + return; + } + setHdds(); + removeBlackListedTags(); + addPlaceHolders(); + } catch ( XPathExpressionException e ) { + LOGGER.debug( "Could not initialize VBoxConfig", e ); + return; + } + } + + private void ensureHardwareUuid() throws XPathExpressionException + { + NodeList hwNodes = findNodes( "Hardware" ); + int count = hwNodes.getLength(); + // we will need the machine uuid, so get it + String machineUuid = xPath.compile( "/VirtualBox/Machine/@uuid" ).evaluate( this.doc ); + if ( machineUuid.isEmpty() ) { + LOGGER.error( "Machine UUID empty, should never happen!" ); + return; + } + // now check if we had a node, which we always should + if ( count == 1 ) { + Element hw = (Element)hwNodes.item( 0 ); + String hwUuid = hw.getAttribute( "uuid" ); + if ( !hwUuid.isEmpty() ) { + LOGGER.info( "Found hardware uuid: " + hwUuid ); + return; + } else { + if ( !addAttributeToNode( hw, "uuid", machineUuid ) ) { + LOGGER.error( "Failed to set machine UUID '" + machineUuid + "' as hardware UUID." ); + return; + } + LOGGER.info( "Saved machine UUID as hardware UUID." ); + } + } else { + // zero or more than 1 were found, fatal either way + // HACK: hijack XPathExpressionException ... + throw new XPathExpressionException( "Zero or more than one node found, should never happen!" ); + } + } + + /** + * Function checks if the placeholders are present + * + * @return true if the placeholders are present, false otherwise + */ + private boolean checkForPlaceholders() + { + NodeList hdds = findNodes( "HardDisk" ); + for ( int i = 0; i < hdds.getLength(); i++ ) { + Element hdd = (Element)hdds.item( i ); + if ( hdd == null ) + continue; + if ( hdd.getAttribute( "location" ).equals( PlaceHolder.HDDLOCATION.holderName() ) ) { + return true; + } + } + return false; + } + + /** + * Function finds and saves the name of the machine + * + * @throws XPathExpressionException + */ + public void setMachineName() throws XPathExpressionException + { + String name = xPath.compile( displayNameExpression ).evaluate( this.doc ); + if ( name != null && !name.isEmpty() ) { + displayName = name; + } + } + + /** + * Function finds and saves the name of the os + * + * @throws XPathExpressionException + */ + public void setOsType() throws XPathExpressionException + { + String os = xPath.compile( osTypeExpression ).evaluate( this.doc ); + if ( os != null && !os.isEmpty() ) { + osName = os; + } + } + + public void setHdds() throws XPathExpressionException + { + XPathExpression hddsExpr = xPath.compile( hddsExpression ); + Object result = hddsExpr.evaluate( this.doc, XPathConstants.NODESET ); + // take all the hdd nodes + NodeList nodes = (NodeList)result; + // foreach hdd in the hddnodes do: + for ( int i = 0; i < nodes.getLength(); i++ ) { + // have the node + // take the uuid + // look under 1 ) { + LOGGER.error( "There can not be more HDDs with the same UUID!" ); + return; + } + Element deviceElement = (Element)devicesNodes.item( 0 ); + String controllerDevice = deviceElement.getAttribute( "type" ); + String bus = deviceElement.getAttribute( "name" ); + DriveBusType busType = null; + if ( bus.equals( "IDE" ) ) { + busType = DriveBusType.IDE; + } else if ( bus.equals( "SCSI" ) ) { + busType = DriveBusType.SCSI; + } else if ( bus.equals( "SATA" ) ) { + busType = DriveBusType.SATA; + } + // add them together + hddsArray.add( new HardDisk( controllerDevice, busType, fileName ) ); + } + } + + public void addPlaceHolders() + { + // placeholder for the MACAddress + changeAttribute( "Adapter", "MACAddress", PlaceHolder.NETWORKMAC.holderName() ); + + // placeholder for the machine uuid + changeAttribute( "Machine", "uuid", PlaceHolder.MACHINEUUID.holderName() ); + + // placeholder for the location of the virtual hdd + changeAttribute( "HardDisk", "location", PlaceHolder.HDDLOCATION.holderName() ); + + // placeholder for the memory + changeAttribute( "Memory", "RAMSize", PlaceHolder.MEMORY.holderName() ); + + // placeholder for the CPU + changeAttribute( "CPU", "count", PlaceHolder.CPU.holderName() ); + + // add placeholder for the uuid of the virtual harddrive. + // must be added on 2 positions...in the HardDisk tag and the attachedDevice tag + // first find the uuid + NodeList hdds = findNodes( "HardDisk" ); + for ( int i = 0; i < hdds.getLength(); i++ ) { + Element hdd = (Element)findNodes( "HardDisk" ).item( i ); + if ( hdd == null ) + continue; + String uuid = hdd.getAttribute( "uuid" ); + hdd.setAttribute( "uuid", PlaceHolder.HDDUUID.holderName() + i + "%" ); + NodeList images = findNodes( "Image" ); + Element image; + for ( int j = 0; j < images.getLength(); j++ ) { + if ( ( (Element)images.item( j ) ).getAttribute( "uuid" ).equals( uuid ) ) { + image = (Element)images.item( j ); + image.setAttribute( "uuid", PlaceHolder.HDDUUID.holderName() + i + "%" ); + break; + } + } + } + } + + /** + * Function used to find nodes in the document + * Function returnes a NodeList of Nodes...not just a Node...even when the wanted Node is a + * single + * Node, you get a NodeList with just one element + * + * @param targetTag as String + * @return nodes as NodeList + */ + public NodeList findNodes( String targetTag ) + { + String path = ".//" + targetTag; + XPathExpression expr; + NodeList nodes = null; + try { + expr = xPath.compile( path ); + Object nodesObject = expr.evaluate( this.doc, XPathConstants.NODESET ); + nodes = (NodeList)nodesObject; + } catch ( XPathExpressionException e ) { + LOGGER.error( "Could not build path", e ); + } + return nodes; + } + + /** + * Function uses the findNodes function to narrow down the wanted node using 1 attribute and + * its value + * + * @param targetTag + * @param targetAttr0 + * @param value0 + * @return + */ + public Node findNode( String targetTag, String targetAttr0, String value0 ) + { + Node returnNode = null; + + NodeList foundNodes = findNodes( targetTag ); + + for ( int i = 0; i < foundNodes.getLength(); i++ ) { + Element node = (Element)foundNodes.item( i ); + if ( node != null && node.hasAttribute( targetAttr0 ) && node.getAttribute( targetAttr0 ).equals( value0 ) ) { + returnNode = foundNodes.item( i ); + } + } + return returnNode; + } + + /** + * Function used to change the value of an attribute + * Use this function if you know the targetNode is unique + * + * @param targetTag + * @param attribute + * @param newValue + */ + public void changeAttribute( String targetTag, String attribute, String newValue ) + { + changeAttribute( targetTag, attribute, newValue, null, null ); + } + + /** + * Function used to change the value of an attribute + * Use this function if you are not sure if the targetNode is unique + * Use refAttr and refVal to address the right node + * + * @param targetTag + * @param targetAttr + * @param newValue + * @param refAttr + * @param refVal + */ + public void changeAttribute( String targetTag, String targetAttr, String newValue, String refAttr, String refVal ) + { + NodeList nodes = findNodes( targetTag ); + + for ( int i = 0; i < nodes.getLength(); i++ ) { + Element element = (Element)nodes.item( i ); + if ( element == null ) + return; + if ( refAttr != null && refVal != null ) { + if ( element.getAttribute( refAttr ).equals( refVal ) ) { + element.setAttribute( targetAttr, newValue ); + break; + } + } else { + if ( nodes.getLength() > 1 ) { + LOGGER.error( "Action would change values of more than one node; stopped!" ); + return; + } + element.setAttribute( targetAttr, newValue ); + } + } + } + + public boolean addAttributeToNode( Node targetNode, String attrName, String value ) + { + if ( targetNode == null ) { + LOGGER.warn( "Node is null; stopped!" ); + return false; + } + try { + ( (Element)targetNode ).setAttribute( attrName, value ); + } catch ( DOMException e ) { + LOGGER.error( "Failed set '" + attrName + "' to '" + value + "' of xml node '" + targetNode.getNodeName() + "': ", e ); + return false; + } + return true; + } + + public Node addNewNode( String nameOfParent, String nameOfnewNode, boolean oneLiner ) + { + return addNewNode( nameOfParent, nameOfnewNode, oneLiner, null, null ); + } + + public Node addNewNode( String nameOfParent, String nameOfnewNode, boolean oneLiner, String refAttr, String refVal ) + { + Node parent = null; + NodeList posibleParents = findNodes( nameOfParent ); + Element newNode; + try { + if ( posibleParents.getLength() > 1 ) { + // if we have more then 1 parent we need to have an sanityArg s.t. we insert our new attribute in the right tag + if ( refAttr == null ) { + LOGGER.warn( "Action would change values of more than one node; stopped!" ); + return null; + } + for ( int i = 1; i < posibleParents.getLength(); i++ ) { + if ( ( (Element)posibleParents.item( i ) ).getAttribute( refAttr ).equals( refVal ) ) { + parent = posibleParents.item( i ); + break; + } + } + } else { + parent = posibleParents.item( 0 ); + } + + if ( parent == null ) { + LOGGER.warn( "Node: '" + nameOfParent + "' could not be found" ); + return null; + } + newNode = doc.createElement( nameOfnewNode ); + + if ( !oneLiner ) { + org.w3c.dom.Text a = doc.createTextNode( "\n" ); + newNode.appendChild( a ); + } + parent.appendChild( newNode ); + } catch ( DOMException e ) { + LOGGER.error( "Something went wrong: ", e ); + return null; + } + + return newNode; + } + + /** + * USB will be enabled + */ + public void enableUsb() + { + addNewNode( "Hardware", "USB", false ); + addNewNode( "USB", "Controllers", false ); + // OHCI for USB 1.0 + Node controller1 = addNewNode( "Controllers", "Controller", true ); + addAttributeToNode( controller1, "name", "OHCI" ); + addAttributeToNode( controller1, "type", "OHCI" ); + // EHCI for USB 2.0 + Node controller2 = addNewNode( "Controllers", "Controller", true ); + addAttributeToNode( controller2, "name", "EHCI" ); + addAttributeToNode( controller2, "type", "EHCI" ); + } + + /** + * Disable usb by removing the USB tag + */ + public void disableUsb() + { + NodeList usbList = findNodes( "USB" ); + removeNode( usbList.item( 0 ) ); + } + + // function removes a given child and the format childNode + private void removeNode( Node node ) + { + if ( node == null ) { + LOGGER.warn( "node is null; unsafe" ); + return; + } + Node parent = node.getParentNode(); + // this node here is usually a type3 Node used only for the formating of the vbox file + Node previousSibling = node.getPreviousSibling(); + + parent.removeChild( node ); + + // HACK remove empty lines + // format children (\n or \t) have type 3 + if ( previousSibling.getNodeType() == 3 ) { + // the value of these Nodes are characters + String tmp = previousSibling.getNodeValue(); + boolean shouldDelete = true; + for ( int i = 0; i < tmp.length(); i++ ) { + if ( !Character.isWhitespace( tmp.charAt( i ) ) ) { + shouldDelete = false; + break; + } + } + if ( shouldDelete ) + parent.removeChild( previousSibling ); + } + } + + // cleanup part here + private void removeBlackListedTags() throws XPathExpressionException + { + // iterate over the blackList + for ( String blackedTag : blackList ) { + String blackedExpression = ".//" + blackedTag; + XPathExpression blackedExpr = xPath.compile( blackedExpression ); + + NodeList blackedNodes = (NodeList)blackedExpr.evaluate( this.doc, XPathConstants.NODESET ); + for ( int i = 0; i < blackedNodes.getLength(); i++ ) { + // get the child node + Element child = (Element)blackedNodes.item( i ); + // remove child + if ( child.getTagName().equals( "Adapter" ) && child.getAttribute( "enabled" ).equals( "true" ) ) { + // we need to remove the children of the active network adapter + // these are the mode of network connection and disabled modes...they go together -> see wiki + Node firstChild = child.getChildNodes().item( 0 ); + Node secondChild = child.getChildNodes().item( 1 ); + if ( firstChild != null && secondChild != null ) { + if ( firstChild.getNodeName().equals( "#text" ) && !secondChild.getNodeName().equals( "#text" ) ) { + removeNode( child.getChildNodes().item( 1 ) ); + } + } + LOGGER.warn( "possible problem while removing formating node" ); + continue; + } + + if ( ( child.getTagName().equals( "StorageController" ) && !child.getAttribute( "name" ).equals( "Floppy" ) ) + || ( child.getTagName().equals( "AttachedDevice" ) && child.getAttribute( "type" ).equals( "HardDisk" ) ) ) { + continue; + } + // the structure of the xml Document is achieved through children nodes that are made up of just /n and spaces + // when a child node is deleted we get an empty line there the old child node used to be + removeNode( child ); + } + } + } + + private String givePathToStorageController( String uuid ) + { + // StorageController is the parent of the parent of node with given uuid + return "//Image[contains(@uuid, \'" + uuid + "\')]/../.."; + } + + public String getDisplayName() + { + return displayName; + } + + public String getOsName() + { + return osName; + } + + public ArrayList getHdds() + { + return hddsArray; + } + + public String toString() + { + try { + StringWriter sw = new StringWriter(); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "no" ); + transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); + transformer.setOutputProperty( OutputKeys.INDENT, "yes" ); + transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" ); + + transformer.transform( new DOMSource( doc ), new StreamResult( sw ) ); + return sw.toString(); + } catch ( Exception ex ) { + throw new RuntimeException( "Error converting to String", ex ); + } + } +} diff --git a/src/main/java/org/openslx/util/vm/VboxMetaData.java b/src/main/java/org/openslx/util/vm/VboxMetaData.java new file mode 100644 index 0000000..81ffc5b --- /dev/null +++ b/src/main/java/org/openslx/util/vm/VboxMetaData.java @@ -0,0 +1,436 @@ +package org.openslx.util.vm; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.OperatingSystem; +import org.openslx.bwlp.thrift.iface.Virtualizer; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +class VBoxSoundCardMeta +{ + public final boolean isPresent; + public final String value; + + public VBoxSoundCardMeta( boolean present, String val ) + { + isPresent = present; + value = val; + } +} + +class VBoxDDAccelMeta +{ + public final boolean isPresent; + + public VBoxDDAccelMeta( boolean present ) + { + isPresent = present; + } +} + +class VBoxHWVersionMeta +{ + public final int version; + + public VBoxHWVersionMeta( int vers ) + { + version = vers; + } +} + +class VBoxEthernetDevTypeMeta +{ + public final String value; + public final boolean isPresent; + + public VBoxEthernetDevTypeMeta( boolean present, String val ) + { + value = val; + isPresent = present; + } +} + +public class VboxMetaData extends VmMetaData +{ + private static final Logger LOGGER = Logger.getLogger( VboxMetaData.class ); + + private static final Virtualizer virtualizer = new Virtualizer( "virtualbox", "VirtualBox" ); + + private final VboxConfig config; + + public static enum EthernetType + { + NAT( "vboxnet1" ), BRIDGED( "vboxnet0" ), HOST_ONLY( "vboxnet2" ); + + public final String vnet; + + private EthernetType( String vnet ) + { + this.vnet = vnet; + } + } + + public VboxMetaData( List osList, File file ) throws IOException, UnsupportedVirtualizerFormatException + { + super( osList ); + this.config = new VboxConfig( file ); + init(); + } + + public VboxMetaData( List osList, byte[] vmContent, int length ) throws IOException, UnsupportedVirtualizerFormatException + { + super( osList ); + this.config = new VboxConfig( vmContent, length ); + init(); + } + + private void init() + { + registerVirtualHW(); + + this.config.init(); + displayName = config.getDisplayName(); + setOs( "virtualbox", config.getOsName() ); + + for ( HardDisk hardDisk : config.getHdds() ) { + hdds.add( hardDisk ); + } + } + + @Override + public Virtualizer getVirtualizer() + { + return virtualizer; + } + + @Override + public void enableUsb( boolean enabled ) + { + if ( !enabled ) { + config.disableUsb(); + } else { + config.enableUsb(); + } + } + + @Override + public void applySettingsForLocalEdit() + { + // TODO Auto-generated method stub + } + + @Override + public byte[] getFilteredDefinitionArray() + { + return config.toString().getBytes( StandardCharsets.UTF_8 ); + } + + @Override + public boolean addHddTemplate( String diskImage, String hddMode, String redoDir ) + { + config.changeAttribute( "HardDisk", "location", diskImage ); + config.changeAttribute( "Machine", "snapshotFolder", redoDir ); + return true; + } + + @Override + public boolean addHddTemplate( File diskImage, String hddMode, String redoDir ) + { + String diskImagePath = diskImage.getName(); + config.changeAttribute( "HardDisk", "location", diskImagePath ); + + UUID newhdduuid = UUID.randomUUID(); + + // patching the new uuid in the vbox config file here + String vboxUUid = "{" + newhdduuid.toString() + "}"; + config.changeAttribute( "HardDisk", "uuid", vboxUUid ); + config.changeAttribute( "Image", "uuid", vboxUUid ); + + // 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() + "}"; + config.changeAttribute( "Machine", "uuid", machineUUid ); + return true; + } + + @Override + public boolean addDefaultNat() + { + config.addNewNode( "Adapter", "NAT", true ); + config.changeAttribute( "Adapter", "MACAddress", "080027B86D12" ); + return true; + } + + @Override + public void setOs( String vendorOsId ) + { + config.changeAttribute( "Machine", "OSType", vendorOsId ); + setOs( "virtualbox", vendorOsId ); + } + + @Override + public boolean addDisplayName( String name ) + { + config.changeAttribute( "Machine", "name", name ); + return true; + } + + @Override + public boolean addRam( int mem ) + { + config.changeAttribute( "Memory", "RAMSize", Integer.toString( mem ) ); + return true; + } + + @Override + public void addFloppy( int index, String image, boolean readOnly ) + { + + Node somenode = config.findNode( "StorageController", "name", "Floppy" ); + if ( somenode == null ) { + Element controller = (Element)config.addNewNode( "StorageControllers", "StorageController", false ); + controller.setAttribute( "name", "Floppy" ); + controller.setAttribute( "type", "I82078" ); + controller.setAttribute( "PortCount", "1" ); + controller.setAttribute( "useHostIOCache", "true" ); + controller.setAttribute( "Bootable", "true" ); + } + + Element attachedDev = null; + + if ( image == null ) { + attachedDev = (Element)config.addNewNode( "StorageController", "AttachedDevice", true, "name", "Floppy" ); + LOGGER.warn( "Floppy controller has no image attached" ); + } else { + attachedDev = (Element)config.addNewNode( "StorageController", "AttachedDevice", false, "name", "Floppy" ); + + Element imageTag = (Element)config.addNewNode( "AttachedDevice", "Image", true, "type", "Floppy" ); + imageTag.setAttribute( "uuid", VboxConfig.PlaceHolder.FLOPPYUUID.holderName() ); + config.addNewNode( "MediaRegistry", "FloppyImages", false ); + Element floppyImageTag = (Element)config.addNewNode( "FloppyImages", "Image", true ); + floppyImageTag.setAttribute( "uuid", VboxConfig.PlaceHolder.FLOPPYUUID.holderName() ); + floppyImageTag.setAttribute( "location", VboxConfig.PlaceHolder.FLOPPYLOCATION.holderName() ); + } + + attachedDev.setAttribute( "type", "Floppy" ); + attachedDev.setAttribute( "hotpluggable", "false" ); + attachedDev.setAttribute( "port", "0" ); + attachedDev.setAttribute( "device", Integer.toString( index ) ); + } + + @Override + public boolean addCdrom( String image ) + { + return false; + } + + @Override + public boolean addCpuCoreCount( int nrOfCores ) + { + config.changeAttribute( "CPU", "count", Integer.toString( nrOfCores ) ); + return true; + } + + @Override + public void setSoundCard( org.openslx.util.vm.VmMetaData.SoundCardType type ) + { + VBoxSoundCardMeta sound = soundCards.get( type ); + + config.changeAttribute( "AudioAdapter", "enabled", Boolean.toString( sound.isPresent ) ); + config.changeAttribute( "AudioAdapter", "controller", sound.value ); + } + + @Override + public VmMetaData.SoundCardType getSoundCard() + { + // initialize here to type None to avoid all null pointer exceptions thrown for unknown user written sound cards + VmMetaData.SoundCardType returnsct = VmMetaData.SoundCardType.NONE; + Element x = (Element)config.findNodes( "AudioAdapter" ).item( 0 ); + if ( !x.hasAttribute( "enabled" ) || ( x.hasAttribute( "enabled" ) && x.getAttribute( "enabled" ).equals( "false" ) ) ) { + return returnsct; + } else { + // extra separate case for the non-existing argument} + if ( !x.hasAttribute( "controller" ) ) { + returnsct = VmMetaData.SoundCardType.AC; + } else { + String controller = x.getAttribute( "controller" ); + VBoxSoundCardMeta soundMeta = null; + for ( VmMetaData.SoundCardType type : VmMetaData.SoundCardType.values() ) { + soundMeta = soundCards.get( type ); + if ( soundMeta != null ) { + if ( controller.equals( soundMeta.value ) ) { + returnsct = type; + } + } + } + } + } + return returnsct; + } + + @Override + public void setDDAcceleration( VmMetaData.DDAcceleration type ) + { + VBoxDDAccelMeta accel = ddacc.get( type ); + config.changeAttribute( "Display", "accelerate3D", Boolean.toString( accel.isPresent ) ); + } + + @Override + public VmMetaData.DDAcceleration getDDAcceleration() + { + VmMetaData.DDAcceleration returndda = null; + Element x = (Element)config.findNodes( "Display" ).item( 0 ); + if ( x.hasAttribute( "accelerate3D" ) ) { + if ( x.getAttribute( "accelerate3D" ).equals( "true" ) ) { + returndda = VmMetaData.DDAcceleration.ON; + } else { + returndda = VmMetaData.DDAcceleration.OFF; + } + } else { + returndda = VmMetaData.DDAcceleration.OFF; + } + return returndda; + } + + @Override + /** + * Function does nothing for Virtual Box; + * Virtual Box accepts per default only one hardware version and is hidden from the user + */ + public void setHWVersion( HWVersion type ) + { + } + + @Override + public VmMetaData.HWVersion getHWVersion() + { + VmMetaData.HWVersion returnhwv = null; + // Virtual Box uses only one virtual hardware version and can't be changed + returnhwv = VmMetaData.HWVersion.DEFAULT; + return returnhwv; + } + + @Override + public void setEthernetDevType( int cardIndex, EthernetDevType type ) + { + String index = "0"; + VBoxEthernetDevTypeMeta networkc = networkCards.get( type ); + // cardIndex is not used yet...maybe later needed for different network cards + config.changeAttribute( "Adapter", "enabled", Boolean.toString( networkc.isPresent ), "slot", index ); + config.changeAttribute( "Adapter", "type", networkc.value, "slot", index ); + } + + @Override + public VmMetaData.EthernetDevType getEthernetDevType( int cardIndex ) + { + VmMetaData.EthernetDevType returnedt = VmMetaData.EthernetDevType.NONE; + Element x = (Element)config.findNodes( "Adapter" ).item( 0 ); + if ( !x.hasAttribute( "enabled" ) || ( x.hasAttribute( "enabled" ) && x.getAttribute( "enabled" ).equals( "false" ) ) ) { + return returnedt; + } else { + // extra separate case for the non-existing argument} + if ( !x.hasAttribute( "type" ) ) { + returnedt = VmMetaData.EthernetDevType.PCNETFAST3; + } else { + String temp = x.getAttribute( "type" ); + VBoxEthernetDevTypeMeta etherMeta = null; + for ( VmMetaData.EthernetDevType type : VmMetaData.EthernetDevType.values() ) { + etherMeta = networkCards.get( type ); + if ( etherMeta != null ) { + if ( temp.equals( etherMeta.value ) ) { + returnedt = type; + } + } + } + } + } + return returnedt; + } + + @Override + public byte[] getDefinitionArray() + { + return config.toString().getBytes( StandardCharsets.UTF_8 ); + } + + 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 + 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" ) ); + soundCards.put( VmMetaData.SoundCardType.AC, new VBoxSoundCardMeta( true, "AC97" ) ); + + ddacc.put( VmMetaData.DDAcceleration.OFF, new VBoxDDAccelMeta( false ) ); + ddacc.put( VmMetaData.DDAcceleration.ON, new VBoxDDAccelMeta( true ) ); + + hwversion.put( VmMetaData.HWVersion.DEFAULT, new VBoxHWVersionMeta( 0 ) ); + + // 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 + networkCards.put( VmMetaData.EthernetDevType.NONE, new VBoxEthernetDevTypeMeta( false, "Am79C970A" ) ); + networkCards.put( VmMetaData.EthernetDevType.PCNETPCI2, new VBoxEthernetDevTypeMeta( true, "Am79C970A" ) ); + networkCards.put( VmMetaData.EthernetDevType.PCNETFAST3, new VBoxEthernetDevTypeMeta( true, "Am79C973" ) ); + networkCards.put( VmMetaData.EthernetDevType.PRO1000MTD, new VBoxEthernetDevTypeMeta( true, "82540EM" ) ); + 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" ) ); + } + + @Override + public boolean addEthernet( VmMetaData.EtherType type ) + { + Node hostOnlyInterfaceNode = config.addNewNode( "Adapter", "HostOnlyInterface", true, "slot", "0" ); + 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 boolean disableSuspend() + { + // TODO how?? + // short answer is: you can't + // https://forums.virtualbox.org/viewtopic.php?f=6&t=77169 + // https://forums.virtualbox.org/viewtopic.php?f=8&t=80338 + return true; + } +} diff --git a/src/main/java/org/openslx/util/vm/VmMetaData.java b/src/main/java/org/openslx/util/vm/VmMetaData.java index 75e559a..c3bbb38 100644 --- a/src/main/java/org/openslx/util/vm/VmMetaData.java +++ b/src/main/java/org/openslx/util/vm/VmMetaData.java @@ -1,30 +1,103 @@ package org.openslx.util.vm; +import java.io.File; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import org.apache.log4j.Logger; import org.openslx.bwlp.thrift.iface.OperatingSystem; import org.openslx.bwlp.thrift.iface.Virtualizer; /** - * Describes a configured virtual machine. This class is parsed from a machine description, like a - * *.vmx for VMware machines. + * Describes a configured virtual machine. This class is parsed from a machine + * description, like a *.vmx for VMware machines. */ -public abstract class VmMetaData +public abstract class VmMetaData { + private static final Logger LOGGER = Logger.getLogger( VmMetaData.class ); /* * Helper types */ + protected Map soundCards = new HashMap<>(); + protected Map ddacc = new HashMap<>(); + protected Map hwversion = new HashMap<>(); + protected Map 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" ), NONE( "No Network Card" ); + + public final String displayName; + + private EthernetDevType( String dName ) + { + this.displayName = dName; + } + } public static enum DriveBusType { - SCSI, - IDE, - SATA; + SCSI, IDE, SATA; } public static class HardDisk @@ -41,6 +114,10 @@ public abstract class VmMetaData } } + public static enum EtherType + { + NAT, BRIDGED, HOST_ONLY; + } /* * Members */ @@ -54,8 +131,35 @@ public abstract class VmMetaData protected String displayName = null; /* - * Guettas + * Getters for virtual hardware */ + public List getSupportedSoundCards() + { + ArrayList availables = new ArrayList( soundCards.keySet() ); + Collections.sort( availables ); + return availables; + } + + public List getSupportedDDAccs() + { + ArrayList availables = new ArrayList( ddacc.keySet() ); + Collections.sort( availables ); + return availables; + } + + public List getSupportedHWVersions() + { + ArrayList availables = new ArrayList( hwversion.keySet() ); + Collections.sort( availables ); + return availables; + } + + public List getSupportedEthernetDevices() + { + ArrayList availables = new ArrayList( networkCards.keySet() ); + Collections.sort( availables ); + return availables; + } /** * Get operating system of this VM. @@ -82,10 +186,11 @@ public abstract class VmMetaData } /** - * This method should return a minimal representation of the input meta data. The representation - * is platform dependent, and should be stripped of all non-essential configuration, such as - * CD/DVD/FLoppy drives, serial or parallel ports, shared folders, or anything else that could be - * considered sensible information (absolute paths containing the local user's name). + * This method should return a minimal representation of the input meta data. + * The representation is platform dependent, and should be stripped of all + * non-essential configuration, such as CD/DVD/FLoppy drives, serial or parallel + * ports, shared folders, or anything else that could be considered sensible + * information (absolute paths containing the local user's name). */ public abstract byte[] getFilteredDefinitionArray(); @@ -104,12 +209,14 @@ public abstract class VmMetaData } /** - * Called from subclass to set the OS. If the OS cannot be determined from the given parameters, - * it will not be set. + * 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 + * @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 ) { @@ -131,14 +238,130 @@ public abstract class VmMetaData this.os = lazyMatch; } - public abstract Virtualizer getVirtualizer(); - - public abstract void enableUsb(boolean enabled); - /** - * Apply config options that are desired when locally editing a VM. - * for vmware, this disables automatic DPI scaling of the guest. + * 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 + * @throws IOException + */ + public static VmMetaData getInstance( List osList, File file ) throws IOException + { + Exception errEx = null; + try { + return new VmwareMetaData( osList, file ); + } catch ( UnsupportedVirtualizerFormatException e ) { + LOGGER.debug( "Disk file not .vmdk" ); + errEx = e; + } + try { + return new VboxMetaData( osList, file ); + } catch ( UnsupportedVirtualizerFormatException e ) { + LOGGER.debug( "Disk file not .vdi" ); + errEx = e; + } + try { + return new QemuMetaData( osList, file ); + } catch ( Exception e ) { + LOGGER.debug( "Disk file not qemu supported format" ); + errEx = e; + } + if ( errEx != null ) { + LOGGER.error( "Unsupported disk file format!", errEx ); + } + 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 osList, byte[] vmContent, int length ) throws IOException + { + Exception errEx = null; + try { + return new VmwareMetaData( osList, vmContent, length ); + } catch ( UnsupportedVirtualizerFormatException e ) { + LOGGER.debug( "Machine description not in .vmx format." ); + errEx = e; + } + try { + return new VboxMetaData( osList, vmContent, length ); + } catch ( UnsupportedVirtualizerFormatException | NullPointerException e ) { + LOGGER.debug( "Machine description not in .vbox format." ); + errEx = e; + } + // how to do this check ?? + try { + return new QemuMetaData( osList, vmContent ); + } catch ( Exception e ) { + LOGGER.debug( "Machine description not in qemu supported format" ); + errEx = e; + } + if ( errEx != null ) { + LOGGER.error( "Machine description has an unknown format!", errEx ); + } + 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(); } diff --git a/src/main/java/org/openslx/util/vm/VmwareConfig.java b/src/main/java/org/openslx/util/vm/VmwareConfig.java index c0e30f6..ac13e4f 100644 --- a/src/main/java/org/openslx/util/vm/VmwareConfig.java +++ b/src/main/java/org/openslx/util/vm/VmwareConfig.java @@ -31,7 +31,7 @@ public class VmwareConfig // (void) } - public VmwareConfig( File file ) throws IOException + public VmwareConfig( File file ) throws IOException, UnsupportedVirtualizerFormatException { int todo = (int)Math.min( 100000, file.length() ); int offset = 0; @@ -53,7 +53,7 @@ public class VmwareConfig } - public VmwareConfig( InputStream is ) throws IOException + public VmwareConfig( InputStream is ) throws IOException, UnsupportedVirtualizerFormatException { int todo = Math.max( 4000, Math.min( 100000, is.available() ) ); int offset = 0; @@ -68,34 +68,44 @@ public class VmwareConfig init( data, offset ); } - public VmwareConfig( byte[] vmxContent, int length ) + public VmwareConfig( byte[] vmxContent, int length ) throws UnsupportedVirtualizerFormatException { init( vmxContent, length ); } - private void init( byte[] vmxContent, int length ) + // function is used for both .vmx and .vmdk files + private void init( byte[] vmxContent, int length ) throws UnsupportedVirtualizerFormatException { try { + boolean isValid = false; BufferedReader reader = getVmxReader( vmxContent, length ); String line; while ( ( line = reader.readLine() ) != null ) { KeyValuePair entry = parse( line ); + if ( entry != null ) { + if ( entry.key.equals( "virtualHW.version" ) || entry.key.equals( "ddb.virtualHWVersion" ) ) { + isValid = true; + } set( entry.key, unescape( entry.value ) ); } } + if ( !isValid ) { + throw new UnsupportedVirtualizerFormatException( "Not in VMX format." ); + } } catch ( IOException e ) { LOGGER.warn( "Exception when loading vmx from byte array (how!?)", e ); } } - public static BufferedReader getVmxReader( byte[] vmxContent, int length ) throws IOException { - Charset cs = getCharset(vmxContent, length); + public static BufferedReader getVmxReader( byte[] vmxContent, int length ) throws IOException + { + Charset cs = getCharset( vmxContent, length ); return new BufferedReader( new InputStreamReader( new ByteArrayInputStream( vmxContent, 0, length ), cs ) ); - } - public static Charset getCharset( byte[] vmxContent, int length ) { + public static Charset getCharset( byte[] vmxContent, int length ) + { String csName = detectCharset( new ByteArrayInputStream( vmxContent, 0, length ) ); Charset cs = null; try { @@ -145,10 +155,8 @@ public class VmwareConfig return entries.entrySet(); } - private static final Pattern settingMatcher1 = Pattern.compile( "^\\s*(#?[a-z0-9\\.\\:_]+)\\s*=\\s*\"(.*)\"\\s*$", - Pattern.CASE_INSENSITIVE ); - private static final Pattern settingMatcher2 = Pattern.compile( "^\\s*(#?[a-z0-9\\.\\:_]+)\\s*=\\s*([^\"]*)\\s*$", - Pattern.CASE_INSENSITIVE ); + private static final Pattern settingMatcher1 = Pattern.compile( "^\\s*(#?[a-z0-9\\.\\:_]+)\\s*=\\s*\"(.*)\"\\s*$", Pattern.CASE_INSENSITIVE ); + private static final Pattern settingMatcher2 = Pattern.compile( "^\\s*(#?[a-z0-9\\.\\:_]+)\\s*=\\s*([^\"]*)\\s*$", Pattern.CASE_INSENSITIVE ); private static KeyValuePair parse( String line ) { @@ -159,8 +167,7 @@ public class VmwareConfig if ( !matcher.matches() ) { return null; } - return new KeyValuePair( - matcher.group( 1 ), matcher.group( 2 ) ); + return new KeyValuePair( matcher.group( 1 ), matcher.group( 2 ) ); } @@ -202,8 +209,7 @@ public class VmwareConfig StringBuilder sb = new StringBuilder( 300 ); for ( Entry entry : entries.entrySet() ) { ConfigEntry value = entry.getValue(); - if ( ( !filteredRequired || value.forFiltered ) && - ( !generatedRequired || value.forGenerated ) ) { + if ( ( !filteredRequired || value.forFiltered ) && ( !generatedRequired || value.forGenerated ) ) { sb.append( entry.getKey() ); sb.append( " = \"" ); sb.append( value.getEscaped() ); @@ -263,7 +269,5 @@ public class VmwareConfig { this.value = value; } - } - } diff --git a/src/main/java/org/openslx/util/vm/VmwareMetaData.java b/src/main/java/org/openslx/util/vm/VmwareMetaData.java index 8b4fa0f..c8e4716 100644 --- a/src/main/java/org/openslx/util/vm/VmwareMetaData.java +++ b/src/main/java/org/openslx/util/vm/VmwareMetaData.java @@ -16,7 +16,49 @@ import org.openslx.bwlp.thrift.iface.Virtualizer; import org.openslx.util.Util; import org.openslx.util.vm.VmwareConfig.ConfigEntry; -public class VmwareMetaData extends VmMetaData +class VmWareSoundCardMeta +{ + public final boolean isPresent; + public final String value; + + public VmWareSoundCardMeta( boolean present, String val ) + { + isPresent = present; + value = val; + } +} + +class VmWareDDAccelMeta +{ + public final boolean isPresent; + + public VmWareDDAccelMeta( boolean present ) + { + isPresent = present; + } +} + +class VmWareHWVersionMeta +{ + public final int version; + + public VmWareHWVersionMeta( int vers ) + { + version = vers; + } +} + +class VmWareEthernetDevTypeMeta +{ + public final String value; + + public VmWareEthernetDevTypeMeta( String val ) + { + value = val; + } +} + +public class VmwareMetaData extends VmMetaData { private static final Logger LOGGER = Logger.getLogger( VmwareMetaData.class ); @@ -40,24 +82,38 @@ public class VmwareMetaData extends VmMetaData } } + 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 disks = new HashMap<>(); - public VmwareMetaData( List osList, File file ) throws IOException + public VmwareMetaData( List osList, File file ) throws IOException, UnsupportedVirtualizerFormatException { super( osList ); this.config = new VmwareConfig( file ); init(); } - public VmwareMetaData( List osList, byte[] vmxContent, int length ) + public VmwareMetaData( List osList, byte[] vmxContent, int length ) throws UnsupportedVirtualizerFormatException { super( osList ); - this.config = new VmwareConfig( vmxContent, length ); // still unfiltered + this.config = new VmwareConfig( vmxContent, length ); // still unfiltered init(); // now filtered } private void init() { + registerVirtualHW(); + for ( Entry entry : config.entrySet() ) { handleLoadEntry( entry ); } @@ -168,8 +224,19 @@ public class VmwareMetaData extends VmMetaData } } + @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" ) ); @@ -218,14 +285,29 @@ public class VmwareMetaData extends VmMetaData return true; } - public boolean addEthernet( EthernetType type ) + public boolean addEthernet( VmMetaData.EtherType type ) { + boolean ret = false; int index = 0; for ( ;; ++index ) { if ( config.get( "ethernet" + index + ".present" ) == null ) break; } - return addEthernet( index, type ); + 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 ) @@ -292,18 +374,21 @@ public class VmwareMetaData extends VmMetaData 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 ) ); @@ -359,20 +444,6 @@ public class VmwareMetaData extends VmMetaData } } - public static enum EthernetType - { - NAT( "vmnet1" ), - BRIDGED( "vmnet0" ), - HOST_ONLY( "vmnet2" ); - - public final String vmnet; - - private EthernetType( String vnet ) - { - this.vmnet = vnet; - } - } - @Override public void enableUsb( boolean enabled ) { @@ -391,147 +462,79 @@ public class VmwareMetaData extends VmMetaData return config.get( key ); } - // SOUND - public static enum SoundCardType - { - NONE( false, null, "None" ), - DEFAULT( true, null, "(default)" ), - SOUND_BLASTER( true, "sb16", "Sound Blaster 16" ), - ES( true, "es1371", "ES 1371" ), - HD_AUDIO( true, "hdaudio", "Intel Integrated HD Audio" ); - - public final boolean isPresent; - public final String value; - public final String displayName; - - private SoundCardType( boolean present, String value, String dName ) - { - this.isPresent = present; - this.value = value; - this.displayName = dName; - } - } - - public void setSoundCard( SoundCardType type ) + public void setSoundCard( VmMetaData.SoundCardType type ) { - addFiltered( "sound.present", vmBoolean( type.isPresent ) ); - if ( type.value != null ) { - addFiltered( "sound.virtualDev", type.value ); + 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 SoundCardType getSoundCard() + public VmMetaData.SoundCardType getSoundCard() { if ( !isSetAndTrue( "sound.present" ) || !isSetAndTrue( "sound.autodetect" ) ) { - return SoundCardType.NONE; + return VmMetaData.SoundCardType.NONE; } String current = config.get( "sound.virtualDev" ); if ( current != null ) { - for ( SoundCardType type : SoundCardType.values() ) { - if ( current.equals( type.value ) ) { - return type; + 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 SoundCardType.DEFAULT; - } - - // 3DAcceleration - public static enum DDAcceleration - { - OFF( false, "Off" ), - ON( true, "On" ); - - public final boolean isPresent; - public final String displayName; - - private DDAcceleration( boolean present, String dName ) - { - this.isPresent = present; - this.displayName = dName; - } + return VmMetaData.SoundCardType.DEFAULT; } - public void setDDAcceleration( DDAcceleration type ) + public void setDDAcceleration( VmMetaData.DDAcceleration type ) { - addFiltered( "mks.enable3d", vmBoolean( type.isPresent ) ); + VmWareDDAccelMeta ddaMeta = ddacc.get( type ); + addFiltered( "mks.enable3d", vmBoolean( ddaMeta.isPresent ) ); } - public DDAcceleration getDDAcceleration() + public VmMetaData.DDAcceleration getDDAcceleration() { if ( isSetAndTrue( "mks.enable3d" ) ) { - return DDAcceleration.ON; + return VmMetaData.DDAcceleration.ON; } else { - return DDAcceleration.OFF; + return VmMetaData.DDAcceleration.OFF; } } - // Virtual hardware version - public static enum HWVersion + public void setHWVersion( VmMetaData.HWVersion type ) { - NONE( 0, "(invalid)" ), - THREE( 3, " 3 (Workstation 4/5, Player 1)" ), - FOUR( 4, " 4 (Workstation 4/5, Player 1/2, Fusion 1)" ), - SIX( 6, " 6 (Workstation 6)" ), - SEVEN( 7, " 7 (Workstation 6.5/7, Player 3, Fusion 2/3)" ), - EIGHT( 8, " 8 (Workstation 8, Player/Fusion 4)" ), - NINE( 9, " 9 (Workstation 9, Player/Fusion 5)" ), - TEN( 10, "10 (Workstation 10, Player/Fusion 6)" ), - ELEVEN( 11, "11 (Workstation 11, Player/Fusion 7)" ), - TWELVE( 12, "12 (Workstation/Player 12, Fusion 8)" ); - - public final int version; - public final String displayName; - - private HWVersion( int vers, String dName ) - { - this.version = vers; - this.displayName = dName; - } - } - - public void setHWVersion( HWVersion type ) - { - addFiltered( "virtualHW.version", vmInteger( type.version ) ); + VmWareHWVersionMeta hwVersionMeta = hwversion.get( type ); + addFiltered( "virtualHW.version", vmInteger( hwVersionMeta.version ) ); } - public HWVersion getHWVersion() + public VmMetaData.HWVersion getHWVersion() { int currentValue = Util.parseInt( config.get( "virtualHW.version" ), -1 ); - for ( HWVersion ver : HWVersion.values() ) { - if ( currentValue == ver.version ) { + 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; } - // Virtual network adapter - public static enum EthernetDevType + public void setEthernetDevType( int cardIndex, VmMetaData.EthernetDevType type ) { - AUTO( null, "(default)" ), - PCNET32( "vlance", "AMD PCnet32" ), - E1000( "e1000", "Intel E1000 (PCI)" ), - E1000E( "e1000e", "Intel E1000e (PCI-Express)" ), - VMXNET( "vmxnet", "VMXnet" ), - VMXNET3( "vmxnet3", "VMXnet 3" ); - - public final String value; - public final String displayName; - - private EthernetDevType( String value, String dName ) - { - this.value = value; - this.displayName = dName; - } - } - - public void setEthernetDevType( int cardIndex, EthernetDevType type ) - { - if ( type.value != null ) { - addFiltered( "ethernet" + cardIndex + ".virtualDev", type.value ); + VmWareEthernetDevTypeMeta ethernetDevTypeMeta = networkCards.get( type ); + if ( ethernetDevTypeMeta.value != null ) { + addFiltered( "ethernet" + cardIndex + ".virtualDev", ethernetDevTypeMeta.value ); } else { config.remove( "ethernet" + cardIndex + ".virtualDev" ); } @@ -541,13 +544,54 @@ public class VmwareMetaData extends VmMetaData { String temp = config.get( "ethernet" + cardIndex + ".virtualDev" ); if ( temp != null ) { - - for ( EthernetDevType type : EthernetDevType.values() ) { - if ( temp.equals( type.value ) ) { + 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; + } + + 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" ) ); + } } -- cgit v1.2.3-55-g7522