From be40e979e03e41ddcd831d9c330902f76908ca64 Mon Sep 17 00:00:00 2001 From: Manuel Bentele Date: Thu, 25 Feb 2021 15:00:38 +0100 Subject: Refactor disk image representation and add unit tests --- src/main/java/org/openslx/util/ThriftUtil.java | 2 +- src/main/java/org/openslx/util/vm/DiskImage.java | 379 --------- .../org/openslx/util/vm/DockerMetaDataDummy.java | 216 ------ .../java/org/openslx/util/vm/KeyValuePair.java | 13 - .../java/org/openslx/util/vm/QemuMetaData.java | 854 --------------------- .../org/openslx/util/vm/QemuMetaDataUtils.java | 188 ----- .../vm/UnsupportedVirtualizerFormatException.java | 9 - src/main/java/org/openslx/util/vm/VboxConfig.java | 631 --------------- .../java/org/openslx/util/vm/VboxMetaData.java | 535 ------------- src/main/java/org/openslx/util/vm/VmMetaData.java | 418 ---------- .../java/org/openslx/util/vm/VmwareConfig.java | 276 ------- .../java/org/openslx/util/vm/VmwareMetaData.java | 683 ---------------- 12 files changed, 1 insertion(+), 4203 deletions(-) delete mode 100644 src/main/java/org/openslx/util/vm/DiskImage.java delete mode 100644 src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java delete mode 100644 src/main/java/org/openslx/util/vm/KeyValuePair.java delete mode 100644 src/main/java/org/openslx/util/vm/QemuMetaData.java delete mode 100644 src/main/java/org/openslx/util/vm/QemuMetaDataUtils.java delete mode 100644 src/main/java/org/openslx/util/vm/UnsupportedVirtualizerFormatException.java delete mode 100644 src/main/java/org/openslx/util/vm/VboxConfig.java delete mode 100644 src/main/java/org/openslx/util/vm/VboxMetaData.java delete mode 100644 src/main/java/org/openslx/util/vm/VmMetaData.java delete mode 100644 src/main/java/org/openslx/util/vm/VmwareConfig.java delete mode 100644 src/main/java/org/openslx/util/vm/VmwareMetaData.java (limited to 'src/main/java/org/openslx/util') diff --git a/src/main/java/org/openslx/util/ThriftUtil.java b/src/main/java/org/openslx/util/ThriftUtil.java index 327f2d3..3c2c9ea 100644 --- a/src/main/java/org/openslx/util/ThriftUtil.java +++ b/src/main/java/org/openslx/util/ThriftUtil.java @@ -7,7 +7,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import org.openslx.util.vm.VmwareConfig; +import org.openslx.vm.VmwareConfig; public class ThriftUtil { diff --git a/src/main/java/org/openslx/util/vm/DiskImage.java b/src/main/java/org/openslx/util/vm/DiskImage.java deleted file mode 100644 index 617fadd..0000000 --- a/src/main/java/org/openslx/util/vm/DiskImage.java +++ /dev/null @@ -1,379 +0,0 @@ -package org.openslx.util.vm; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.Arrays; -import java.util.List; -import java.util.function.Predicate; - -import org.apache.log4j.Logger; -import org.openslx.bwlp.thrift.iface.Virtualizer; -import org.openslx.thrifthelper.TConst; -import org.openslx.util.Util; - -public class DiskImage -{ - private static final Logger LOGGER = Logger.getLogger( DiskImage.class ); - /** - * Big endian representation of the 4 bytes 'KDMV' - */ - private static final int VMDK_MAGIC = 0x4b444d56; - private static final int VDI_MAGIC = 0x7f10dabe; - /** - * Big endian representation of the 4 bytes 'QFI\xFB' - */ - private static final int QEMU_MAGIC = 0x514649fb; - - public enum ImageFormat - { - VMDK( "vmdk" ), QCOW2( "qcow2" ), VDI( "vdi" ), DOCKER( "dockerfile" ); - - public final String extension; - - private ImageFormat( String extension ) - { - this.extension = extension; - } - - public static ImageFormat defaultForVirtualizer( Virtualizer virt ) - { - if ( virt == null ) - return null; - return defaultForVirtualizer( virt.virtId ); - } - - public static ImageFormat defaultForVirtualizer( String virtId ) - { - if ( virtId == null ) - return null; - if ( virtId.equals( TConst.VIRT_VMWARE ) ) - return VMDK; - if ( virtId.equals( TConst.VIRT_VIRTUALBOX ) ) - return VDI; - if ( virtId.equals( TConst.VIRT_QEMU ) ) - return QCOW2; - if ( virtId.equals( TConst.VIRT_DOCKER ) ) - return DOCKER; - return null; - } - } - - public final boolean isStandalone; - public final boolean isCompressed; - public final boolean isSnapshot; - public final ImageFormat format; - public final String subFormat; - public final int hwVersion; - public final String diskDescription; - - public ImageFormat getImageFormat() - { - return format; - } - - public DiskImage( File disk ) throws FileNotFoundException, IOException, UnknownImageFormatException - { - LOGGER.debug( "Validating disk file: " + disk.getAbsolutePath() ); - try ( RandomAccessFile file = new RandomAccessFile( disk, "r" ) ) { - // vmdk - boolean isVmdkMagic = ( file.readInt() == VMDK_MAGIC ); - if ( isVmdkMagic || file.length() < 4096 ) { - if ( isVmdkMagic ) { - file.seek( 512 ); - } else { - file.seek( 0 ); - } - byte[] buffer = new byte[ (int)Math.min( 2048, file.length() ) ]; - file.readFully( buffer ); - VmwareConfig config; - try { - config = new VmwareConfig( buffer, findNull( buffer ) ); - } catch ( UnsupportedVirtualizerFormatException e ) { - config = null; - } - if ( config != null ) { - String sf = config.get( "createType" ); - String parent = config.get( "parentCID" ); - if ( sf != null || parent != null ) { - subFormat = sf; - this.isStandalone = isStandaloneCreateType( subFormat, parent ); - this.isCompressed = subFormat != null && subFormat.equalsIgnoreCase( "streamOptimized" ); - this.isSnapshot = parent != null && !parent.equalsIgnoreCase( "ffffffff" ); - this.format = ImageFormat.VMDK; - String hwv = config.get( "ddb.virtualHWVersion" ); - if ( hwv == null ) { - this.hwVersion = 10; - } else { - this.hwVersion = Util.parseInt( hwv, 10 ); - } - this.diskDescription = null; - return; - } - } - } - // Format spec from: https://forums.virtualbox.org/viewtopic.php?t=8046 - // First 64 bytes are the opening tag: <<< .... >>> - // which we don't care about, then comes the VDI signature - file.seek( 64 ); - if ( file.readInt() == VDI_MAGIC ) { - // skip the next 4 ints as they don't interest us: - // - VDI version - // - size of header, strangely irrelevant? - // - image type, 1 for dynamic allocated storage, 2 for fixed size - // - image flags, seem to be always 00 00 00 00 - file.skipBytes( 4 * 4 ); - - // next 256 bytes are image description - byte[] imageDesc = new byte[ 256 ]; - file.readFully( imageDesc ); - // next sections are irrelevant (int if not specified): - // - offset blocks - // - offset data - // - cylinders - // - heads - // - sectors - // - sector size - // - - // - disk size (long = 8 bytes) - // - block size - // - block extra data - // - blocks in hdd - // - blocks allocated - file.skipBytes( 4 * 13 ); - - // now it gets interesting, UUID of VDI - byte[] diskUuid = new byte[ 16 ]; - file.readFully( diskUuid ); - // last snapshot uuid, mostly uninteresting since we don't support snapshots -> skip 16 bytes - // TODO: meaning of "uuid link"? for now, skip 16 - file.skipBytes( 32 ); - - // parent uuid, indicator if this VDI is a snapshot or not - byte[] parentUuid = new byte[ 16 ]; - file.readFully( parentUuid ); - byte[] zeroUuid = new byte[ 16 ]; - Arrays.fill( zeroUuid, (byte)0 ); - this.isSnapshot = !Arrays.equals( parentUuid, zeroUuid ); - // VDI does not seem to support split VDI files so always standalone - this.isStandalone = true; - // compression is done by sparsifying the disk files, there is no flag for it - this.isCompressed = false; - this.format = ImageFormat.VDI; - this.subFormat = null; - this.diskDescription = new String( imageDesc ); - this.hwVersion = 0; - return; - } - - // qcow2 disk image - file.seek( 0 ); - if ( file.readInt() == QEMU_MAGIC ) { - // - // qcow2 (version 2 and 3) header format: - // - // magic (4 byte) - // version (4 byte) - // backing_file_offset (8 byte) - // backing_file_size (4 byte) - // cluster_bits (4 byte) - // size (8 byte) - // crypt_method (4 byte) - // l1_size (4 byte) - // l1_table_offset (8 byte) - // refcount_table_offset (8 byte) - // refcount_table_clusters (4 byte) - // nb_snapshots (4 byte) - // snapshots_offset (8 byte) - // incompatible_features (8 byte) [*] - // compatible_features (8 byte) [*] - // autoclear_features (8 byte) [*] - // refcount_order (8 byte) [*] - // header_length (4 byte) [*] - // - // [*] these fields are only available in the qcow2 version 3 header format - // - - // - // check qcow2 file format version - // - file.seek( 4 ); - final int qcowVersion = file.readInt(); - if ( qcowVersion < 2 || qcowVersion > 3 ) { - // disk image format is not a qcow2 disk format - throw new UnknownImageFormatException(); - } else { - // disk image format is a valid qcow2 disk format - this.hwVersion = qcowVersion; - this.subFormat = null; - } - - // - // check if qcow2 image does not refer to any backing file - // - file.seek( 8 ); - this.isStandalone = ( file.readLong() == 0 ) ? true : false; - - // - // check if qcow2 image does not contain any snapshot - // - file.seek( 56 ); - this.isSnapshot = ( file.readInt() == 0 ) ? true : false; - - // - // check if qcow2 image uses extended L2 tables - // - boolean qcowUseExtendedL2 = false; - - // extended L2 tables are only possible in qcow2 version 3 header format - if ( qcowVersion == 3 ) { - // read incompatible feature bits - file.seek( 72 ); - final long qcowIncompatibleFeatures = file.readLong(); - - // support for extended L2 tables is enabled if bit 4 is set - qcowUseExtendedL2 = ( ( ( qcowIncompatibleFeatures & 0x000000000010 ) >>> 4 ) == 1 ); - } - - // - // check if qcow2 image contains compressed clusters - // - boolean qcowCompressed = false; - - // get number of entries in L1 table - file.seek( 36 ); - final int qcowL1TableSize = file.readInt(); - - // check if a valid L1 table is present - if ( qcowL1TableSize > 0 ) { - // qcow2 image contains active L1 table with more than 0 entries: l1_size > 0 - // search for first L2 table and its first entry to get compression bit - - // get cluster bits to calculate the cluster size - file.seek( 20 ); - final int qcowClusterBits = file.readInt(); - final int qcowClusterSize = ( 1 << qcowClusterBits ); - - // entries of a L1 table have always the size of 8 byte (64 bit) - final int qcowL1TableEntrySize = 8; - - // entries of a L2 table have either the size of 8 or 16 byte (64 or 128 bit) - final int qcowL2TableEntrySize = ( qcowUseExtendedL2 ) ? 16 : 8; - - // calculate number of L2 table entries - final int qcowL2TableSize = qcowClusterSize / qcowL2TableEntrySize; - - // get offset of L1 table - file.seek( 40 ); - long qcowL1TableOffset = file.readLong(); - - // check for each L2 table referenced from an L1 table its entries - // until a compressed cluster descriptor is found - for ( long i = 0; i < qcowL1TableSize; i++ ) { - // get offset of current L2 table from the current L1 table entry - long qcowL1TableEntryOffset = qcowL1TableOffset + ( i * qcowL1TableEntrySize ); - file.seek( qcowL1TableEntryOffset ); - long qcowL1TableEntry = file.readLong(); - - // extract offset (bits 9 - 55) from L1 table entry - long qcowL2TableOffset = ( qcowL1TableEntry & 0x00fffffffffffe00L ); - - if ( qcowL2TableOffset == 0 ) { - // L2 table and all clusters described by this L2 table are unallocated - continue; - } - - // get each L2 table entry and check if it is a compressed cluster descriptor - for ( long j = 0; j < qcowL2TableSize; j++ ) { - // get current L2 table entry - long qcowL2TableEntryOffset = qcowL2TableOffset + ( j * qcowL2TableEntrySize ); - file.seek( qcowL2TableEntryOffset ); - long qcowL2TableEntry = file.readLong(); - - // extract cluster type (standard or compressed) (bit 62) - boolean qcowClusterCompressed = ( ( ( qcowL2TableEntry & 0x4000000000000000L ) >>> 62 ) == 1 ); - - // check if qcow2 disk image contains at least one compressed cluster descriptor - if ( qcowClusterCompressed ) { - qcowCompressed = true; - break; - } - } - - // terminate if one compressed cluster descriptor is already found - if ( qcowCompressed ) { - break; - } - } - } else { - // qcow2 image does not contain an active L1 table with any entry: l1_size = 0 - qcowCompressed = false; - } - - this.isCompressed = qcowCompressed; - this.format = ImageFormat.QCOW2; - this.diskDescription = null; - - return; - } - } - throw new UnknownImageFormatException(); - } - - /** - * Creates new disk image and checks if image is supported by hypervisor's image formats. - * - * @param disk file to a disk storing the virtual machine content. - * @param supportedImageTypes list of supported image types of a hypervisor's image format. - * @throws FileNotFoundException cannot find virtual machine image file. - * @throws IOException cannot access the virtual machine image file. - * @throws UnknownImageFormatException virtual machine image file has an unknown image format. - */ - public DiskImage( File disk, List supportedImageTypes ) - throws FileNotFoundException, IOException, UnknownImageFormatException - { - this( disk ); - - if ( !this.isSupportedByHypervisor( supportedImageTypes ) ) { - throw new UnknownImageFormatException(); - } - } - - /** - * Checks if image format is supported by a list of supported hypervisor image formats. - * - * @param supportedImageTypes list of supported image types of a hypervisor. - * @return true if image type is supported by the hypervisor; otherwise - * false. - */ - public boolean isSupportedByHypervisor( List supportedImageTypes ) - { - Predicate matchDiskFormat = supportedImageType -> supportedImageType.toString() - .equalsIgnoreCase( this.getImageFormat().toString() ); - return supportedImageTypes.stream().anyMatch( matchDiskFormat ); - } - - private int findNull( byte[] buffer ) - { - for ( int i = 0; i < buffer.length; ++i ) { - if ( buffer[i] == 0 ) - return i; - } - return buffer.length; - } - - private boolean isStandaloneCreateType( String type, String parent ) - { - if ( type == null ) - return false; - if ( parent != null && !parent.equalsIgnoreCase( "ffffffff" ) ) - return false; - 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/DockerMetaDataDummy.java b/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java deleted file mode 100644 index 38388ce..0000000 --- a/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java +++ /dev/null @@ -1,216 +0,0 @@ -package org.openslx.util.vm; - -import org.apache.commons.io.IOUtils; -import org.apache.log4j.Logger; -import org.openslx.bwlp.thrift.iface.OperatingSystem; -import org.openslx.bwlp.thrift.iface.Virtualizer; -import org.openslx.thrifthelper.TConst; -import org.openslx.util.vm.DiskImage.ImageFormat; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -class DockerSoundCardMeta -{ -} - -class DockerDDAccelMeta -{ -} - -class DockerHWVersionMeta -{ -} - -class DockerEthernetDevTypeMeta -{ -} - -class DockerUsbSpeedMeta -{ -} - -public class DockerMetaDataDummy extends VmMetaData { - - /** - * List of supported image formats by the Docker hypervisor. - */ - private static final List SUPPORTED_IMAGE_FORMATS = Collections.unmodifiableList( - Arrays.asList( ImageFormat.DOCKER ) ); - - private static final Logger LOGGER = Logger.getLogger( DockerMetaDataDummy.class); - - private final Virtualizer virtualizer = new Virtualizer(TConst.VIRT_DOCKER, "Docker"); - - /** - * containerDefinition is a serialized tar.gz archive and represents a - * ContainerDefinition. This archive contains a serialized Container Recipe (e.g. Dockerfile) - * and a ContainerMeta witch is serialized as a json file. - *

- * See ContainerDefintion in tutor-module (bwsuite). - *

- * This field is in vm context the machine description e.g. vmware = vmx. - * This field will be stored in table imageversion.virtualizerconfig - */ - private byte[] containerDefinition; - - @SuppressWarnings( "deprecation" ) - public DockerMetaDataDummy(List osList, File file) throws UnsupportedVirtualizerFormatException { - super(osList); - - BufferedInputStream bis = null; - - try { - bis = new BufferedInputStream(new FileInputStream(file)); - containerDefinition = new byte[(int) file.length()]; - bis.read(containerDefinition); - - checkIsTarGz(); - } catch (IOException | UnsupportedVirtualizerFormatException e) { - LOGGER.error("Couldn't read dockerfile", e); - } finally { - IOUtils.closeQuietly( bis ); - } - } - - public DockerMetaDataDummy(List osList, byte[] vmContent, int length) - throws UnsupportedVirtualizerFormatException { - super(osList); - - containerDefinition = vmContent; - - checkIsTarGz(); - } - - /* - TODO This is just a simple check to prevent the workflow from considering any content as acceptable. - */ - /** - * Checks if the first two bytes of the content identifies a tar.gz archive. - * The first byte is 31 == 0x1f, the second byte has to be -117 == 0x8b. - * - * @throws UnsupportedVirtualizerFormatException - */ - private void checkIsTarGz() throws UnsupportedVirtualizerFormatException { - if (!((31 == containerDefinition[0]) && (-117 == containerDefinition[1]))) { - LOGGER.warn("Not Supported Content."); - throw new UnsupportedVirtualizerFormatException( - "DockerMetaDataDummy: Not tar.gz encoded content!"); - } - } - - @Override public byte[] getFilteredDefinitionArray() { - return containerDefinition; - } - - @Override - public List getSupportedImageFormats() - { - return DockerMetaDataDummy.SUPPORTED_IMAGE_FORMATS; - } - - @Override public void applySettingsForLocalEdit() { - - } - - @Override public boolean addHddTemplate(File diskImage, String hddMode, String redoDir) { - return false; - } - - @Override public boolean addHddTemplate(String diskImagePath, String hddMode, String redoDir) { - return false; - } - - @Override public boolean addDefaultNat() { - return false; - } - - @Override public void setOs(String vendorOsId) { - - } - - @Override public boolean addDisplayName(String name) { - return false; - } - - @Override public boolean addRam(int mem) { - return false; - } - - @Override public void addFloppy(int index, String image, boolean readOnly) { - - } - - @Override public boolean addCdrom(String image) { - return false; - } - - @Override public boolean addCpuCoreCount(int nrOfCores) { - return false; - } - - @Override public void setSoundCard(SoundCardType type) { - - } - - @Override public SoundCardType getSoundCard() { - return SoundCardType.NONE; - } - - @Override public void setDDAcceleration(DDAcceleration type) { - - } - - @Override public DDAcceleration getDDAcceleration() { - return DDAcceleration.OFF; - } - - @Override public void setHWVersion(HWVersion type) { - - } - - @Override public HWVersion getHWVersion() { - return HWVersion.DEFAULT; - } - - @Override public void setEthernetDevType(int cardIndex, EthernetDevType type) { - - } - - @Override public EthernetDevType getEthernetDevType(int cardIndex) { - return EthernetDevType.NONE; - } - - @Override public void setMaxUsbSpeed(UsbSpeed speed) { - - } - - @Override public UsbSpeed getMaxUsbSpeed() { - return UsbSpeed.NONE; - } - - @Override public byte[] getDefinitionArray() { - return new byte[0]; - } - - @Override public boolean addEthernet(EtherType type) { - return false; - } - - @Override public Virtualizer getVirtualizer() { - return virtualizer; - } - - @Override public boolean tweakForNonPersistent() { - return false; - } - - @Override public void registerVirtualHW() { - - } -} diff --git a/src/main/java/org/openslx/util/vm/KeyValuePair.java b/src/main/java/org/openslx/util/vm/KeyValuePair.java deleted file mode 100644 index d89d51b..0000000 --- a/src/main/java/org/openslx/util/vm/KeyValuePair.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.openslx.util.vm; - -class KeyValuePair -{ - public final String key; - public final String value; - - public KeyValuePair( String key, String value ) - { - this.key = key; - this.value = value; - } -} \ No newline at end of file diff --git a/src/main/java/org/openslx/util/vm/QemuMetaData.java b/src/main/java/org/openslx/util/vm/QemuMetaData.java deleted file mode 100644 index d3b8451..0000000 --- a/src/main/java/org/openslx/util/vm/QemuMetaData.java +++ /dev/null @@ -1,854 +0,0 @@ -package org.openslx.util.vm; - -import java.io.File; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map.Entry; - -import org.openslx.bwlp.thrift.iface.OperatingSystem; -import org.openslx.bwlp.thrift.iface.Virtualizer; -import org.openslx.libvirt.domain.Domain; -import org.openslx.libvirt.domain.device.ControllerUsb; -import org.openslx.libvirt.domain.device.Disk.BusType; -import org.openslx.libvirt.domain.device.Disk.StorageType; -import org.openslx.libvirt.domain.device.DiskCdrom; -import org.openslx.libvirt.domain.device.DiskFloppy; -import org.openslx.libvirt.domain.device.DiskStorage; -import org.openslx.libvirt.domain.device.Graphics; -import org.openslx.libvirt.domain.device.GraphicsSpice; -import org.openslx.libvirt.domain.device.Interface; -import org.openslx.libvirt.domain.device.Sound; -import org.openslx.libvirt.domain.device.Video; -import org.openslx.libvirt.xml.LibvirtXmlDocumentException; -import org.openslx.libvirt.xml.LibvirtXmlSerializationException; -import org.openslx.libvirt.xml.LibvirtXmlValidationException; -import org.openslx.thrifthelper.TConst; -import org.openslx.util.vm.DiskImage.ImageFormat; - -/** - * Metadata to describe the hardware type of a QEMU sound card. - * - * @author Manuel Bentele - * @version 1.0 - */ -class QemuSoundCardMeta -{ - /** - * Stores the hardware model of the QEMU sound card. - */ - private final Sound.Model model; - - /** - * Creates metadata to describe the hardware model of a QEMU sound card. - * - * @param model hardware model of the QEMU sound card. - */ - public QemuSoundCardMeta( Sound.Model model ) - { - this.model = model; - } - - /** - * Returns hardware model of the QEMU sound card. - * - * @return hardware model of the QEMU sound card. - */ - public Sound.Model getModel() - { - return this.model; - } -} - -/** - * Metadata to describe the hardware acceleration state of QEMU virtual graphics. - * - * @author Manuel Bentele - * @version 1.0 - */ -class QemuDDAccelMeta -{ - /** - * Stores state of the hardware acceleration for QEMU virtual graphics. - */ - private final boolean enabled; - - /** - * Creates metadata to describe the hardware acceleration state of QEMU virtual graphics. - * - * @param enabled state of the hardware acceleration for QEMU virtual graphics. - */ - public QemuDDAccelMeta( boolean enabled ) - { - this.enabled = enabled; - } - - /** - * Returns state of the hardware acceleration of QEMU virtual graphics. - * - * @return state of the hardware acceleration for QEMU virtual graphics. - */ - public boolean isEnabled() - { - return this.enabled; - } -} - -/** - * Metadata to describe the version of a QEMU virtual machine configuration. - * - * @author Manuel Bentele - * @version 1.0 - */ -class QemuHWVersionMeta -{ - /** - * Stores the version of a QEMU virtual machine configuration. - */ - private final int version; - - /** - * Creates metadata to describe the version of a QEMU virtual machine configuration. - * - * @param version version of the QEMU virtual machine configuration. - */ - public QemuHWVersionMeta( int version ) - { - this.version = version; - } - - /** - * Returns version of the QEMU virtual machine configuration. - * - * @return version of the QEMU virtual machine configuration. - */ - public int getVersion() - { - return this.version; - } -} - -/** - * Metadata to describe the hardware type of a QEMU ethernet device. - * - * @author Manuel Bentele - * @version 1.0 - */ -class QemuEthernetDevTypeMeta -{ - /** - * Stores the hardware model of the QEMU ethernet device. - */ - private final Interface.Model model; - - /** - * Creates metadata to describe the hardware type of a QEMU ethernet device. - * - * @param model hardware type of the QEMU ethernet device. - */ - public QemuEthernetDevTypeMeta( Interface.Model model ) - { - this.model = model; - } - - /** - * Returns the hardware type of a QEMU ethernet device. - * - * @return hardware type of the QEMU ethernet device. - */ - public Interface.Model getModel() - { - return this.model; - } -} - -/** - * Metadata to describe a QEMU USB controller. - * - * @author Manuel Bentele - * @version 1.0 - */ -class QemuUsbSpeedMeta -{ - /** - * Stores the USB speed of the QEMU USB controller. - */ - private final int speed; - - /** - * Stores the QEMU hardware model of the USB controller. - */ - private final ControllerUsb.Model model; - - /** - * Creates metadata to describe a QEMU USB controller. - * - * @param speed USB speed of the QEMU USB controller. - * @param model QEMU hardware model of the USB controller. - */ - public QemuUsbSpeedMeta( int speed, ControllerUsb.Model model ) - { - this.speed = speed; - this.model = model; - } - - /** - * Returns the speed of the QEMU USB controller. - * - * @return speed of the QEMU USB controller. - */ - public int getSpeed() - { - return this.speed; - } - - /** - * Returns QEMU hardware model of the USB controller. - * - * @return hardware model of the QEMU USB controller. - */ - public ControllerUsb.Model getModel() - { - return this.model; - } -} - -/** - * Virtual machine configuration (managed by Libvirt) for the QEMU hypervisor. - * - * @author Manuel Bentele - * @version 1.0 - */ -public class QemuMetaData extends - VmMetaData -{ - /** - * Default bridge name of the network bridge connected to the LAN. - */ - public static final String NETWORK_DEFAULT_BRIDGE = "brBwLehrpool"; - - /** - * Default network name of the isolated host network (host only). - */ - public static final String NETWORK_DEFAULT_HOST_ONLY = "host"; - - /** - * Default network name of the NAT network. - */ - public static final String NETWORK_DEFAULT_NAT = "nat"; - - /** - * Default physical CDROM drive of the hypervisor host. - */ - public static final String CDROM_DEFAULT_PHYSICAL_DRIVE = "/dev/sr0"; - - /** - * List of supported image formats by the QEMU hypervisor. - */ - private static final List SUPPORTED_IMAGE_FORMATS = Collections.unmodifiableList( - Arrays.asList( ImageFormat.QCOW2, ImageFormat.VMDK, ImageFormat.VDI ) ); - - /** - * Representation of a QEMU hypervisor (managed by Libvirt). - */ - private static final Virtualizer VIRTUALIZER = new Virtualizer( TConst.VIRT_QEMU, "QEMU" ); - - /** - * Libvirt XML configuration file to modify configuration of virtual machine for QEMU. - */ - private Domain vmConfig = null; - - /** - * Stores current index of added HDD device to the Libvirt XML configuration file. - */ - private int vmDeviceIndexHddAdd = 0; - - /** - * Stores current index of added CDROM device to the Libvirt XML configuration file. - */ - private int vmDeviceIndexCdromAdd = 0; - - /** - * Stores current index of added ethernet device to the Libvirt XML configuration file. - */ - private int vmDeviceIndexEthernetAdd = 0; - - /** - * Creates new virtual machine configuration (managed by Libvirt) for the QEMU hypervisor. - * - * @param osList list of operating systems. - * @param file image file for the QEMU hypervisor. - * @throws UnsupportedVirtualizerFormatException Libvirt XML configuration cannot be processed. - */ - public QemuMetaData( List osList, File file ) throws UnsupportedVirtualizerFormatException - { - super( osList ); - - try { - // read and parse Libvirt domain XML configuration document - this.vmConfig = new Domain( file ); - } catch ( LibvirtXmlDocumentException e ) { - throw new UnsupportedVirtualizerFormatException( e.getLocalizedMessage() ); - } catch ( LibvirtXmlSerializationException e ) { - throw new UnsupportedVirtualizerFormatException( e.getLocalizedMessage() ); - } catch ( LibvirtXmlValidationException e ) { - throw new UnsupportedVirtualizerFormatException( e.getLocalizedMessage() ); - } - - // register virtual hardware models for graphical editing of virtual devices (GPU, sound, USB, ...) - this.registerVirtualHW(); - - // set display name of VM - this.displayName = vmConfig.getName(); - - // this property cannot be checked with the Libvirt domain XML configuration - // to check if machine is in a paused/suspended state, look in the QEMU qcow2 image for snapshots and machine states - this.isMachineSnapshot = false; - - // add HDDs, SSDs to QEMU metadata - for ( DiskStorage storageDiskDevice : this.vmConfig.getDiskStorageDevices() ) { - this.addHddMetaData( storageDiskDevice ); - } - } - - /** - * Adds an existing and observed storage disk device to the HDD metadata. - * - * @param storageDiskDevice existing and observed storage disk that should be added to the - * metadata. - */ - private void addHddMetaData( DiskStorage storageDiskDevice ) - { - String hddChipsetModel = null; - DriveBusType hddChipsetBus = QemuMetaDataUtils.convertBusType( storageDiskDevice.getBusType() ); - String hddImagePath = storageDiskDevice.getStorageSource(); - - this.hdds.add( new HardDisk( hddChipsetModel, hddChipsetBus, hddImagePath ) ); - } - - @Override - public byte[] getFilteredDefinitionArray() - { - // remove UUID in Libvirt domain XML configuration - this.vmConfig.removeUuid(); - - // removes all specified boot order entries - this.vmConfig.removeBootOrder(); - - // removes all referenced storage files of all specified CDROMs, Floppy drives and HDDs - this.vmConfig.removeDiskDevicesStorage(); - - // removes all source networks of all specified network interfaces - this.vmConfig.removeInterfaceDevicesSource(); - - // output filtered Libvirt domain XML configuration - String configuration = this.vmConfig.toString(); - return configuration.getBytes( StandardCharsets.UTF_8 ); - } - - @Override - public List getSupportedImageFormats() - { - return QemuMetaData.SUPPORTED_IMAGE_FORMATS; - } - - @Override - public void applySettingsForLocalEdit() - { - // NOT implemented yet - } - - @Override - public boolean addHddTemplate( File diskImage, String hddMode, String redoDir ) - { - return this.addHddTemplate( diskImage.getAbsolutePath(), hddMode, redoDir ); - } - - @Override - public boolean addHddTemplate( String diskImagePath, String hddMode, String redoDir ) - { - return this.addHddTemplate( this.vmDeviceIndexHddAdd++, diskImagePath, hddMode, redoDir ); - } - - /** - * Adds hard disk drive (HDD) to the QEMU virtual machine configuration. - * - * @param index current index of HDD to be added to the virtual machine configuration. - * @param diskImagePath path to the virtual disk image for the HDD. - * @param hddMode operation mode of the HDD. - * @param redoDir directory for the redo log if an independent non-persistent - * hddMode is set. - * @return result state of adding the HDD. - */ - public boolean addHddTemplate( int index, String diskImagePath, String hddMode, String redoDir ) - { - ArrayList storageDiskDevices = this.vmConfig.getDiskStorageDevices(); - DiskStorage storageDiskDevice = QemuMetaDataUtils.getArrayIndex( storageDiskDevices, index ); - - if ( storageDiskDevice == null ) { - // HDD does not exist, so create new storage (HDD) device - storageDiskDevice = this.vmConfig.addDiskStorageDevice(); - storageDiskDevice.setReadOnly( false ); - storageDiskDevice.setBusType( BusType.VIRTIO ); - String targetDevName = QemuMetaDataUtils.createAlphabeticalDeviceName( "vd", index ); - storageDiskDevice.setTargetDevice( targetDevName ); - storageDiskDevice.setStorage( StorageType.FILE, diskImagePath ); - - // add new created HDD to the metadata of the QemuMetaData object, too - this.addHddMetaData( storageDiskDevice ); - } else { - // HDD exists, so update existing storage (HDD) device - storageDiskDevice.setStorage( StorageType.FILE, diskImagePath ); - } - - return false; - } - - @Override - public boolean addDefaultNat() - { - return this.addEthernet( EtherType.NAT ); - } - - @Override - public void setOs( String vendorOsId ) - { - this.setOs( vendorOsId ); - } - - @Override - public boolean addDisplayName( String name ) - { - this.vmConfig.setName( name ); - - final boolean statusName = this.vmConfig.getName().equals( name ); - - return statusName; - } - - @Override - public boolean addRam( int mem ) - { - BigInteger memory = BigInteger.valueOf( mem ); - - this.vmConfig.setMemory( memory ); - this.vmConfig.setCurrentMemory( memory ); - - final boolean isMemorySet = this.vmConfig.getMemory().toString().equals( memory.toString() ); - final boolean isCurrentMemorySet = this.vmConfig.getCurrentMemory().toString().equals( memory.toString() ); - - return isMemorySet && isCurrentMemorySet; - } - - @Override - public void addFloppy( int index, String image, boolean readOnly ) - { - ArrayList floppyDiskDevices = this.vmConfig.getDiskFloppyDevices(); - DiskFloppy floppyDiskDevice = QemuMetaDataUtils.getArrayIndex( floppyDiskDevices, index ); - - if ( floppyDiskDevice == null ) { - // floppy device does not exist, so create new floppy device - floppyDiskDevice = this.vmConfig.addDiskFloppyDevice(); - floppyDiskDevice.setBusType( BusType.FDC ); - String targetDevName = QemuMetaDataUtils.createAlphabeticalDeviceName( "fd", index ); - floppyDiskDevice.setTargetDevice( targetDevName ); - floppyDiskDevice.setReadOnly( readOnly ); - floppyDiskDevice.setStorage( StorageType.FILE, image ); - } else { - // floppy device exists, so update existing floppy device - floppyDiskDevice.setReadOnly( readOnly ); - floppyDiskDevice.setStorage( StorageType.FILE, image ); - } - } - - @Override - public boolean addCdrom( String image ) - { - return this.addCdrom( this.vmDeviceIndexCdromAdd++, image ); - } - - /** - * Adds CDROM drive to the QEMU virtual machine configuration. - * - * @param index current index of CDROM drive to be added to the virtual machine configuration. - * @param image path to a virtual image that will be inserted as CDROM into the drive. - * @return result state of adding the CDROM drive. - */ - public boolean addCdrom( int index, String image ) - { - ArrayList cdromDiskDevices = this.vmConfig.getDiskCdromDevices(); - DiskCdrom cdromDiskDevice = QemuMetaDataUtils.getArrayIndex( cdromDiskDevices, index ); - - if ( cdromDiskDevice == null ) { - // CDROM device does not exist, so create new CDROM device - cdromDiskDevice = this.vmConfig.addDiskCdromDevice(); - cdromDiskDevice.setBusType( BusType.SATA ); - String targetDevName = QemuMetaDataUtils.createAlphabeticalDeviceName( "sd", index ); - cdromDiskDevice.setTargetDevice( targetDevName ); - cdromDiskDevice.setReadOnly( true ); - - if ( image == null ) { - cdromDiskDevice.setStorage( StorageType.BLOCK, CDROM_DEFAULT_PHYSICAL_DRIVE ); - } else { - cdromDiskDevice.setStorage( StorageType.FILE, image ); - } - } else { - // CDROM device exists, so update existing CDROM device - cdromDiskDevice.setReadOnly( true ); - - if ( image == null ) { - cdromDiskDevice.setStorage( StorageType.BLOCK, CDROM_DEFAULT_PHYSICAL_DRIVE ); - } else { - cdromDiskDevice.setStorage( StorageType.FILE, image ); - } - } - - return false; - } - - @Override - public boolean addCpuCoreCount( int nrOfCores ) - { - this.vmConfig.setVCpu( nrOfCores ); - - boolean isVCpuSet = this.vmConfig.getVCpu() == nrOfCores; - - return isVCpuSet; - } - - @Override - public void setSoundCard( SoundCardType type ) - { - QemuSoundCardMeta soundDeviceConfig = this.soundCards.get( type ); - ArrayList soundDevices = this.vmConfig.getSoundDevices(); - Sound.Model soundDeviceModel = soundDeviceConfig.getModel(); - - if ( soundDevices.isEmpty() ) { - // create new sound device with 'soundDeviceModel' hardware - Sound soundDevice = this.vmConfig.addSoundDevice(); - soundDevice.setModel( soundDeviceModel ); - } else { - // update sound device model type of existing sound devices - for ( Sound soundDevice : soundDevices ) { - soundDevice.setModel( soundDeviceModel ); - } - } - } - - @Override - public SoundCardType getSoundCard() - { - ArrayList soundDevices = this.vmConfig.getSoundDevices(); - SoundCardType soundDeviceType = SoundCardType.DEFAULT; - - if ( soundDevices.isEmpty() ) { - // the VM configuration does not contain a sound card device - soundDeviceType = SoundCardType.NONE; - } else { - // the VM configuration at least one sound card device, so return the type of the first one - Sound.Model soundDeviceModel = soundDevices.get( 0 ).getModel(); - soundDeviceType = QemuMetaDataUtils.convertSoundDeviceModel( soundDeviceModel ); - } - - return soundDeviceType; - } - - @Override - public void setDDAcceleration( DDAcceleration type ) - { - QemuDDAccelMeta accelerationConfig = this.ddacc.get( type ); - ArrayList graphicDevices = this.vmConfig.getGraphicDevices(); - ArrayList