From aac457735f24129b9113c53ae93a1a11f524b6c0 Mon Sep 17 00:00:00 2001
From: Manuel Bentele
Date: Fri, 8 Jan 2021 13:44:15 +0100
Subject: Add support for QCOW2 images for QEMU
---
src/main/java/org/openslx/util/vm/DiskImage.java | 156 +++++++++++++++++++++--
1 file changed, 146 insertions(+), 10 deletions(-)
(limited to 'src/main/java/org/openslx/util/vm')
diff --git a/src/main/java/org/openslx/util/vm/DiskImage.java b/src/main/java/org/openslx/util/vm/DiskImage.java
index 15b3800..1602926 100644
--- a/src/main/java/org/openslx/util/vm/DiskImage.java
+++ b/src/main/java/org/openslx/util/vm/DiskImage.java
@@ -26,8 +26,7 @@ public class DiskImage
public enum ImageFormat
{
- VMDK( "vmdk" ), QCOW2( "qcow2" ), VDI( "vdi" ),
- DOCKER("dockerfile");
+ VMDK( "vmdk" ), QCOW2( "qcow2" ), VDI( "vdi" ), DOCKER( "dockerfile" );
public final String extension;
@@ -53,7 +52,7 @@ public class DiskImage
return VDI;
if ( virtId.equals( TConst.VIRT_QEMU ) )
return QCOW2;
- if ( virtId.equals( TConst.VIRT_DOCKER) )
+ if ( virtId.equals( TConst.VIRT_DOCKER ) )
return DOCKER;
return null;
}
@@ -166,17 +165,154 @@ public class DiskImage
return;
}
- // TODO: qcow
+ // qcow2 disk image
file.seek( 0 );
if ( file.readInt() == QEMU_MAGIC ) {
- // dummy values
- this.isStandalone = true;
- this.isCompressed = false;
- this.isSnapshot = false;
+ //
+ // 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.subFormat = null;
this.diskDescription = null;
- this.hwVersion = 0;
+
return;
}
}
--
cgit v1.2.3-55-g7522
From 706910e440527c22f176bcab0032c37c60357c25 Mon Sep 17 00:00:00 2001
From: Manuel Bentele
Date: Fri, 29 Jan 2021 12:25:44 +0100
Subject: Add support for QEMU VMs (based on Libvirt domain XML documents)
---
pom.xml | 27 +-
src/main/java/org/openslx/util/vm/DiskImage.java | 35 +
.../org/openslx/util/vm/DockerMetaDataDummy.java | 16 +
.../java/org/openslx/util/vm/QemuMetaData.java | 796 ++++++++++++++++++---
.../org/openslx/util/vm/QemuMetaDataUtils.java | 188 +++++
.../java/org/openslx/util/vm/VboxMetaData.java | 14 +
src/main/java/org/openslx/util/vm/VmMetaData.java | 9 +-
.../java/org/openslx/util/vm/VmwareMetaData.java | 16 +-
.../libvirt/xsl/xml-output-transformation.xsl | 12 +
.../java/org/openslx/util/vm/QemuMetaDataTest.java | 466 ++++++++++++
10 files changed, 1487 insertions(+), 92 deletions(-)
create mode 100644 src/main/java/org/openslx/util/vm/QemuMetaDataUtils.java
create mode 100644 src/main/resources/libvirt/xsl/xml-output-transformation.xsl
create mode 100644 src/test/java/org/openslx/util/vm/QemuMetaDataTest.java
(limited to 'src/main/java/org/openslx/util/vm')
diff --git a/pom.xml b/pom.xml
index 6daf903..4b8b3dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,11 +82,21 @@
3.0.0-M5
+
+
+ ${basedir}/src/main/resources
+
+ libvirt/rng/*
+ libvirt/xsl/*
+
+
+
${basedir}/src/test/resources
disk/*
+ libvirt/xml/*
@@ -111,6 +121,12 @@
5.5.2
test
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.5.2
+ test
+
log4j
log4j
@@ -140,9 +156,14 @@
2.8.0
- com.google.jimfs
- jimfs
- 1.1
+ xalan
+ xalan
+ 2.7.2
+
+
+ org.relaxng
+ jing
+ 20181222
diff --git a/src/main/java/org/openslx/util/vm/DiskImage.java b/src/main/java/org/openslx/util/vm/DiskImage.java
index 1602926..617fadd 100644
--- a/src/main/java/org/openslx/util/vm/DiskImage.java
+++ b/src/main/java/org/openslx/util/vm/DiskImage.java
@@ -5,6 +5,8 @@ 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;
@@ -318,7 +320,40 @@ public class DiskImage
}
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 ) {
diff --git a/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java b/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java
index 3ee964f..9698c52 100644
--- a/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java
+++ b/src/main/java/org/openslx/util/vm/DockerMetaDataDummy.java
@@ -3,13 +3,23 @@ package org.openslx.util.vm;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.thrifthelper.TConst;
+import org.openslx.util.vm.DiskImage.ImageFormat;
import java.io.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
public class DockerMetaDataDummy extends VmMetaData {
// TODO Define DOCKER CONSTANT
+ /**
+ * 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" );
@@ -41,6 +51,12 @@ public class DockerMetaDataDummy extends VmMetaData {
@Override public byte[] getFilteredDefinitionArray() {
return dockerfile;
}
+
+ @Override
+ public List getSupportedImageFormats()
+ {
+ return DockerMetaDataDummy.SUPPORTED_IMAGE_FORMATS;
+ }
@Override public void applySettingsForLocalEdit() {
diff --git a/src/main/java/org/openslx/util/vm/QemuMetaData.java b/src/main/java/org/openslx/util/vm/QemuMetaData.java
index 201ffd8..1e5dd33 100644
--- a/src/main/java/org/openslx/util/vm/QemuMetaData.java
+++ b/src/main/java/org/openslx/util/vm/QemuMetaData.java
@@ -1,233 +1,855 @@
package org.openslx.util.vm;
import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
+import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
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;
+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;
-import org.openslx.util.vm.DiskImage.UnknownImageFormatException;
-public class QemuMetaData extends VmMetaData
+/**
+ * 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;
+ }
- private final Map arguments = new HashMap();
- // the above map's elements will take the place of in the config string
- private String config;
- private static final Logger LOGGER = Logger.getLogger( QemuMetaData.class );
+ /**
+ * Returns hardware model of the QEMU sound card.
+ *
+ * @return hardware model of the QEMU sound card.
+ */
+ public Sound.Model getModel()
+ {
+ return this.model;
+ }
+}
- private static final Virtualizer virtualizer = new Virtualizer( TConst.VIRT_QEMU, "QEMU-KVM" );
+/**
+ * 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;
+ }
- public QemuMetaData( List osList, File file ) throws FileNotFoundException, IOException, UnsupportedVirtualizerFormatException
+ /**
+ * 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 );
- DiskImage di;
+
try {
- di = new DiskImage( file );
- } catch ( UnknownImageFormatException e ) {
- di = null;
+ // 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() );
}
- if ( di == null || di.format != ImageFormat.QCOW2 ) {
- throw new UnsupportedVirtualizerFormatException( "This is not a qcow2 disk image" );
+
+ // 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 );
}
- config = "qemu-system-i386 -enable-kvm\nqemu-system-x86_64 -enable-kvm";
- displayName = file.getName().substring( 0, file.getName().indexOf( "." ) );
- setOs( "anyOs" );
- hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, file.getAbsolutePath() ) );
- makeStartSequence();
}
- // initiates the arguments map with a default working sequence that will later be used in the definition array
- public void makeStartSequence()
+ /**
+ * 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 )
{
- arguments.put( "cpu", "host" );
- arguments.put( "smp", "2" );
- arguments.put( "m", "1024" );
- arguments.put( "vga", "std" );
+ String hddChipsetModel = null;
+ DriveBusType hddChipsetBus = QemuMetaDataUtils.convertBusType( storageDiskDevice.getBusType() );
+ String hddImagePath = storageDiskDevice.getStorageSource();
+
+ this.hdds.add( new HardDisk( hddChipsetModel, hddChipsetBus, hddImagePath ) );
}
- private String configWithArgs()
+ @Override
+ public byte[] getFilteredDefinitionArray()
{
- String tempString = "";
- for ( String key : arguments.keySet() ) {
- tempString += "-" + key + " " + arguments.get( key ) + " ";
- }
- return config.replaceAll( "", tempString );
+ // 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 byte[] getFilteredDefinitionArray()
+ public List getSupportedImageFormats()
{
- return configWithArgs().getBytes( StandardCharsets.UTF_8 );
+ return QemuMetaData.SUPPORTED_IMAGE_FORMATS;
}
@Override
public void applySettingsForLocalEdit()
{
+ // NOT implemented yet
}
@Override
public boolean addHddTemplate( File diskImage, String hddMode, String redoDir )
{
- String tempS = config.replaceAll( "", diskImage.getAbsolutePath() );
- config = tempS;
- hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, diskImage.getAbsolutePath() ) );
- return true;
+ return this.addHddTemplate( diskImage.getAbsolutePath(), hddMode, redoDir );
}
@Override
public boolean addHddTemplate( String diskImagePath, String hddMode, String redoDir )
{
- String tempS = config.replaceAll( "", diskImagePath );
- config = tempS;
- hdds.add( new HardDisk( "anychipset", DriveBusType.IDE, diskImagePath ) );
- return true;
+ 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 true;
+ return this.addEthernet( EtherType.NAT );
}
@Override
public void setOs( String vendorOsId )
{
- setOs( TConst.VIRT_QEMU, vendorOsId );
+ this.setOs( vendorOsId );
}
@Override
public boolean addDisplayName( String name )
{
- // TODO Auto-generated method stub
- return false;
+ this.vmConfig.setName( name );
+
+ final boolean statusName = this.vmConfig.getName().equals( name );
+
+ return statusName;
}
@Override
public boolean addRam( int mem )
{
- this.arguments.put( "m", Integer.toString( mem ) );
- return true;
+ 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 )
{
- // TODO Auto-generated method stub
-
+ 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 )
{
- // TODO Auto-generated method stub
+ 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.arguments.put( "smp", Integer.toString( nrOfCores ) );
- return true;
+ this.vmConfig.setVCpu( nrOfCores );
+
+ boolean isVCpuSet = this.vmConfig.getVCpu() == nrOfCores;
+
+ return isVCpuSet;
}
@Override
- public void setSoundCard( VmMetaData.SoundCardType type )
+ 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 VmMetaData.SoundCardType getSoundCard()
+ public SoundCardType getSoundCard()
{
- return null;
+ 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( VmMetaData.DDAcceleration type )
+ public void setDDAcceleration( DDAcceleration type )
{
+ QemuDDAccelMeta accelerationConfig = this.ddacc.get( type );
+ ArrayList graphicDevices = this.vmConfig.getGraphicDevices();
+ ArrayList