summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVictor Mocanu2018-04-16 17:14:45 +0200
committerJonathan Bauer2018-04-16 17:15:47 +0200
commit8a0d508f5a04f930e9945131db4f70591fd02e12 (patch)
tree81fd67311a42da83bdb58e339596cec5a80d3955 /src
parentRemove unused proxy-vole classes (diff)
downloadmaster-sync-shared-8a0d508f5a04f930e9945131db4f70591fd02e12.tar.gz
master-sync-shared-8a0d508f5a04f930e9945131db4f70591fd02e12.tar.xz
master-sync-shared-8a0d508f5a04f930e9945131db4f70591fd02e12.zip
[vbox] add support for VirtualBox
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/openslx/util/vm/DiskImage.java117
-rw-r--r--src/main/java/org/openslx/util/vm/QemuMetaData.java219
-rw-r--r--src/main/java/org/openslx/util/vm/UnsupportedVirtualizerFormatException.java9
-rw-r--r--src/main/java/org/openslx/util/vm/VboxConfig.java611
-rw-r--r--src/main/java/org/openslx/util/vm/VboxMetaData.java436
-rw-r--r--src/main/java/org/openslx/util/vm/VmMetaData.java267
-rw-r--r--src/main/java/org/openslx/util/vm/VmwareConfig.java40
-rw-r--r--src/main/java/org/openslx/util/vm/VmwareMetaData.java298
8 files changed, 1804 insertions, 193 deletions
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<VBoxSoundCardMeta, VBoxDDAccelMeta, VBoxHWVersionMeta, VBoxEthernetDevTypeMeta>
+{
+
+ private Map<String, String> arguments = new HashMap<String, String>();
+ // the above map's elements will take the place of <args> in the config string
+ private static String config = "qemu-system-i386 <args> <image> -enable-kvm \n\r qemu-system-x86_64 <args> <image> -enable-kvm";
+ private static final Logger LOGGER = Logger.getLogger( QemuMetaData.class );
+
+ private static final Virtualizer virtualizer = new Virtualizer( "qemukvm", "QEMU-KVM" );
+
+ public QemuMetaData( List<OperatingSystem> 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<OperatingSystem> 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( "<args>", 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( "<image>", 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( "<image>", 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<HardDisk> hddsArray = new ArrayList<HardDisk>();
+
+ // 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 <VirtualBox> 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 <Hardware/> 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 <Hardware/> were found, fatal either way
+ // HACK: hijack XPathExpressionException ...
+ throw new XPathExpressionException( "Zero or more than one <Hardware> 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 <AttachedDevice if found and do stuff with it
+ Element hddElement = (Element)nodes.item( i );
+ if ( hddElement == null )
+ continue;
+ // read the filePath
+ String fileName = hddElement.getAttribute( "location" );
+ // take the uuid
+ String uuid = hddElement.getAttribute( "uuid" );
+ // search in the xml object and give back the parent of the parent of the node that is called Image and has the given uuid
+ String pathToParent = givePathToStorageController( uuid );
+ XPathExpression attachedDevicesExpr = xPath.compile( pathToParent );
+ Object devicesResult = attachedDevicesExpr.evaluate( this.doc, XPathConstants.NODESET );
+ NodeList devicesNodes = (NodeList)devicesResult;
+ // TODO -- ehm...should only have 1 element...what do when there are more?
+ if ( devicesNodes.getLength() > 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<HardDisk> 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<VBoxSoundCardMeta, VBoxDDAccelMeta, VBoxHWVersionMeta, VBoxEthernetDevTypeMeta>
+{
+ 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<OperatingSystem> osList, File file ) throws IOException, UnsupportedVirtualizerFormatException
+ {
+ super( osList );
+ this.config = new VboxConfig( file );
+ init();
+ }
+
+ public VboxMetaData( List<OperatingSystem> 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<T, U, V, W>
{
+ 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" ), 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<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.
@@ -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<OperatingSystem> 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<OperatingSystem> 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<String, ConfigEntry> 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<VmWareSoundCardMeta, VmWareDDAccelMeta, VmWareHWVersionMeta, VmWareEthernetDevTypeMeta>
{
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<String, Controller> disks = new HashMap<>();
- public VmwareMetaData( List<OperatingSystem> osList, File file ) throws IOException
+ 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 )
+ public VmwareMetaData( List<OperatingSystem> 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<String, ConfigEntry> 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" ) );
+ }
}