From 82a08246b225f5dacc2618e00c5f9675d6169b34 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 14 Jul 2015 18:45:05 +0200 Subject: Add classes for analyzing vm metadata files --- src/main/java/org/openslx/util/vm/VmMetaData.java | 125 +++++++++++ .../java/org/openslx/util/vm/VmwareMetaData.java | 244 +++++++++++++++++++++ 2 files changed, 369 insertions(+) create mode 100644 src/main/java/org/openslx/util/vm/VmMetaData.java create mode 100644 src/main/java/org/openslx/util/vm/VmwareMetaData.java (limited to 'src') diff --git a/src/main/java/org/openslx/util/vm/VmMetaData.java b/src/main/java/org/openslx/util/vm/VmMetaData.java new file mode 100644 index 0000000..ff50920 --- /dev/null +++ b/src/main/java/org/openslx/util/vm/VmMetaData.java @@ -0,0 +1,125 @@ +package org.openslx.util.vm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; + +import org.openslx.bwlp.thrift.iface.OperatingSystem; + +/** + * Describes a configured virtual machine. This class is parsed from a machine description, like a + * *.vmx for VMware machines. + */ +public abstract class VmMetaData +{ + + /* + * Helper types + */ + + public static enum DriveBusType + { + SCSI, + IDE, + SATA; + } + + public static class HardDisk + { + public final String chipsetDriver; + public final DriveBusType bus; + public final String diskImage; + + public HardDisk( String chipsetDriver, DriveBusType bus, String diskImage ) + { + this.chipsetDriver = chipsetDriver; + this.bus = bus; + this.diskImage = diskImage; + } + } + + /* + * Members + */ + + protected final List hdds = new ArrayList<>(); + + private final List osList; + + private OperatingSystem os = null; + + protected String displayName = null; + + /* + * Guettas + */ + + /** + * Get operating system of this VM. + */ + public OperatingSystem getOs() + { + return os; + } + + /** + * Get all hard disks of this VM. + */ + public List getHdds() + { + return Collections.unmodifiableList( hdds ); + } + + /** + * Get display name of VM. + */ + public String getDisplayName() + { + return displayName; + } + + /** + * 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 String getFilteredDefinition(); + + /* + * Methods + */ + + public VmMetaData( List osList ) + { + this.osList = osList; + } + + /** + * Called from subclass to set the OS. If the OS cannot be determined from the given parameters, + * it will not be set. + * + * @param virtId virtualizer, eg "vmware" for VMware + * @param virtOsId the os identifier used by the virtualizer, eg. windows7-64 for 64bit Windows 7 + * on VMware + */ + protected final void setOs( String virtId, String virtOsId ) + { + OperatingSystem lazyMatch = null; + for ( OperatingSystem os : osList ) { + for ( Entry entry : os.getVirtualizerOsId().entrySet() ) { + if ( !entry.getValue().equals( virtOsId ) ) + continue; + if ( entry.getKey().equals( virtId ) ) { + this.os = os; + return; + } else { + lazyMatch = os; + } + } + } + this.os = lazyMatch; + } + +} diff --git a/src/main/java/org/openslx/util/vm/VmwareMetaData.java b/src/main/java/org/openslx/util/vm/VmwareMetaData.java new file mode 100644 index 0000000..703075c --- /dev/null +++ b/src/main/java/org/openslx/util/vm/VmwareMetaData.java @@ -0,0 +1,244 @@ +package org.openslx.util.vm; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.OperatingSystem; +import org.openslx.util.Util; + +public class VmwareMetaData extends VmMetaData +{ + + private static final Logger LOGGER = Logger.getLogger( VmwareMetaData.class ); + + private static class Device + { + public boolean present = false; + public String deviceType = null; + public String filename = null; + + @Override + public String toString() + { + return filename + " is " + deviceType + " (present: " + present + ")"; + } + } + + private static class Controller + { + public boolean present = true; // Seems to be implicit, seen at least for IDE... + public String virtualDev = null; + Map devices = new HashMap<>(); + + @Override + public String toString() + { + return virtualDev + " is (present: " + present + "): " + devices.toString(); + } + } + + private final Map disks = new HashMap<>(); + + public VmwareMetaData( List osList, File file ) throws IOException + { + super( osList ); + int todo = (int)Math.min( 100000, file.length() ); + int offset = 0; + byte[] data = new byte[ todo ]; + FileInputStream fr = null; + try { + fr = new FileInputStream( file ); + while ( todo > 0 ) { + int ret = fr.read( data, offset, todo ); + if ( ret <= 0 ) + break; + todo -= ret; + offset += ret; + } + } finally { + Util.streamClose( fr ); + } + init( data, offset ); + } + + public VmwareMetaData( List osList, byte[] vmxContent, int length ) + { + super( osList ); + init( vmxContent, length ); + } + + private void init( byte[] vmxContent, int length ) + { + String csName = detectCharset( new ByteArrayInputStream( vmxContent, 0, length ) ); + Charset cs = null; + try { + cs = Charset.forName( csName ); + } catch ( Exception e ) { + LOGGER.warn( "Could not instantiate charset " + csName, e ); + } + if ( cs == null ) + cs = StandardCharsets.ISO_8859_1; + try { + loadVmx( new ByteArrayInputStream( vmxContent, 0, length ), cs ); + } catch ( IOException e ) { + LOGGER.warn( "Exception when loading vmx from byte array (how!?)", e ); + } + } + + private void loadVmx( InputStream is, Charset cs ) throws IOException + { + LOGGER.info( "Loading VMX with charset " + cs.name() ); + BufferedReader reader = null; + try { + reader = new BufferedReader( new InputStreamReader( is, cs ) ); + String line; + while ( ( line = reader.readLine() ) != null ) { + KeyValuePair entry = parse( line ); + if ( entry != null ) + handleLoadEntry( entry ); + } + } finally { + Util.streamClose( reader ); + } + // Now find the HDDs and add to list + for ( Entry cEntry : disks.entrySet() ) { + Controller controller = cEntry.getValue(); + String controllerType = cEntry.getKey(); + if ( !controller.present ) + continue; + for ( Entry dEntry : controller.devices.entrySet() ) { + Device device = dEntry.getValue(); + if ( !device.present ) + continue; // Not present + if ( device.deviceType != null && !device.deviceType.endsWith( "disk" ) ) + continue; // Not a HDD + DriveBusType bus = null; + if ( controllerType.startsWith( "ide" ) ) { + bus = DriveBusType.IDE; + } else if ( controllerType.startsWith( "scsi" ) ) { + bus = DriveBusType.SCSI; + } else if ( controllerType.startsWith( "sata" ) ) { + bus = DriveBusType.SATA; + } + hdds.add( new HardDisk( controller.virtualDev, bus, device.filename ) ); + } + } + } + + private static final Pattern hddKey = Pattern.compile( "^(ide\\d|scsi\\d|sata\\d):(\\d)\\.(.*)", Pattern.CASE_INSENSITIVE ); + + private void handleLoadEntry( KeyValuePair entry ) + { + if ( entry.key.equalsIgnoreCase( "guestOS" ) ) { + setOs( "vmware", entry.value ); + return; + } + if ( entry.key.equalsIgnoreCase( "displayName" ) ) { + displayName = entry.value; + return; + } + Matcher hdd = hddKey.matcher( entry.key ); + if ( hdd.find() ) { + handleHddEntry( hdd.group( 1 ).toLowerCase(), hdd.group( 2 ), hdd.group( 3 ), entry.value ); + } + } + + private void handleHddEntry( String controllerStr, String deviceStr, String property, String value ) + { + Controller controller = disks.get( controllerStr ); + if ( controller == null ) { + controller = new Controller(); + disks.put( controllerStr, controller ); + } + if ( deviceStr == null || deviceStr.isEmpty() ) { + // Controller property + if ( property.equalsIgnoreCase( "present" ) ) { + controller.present = Boolean.parseBoolean( value ); + } else if ( property.equalsIgnoreCase( "virtualDev" ) ) { + controller.virtualDev = value; + } + return; + } + // Device property + Device device = controller.devices.get( deviceStr ); + if ( device == null ) { + device = new Device(); + controller.devices.put( deviceStr, device ); + } + if ( property.equalsIgnoreCase( "deviceType" ) ) { + device.deviceType = value; + } else if ( property.equalsIgnoreCase( "filename" ) ) { + device.filename = value; + } else if ( property.equalsIgnoreCase( "present" ) ) { + device.present = Boolean.parseBoolean( value ); + } + } + + private String detectCharset( InputStream is ) + { + BufferedReader csDetectReader = null; + try { + csDetectReader = new BufferedReader( new InputStreamReader( is, StandardCharsets.ISO_8859_1 ) ); + String line; + while ( ( line = csDetectReader.readLine() ) != null ) { + KeyValuePair entry = parse( line ); + if ( entry == null ) + continue; + if ( entry.key.equals( ".encoding" ) ) { + return entry.value; + } + } + } catch ( Exception e ) { + LOGGER.warn( "Could not detect charset, fallback to latin1", e ); + } finally { + Util.streamClose( csDetectReader ); + } + // Dumb fallback + return "ISO-8859-1"; + } + + private static final Pattern settingMatcher = Pattern.compile( "^\\s*([a-z0-9\\.\\:]+)\\s*=\\s*\"(.*)\"\\s*$", Pattern.CASE_INSENSITIVE ); + + private KeyValuePair parse( String line ) + { + Matcher matcher = settingMatcher.matcher( line ); + if ( !matcher.matches() ) + return null; + return new KeyValuePair( + matcher.group( 1 ), matcher.group( 2 ) ); + } + + @Override + public String getFilteredDefinition() + { + // TODO Auto-generated method stub + return null; + } + + private static class KeyValuePair + { + public final String key; + public final String value; + + public KeyValuePair( String key, String value ) + { + this.key = key; + this.value = value; + } + } + +} -- cgit v1.2.3-55-g7522