summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java')
-rw-r--r--src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java903
1 files changed, 903 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java
new file mode 100644
index 0000000..6c62a96
--- /dev/null
+++ b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java
@@ -0,0 +1,903 @@
+package org.openslx.virtualization.configuration;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openslx.bwlp.thrift.iface.OperatingSystem;
+import org.openslx.firmware.FirmwareException;
+import org.openslx.firmware.QemuFirmwareUtil;
+import org.openslx.libvirt.domain.Domain;
+import org.openslx.libvirt.domain.DomainUtils;
+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.libosinfo.LibOsInfo;
+import org.openslx.libvirt.libosinfo.os.Os;
+import org.openslx.libvirt.xml.LibvirtXmlDocumentException;
+import org.openslx.libvirt.xml.LibvirtXmlSerializationException;
+import org.openslx.libvirt.xml.LibvirtXmlValidationException;
+import org.openslx.util.LevenshteinDistance;
+import org.openslx.util.Util;
+import org.openslx.virtualization.Version;
+import org.openslx.virtualization.hardware.VirtOptionValue;
+import org.openslx.virtualization.hardware.ConfigurationGroups;
+import org.openslx.virtualization.hardware.Ethernet;
+import org.openslx.virtualization.hardware.SoundCard;
+import org.openslx.virtualization.hardware.Usb;
+import org.openslx.virtualization.virtualizer.VirtualizerQemu;
+
+/**
+ * Virtual machine configuration (managed by Libvirt) for the QEMU hypervisor.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class VirtualizationConfigurationQemu extends VirtualizationConfiguration
+{
+ /**
+ * Name of the network bridge for the LAN.
+ */
+ public static final String NETWORK_BRIDGE_LAN_DEFAULT = "br0";
+
+ /**
+ * Name of the network bridge for the default NAT network.
+ */
+ public static final String NETWORK_BRIDGE_NAT_DEFAULT = "nat1";
+
+ /**
+ * Name of the network for the isolated host network (host only).
+ */
+ public static final String NETWORK_BRIDGE_HOST_ONLY_DEFAULT = "vsw2";
+
+ /**
+ * Default physical CDROM drive of the hypervisor host.
+ */
+ public static final String CDROM_DEFAULT_PHYSICAL_DRIVE = "/dev/sr0";
+
+ /**
+ * File name extension for QEMU (Libvirt) virtualization configuration files.
+ */
+ public static final String FILE_NAME_EXTENSION = "xml";
+
+ /**
+ * Libvirt XML configuration file to modify configuration of virtual machine for QEMU.
+ */
+ private Domain vmConfig = null;
+
+ /**
+ * 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 VirtualizationConfigurationException Libvirt XML configuration cannot be processed.
+ */
+ public VirtualizationConfigurationQemu( List<OperatingSystem> osList, File file )
+ throws VirtualizationConfigurationException
+ {
+ super( new VirtualizerQemu(), osList );
+
+ try {
+ // read and parse Libvirt domain XML configuration document
+ this.vmConfig = new Domain( file );
+ } catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) {
+ throw new VirtualizationConfigurationException( e.getLocalizedMessage() );
+ }
+
+ // parse VM config and initialize fields of QemuMetaData class
+ this.parseVmConfig();
+ }
+
+ /**
+ * Creates new virtual machine configuration (managed by Libvirt) for the QEMU hypervisor.
+ *
+ * @param osList list of operating systems.
+ * @param vmContent file content for the QEMU hypervisor.
+ * @param length number of bytes of the file content.
+ *
+ * @throws VirtualizationConfigurationException Libvirt XML configuration cannot be processed.
+ */
+ public VirtualizationConfigurationQemu( List<OperatingSystem> osList, byte[] vmContent, int length )
+ throws VirtualizationConfigurationException
+ {
+ super( new VirtualizerQemu(), osList );
+
+ try {
+ // read and parse Libvirt domain XML configuration document
+ this.vmConfig = new Domain( new String( vmContent, StandardCharsets.UTF_8 ) );
+ } catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) {
+ throw new VirtualizationConfigurationException( e.getLocalizedMessage() );
+ }
+
+ // parse VM config and initialize fields of QemuMetaData class
+ this.parseVmConfig();
+ }
+
+ /**
+ * Parses Libvirt domain XML configuration to initialize QEMU metadata.
+ */
+ private void parseVmConfig()
+ {
+ // 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 );
+ }
+
+ // detect the operating system from the optional embedded libosinfo metadata
+ this.setOs( this.vmConfig.getLibOsInfoOsId() );
+ }
+
+ /**
+ * 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 = VirtualizationConfigurationQemuUtils
+ .convertBusType( storageDiskDevice.getBusType() );
+ String hddImagePath = storageDiskDevice.getStorageSource();
+
+ this.hdds.add( new HardDisk( hddChipsetModel, hddChipsetBus, hddImagePath ) );
+ }
+
+ /**
+ * Detects the operating system by the specified libosinfo operating system identifier.
+ *
+ * @param osId libosinfo operating system identifier.
+ */
+ private OperatingSystem detectOperatingSystem( String osId )
+ {
+ OperatingSystem os = null;
+
+ if ( osId != null && !osId.isEmpty() ) {
+ // lookup operating system identifier in the libosinfo database
+ final Os osLookup = LibOsInfo.lookupOs( osId );
+
+ // check if entry in the database was found
+ if ( osLookup != null ) {
+ // operating system entry was found
+ // so determine OpenSLX OS name with the smallest distance to the libosinfo OS name
+ final LevenshteinDistance distance = new LevenshteinDistance( 2, 1, 1 );
+ int smallestDistance = Integer.MAX_VALUE;
+
+ // get name of the OS and combine it with the optional available architecture
+ String osLookupOsName = osLookup.getName();
+ final int osArchSize = VirtualizationConfigurationQemuUtils.getOsArchSize( this.vmConfig.getOsArch() );
+
+ if ( osArchSize > 0 ) {
+ // append architecture size in bit if information is available from the specified architecture
+ osLookupOsName += " (" + osArchSize + " Bit)";
+ }
+
+ for ( final OperatingSystem osCandidate : this.osList ) {
+ final int currentDistance = distance.calculateDistance( osLookupOsName, osCandidate.getOsName() );
+
+ if ( currentDistance < smallestDistance ) {
+ // if the distance is smaller save the current distance and operating system as best candidate
+ smallestDistance = currentDistance;
+ os = osCandidate;
+ }
+ }
+ }
+ }
+
+ return os;
+ }
+
+ @Override
+ public void transformEditable() throws VirtualizationConfigurationException
+ {
+ }
+
+ public void transformOsLoader() throws VirtualizationConfigurationException
+ {
+ final String sourceOsLoader = this.vmConfig.getOsLoader();
+ final String sourceOsArch = this.vmConfig.getOsArch();
+ final String sourceOsMachine = this.vmConfig.getOsMachine();
+
+ // transform OS loader for local editing
+ // check if OS loader is specified
+ if ( sourceOsLoader != null && !sourceOsLoader.isEmpty() ) {
+ // OS loader is specified so transform path to specified firmware path
+ // First, lookup QEMU firmware loader for target
+ String targetOsLoader = null;
+ try {
+ targetOsLoader = QemuFirmwareUtil.lookupTargetOsLoaderDefaultFwSpecDir( sourceOsLoader, sourceOsArch,
+ sourceOsMachine );
+ } catch ( FirmwareException e ) {
+ throw new VirtualizationConfigurationException( e.getLocalizedMessage() );
+ }
+
+ // Second, set target QEMU firmware loader if specified
+ if ( targetOsLoader != null && !targetOsLoader.isEmpty() ) {
+ this.vmConfig.setOsLoader( targetOsLoader );
+ }
+ }
+ }
+
+ @Override
+ public boolean addEmptyHddTemplate()
+ {
+ return this.addHddTemplate( "", null, null );
+ }
+
+ @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 )
+ {
+ int index = this.vmConfig.getDiskStorageDevices().size() - 1;
+ index = ( index > 0 ) ? index : 0;
+ return this.addHddTemplate( index, 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
+ * <code>hddMode</code> is set.
+ * @return result state of adding the HDD.
+ */
+ public boolean addHddTemplate( int index, String diskImagePath, String hddMode, String redoDir )
+ {
+ ArrayList<DiskStorage> storageDiskDevices = this.vmConfig.getDiskStorageDevices();
+ DiskStorage storageDiskDevice = VirtualizationConfigurationQemuUtils.getArrayIndex( storageDiskDevices, index );
+
+ if ( storageDiskDevice == null ) {
+ // HDD does not exist, so create new storage (HDD) device
+ final BusType devBusType = BusType.VIRTIO;
+ final String targetDevName = VirtualizationConfigurationQemuUtils.createDeviceName( this.vmConfig,
+ devBusType );
+ storageDiskDevice = this.vmConfig.addDiskStorageDevice();
+ storageDiskDevice.setReadOnly( false );
+ storageDiskDevice.setBusType( devBusType );
+ storageDiskDevice.setTargetDevice( targetDevName );
+
+ if ( diskImagePath == null || diskImagePath.isEmpty() ) {
+ storageDiskDevice.removeStorage();
+ } else {
+ 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
+ if ( diskImagePath == null || diskImagePath.isEmpty() ) {
+ storageDiskDevice.removeStorage();
+ } else {
+ storageDiskDevice.setStorage( StorageType.FILE, diskImagePath );
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean addDefaultNat()
+ {
+ // since network interface was not filtered during VM upload,
+ // do not add or configure any network interface here
+ return true;
+ }
+
+ @Override
+ public void setOs( String vendorOsId )
+ {
+ final OperatingSystem os = this.detectOperatingSystem( vendorOsId );
+ this.setOs( os );
+ }
+
+ @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 )
+ {
+ // convert given memory in MiB to memory in bytes for Libvirt XML Domain API functions
+ final BigInteger memory = DomainUtils.decodeMemory( Integer.toString( mem ), "MiB" );
+
+ this.vmConfig.setMemory( memory );
+ this.vmConfig.setCurrentMemory( memory );
+
+ final boolean isMemorySet = this.vmConfig.getMemory().equals( memory );
+ final boolean isCurrentMemorySet = this.vmConfig.getCurrentMemory().equals( memory );
+
+ return isMemorySet && isCurrentMemorySet;
+ }
+
+ @Override
+ public void addFloppy( int index, String image, boolean readOnly )
+ {
+ ArrayList<DiskFloppy> floppyDiskDevices = this.vmConfig.getDiskFloppyDevices();
+ DiskFloppy floppyDiskDevice = VirtualizationConfigurationQemuUtils.getArrayIndex( floppyDiskDevices, index );
+
+ if ( floppyDiskDevice == null ) {
+ // floppy device does not exist, so create new floppy device
+ final BusType devBusType = BusType.FDC;
+ final String targetDevName = VirtualizationConfigurationQemuUtils.createDeviceName( this.vmConfig,
+ devBusType );
+ floppyDiskDevice = this.vmConfig.addDiskFloppyDevice();
+ floppyDiskDevice.setBusType( devBusType );
+ floppyDiskDevice.setTargetDevice( targetDevName );
+ floppyDiskDevice.setReadOnly( readOnly );
+
+ if ( image == null || image.isEmpty() ) {
+ floppyDiskDevice.removeStorage();
+ } else {
+ floppyDiskDevice.setStorage( StorageType.FILE, image );
+ }
+ } else {
+ // floppy device exists, so update existing floppy device
+ floppyDiskDevice.setReadOnly( readOnly );
+
+ if ( image == null || image.isEmpty() ) {
+ floppyDiskDevice.removeStorage();
+ } else {
+ floppyDiskDevice.setStorage( StorageType.FILE, image );
+ }
+ }
+ }
+
+ @Override
+ public boolean addCdrom( String image )
+ {
+ int index = this.vmConfig.getDiskCdromDevices().size() - 1;
+ index = ( index > 0 ) ? index : 0;
+ return this.addCdrom( index, 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<DiskCdrom> cdromDiskDevices = this.vmConfig.getDiskCdromDevices();
+ DiskCdrom cdromDiskDevice = VirtualizationConfigurationQemuUtils.getArrayIndex( cdromDiskDevices, index );
+
+ if ( cdromDiskDevice == null ) {
+ // CDROM device does not exist, so create new CDROM device
+ final BusType devBusType = BusType.SATA;
+ final String targetDevName = VirtualizationConfigurationQemuUtils.createDeviceName( this.vmConfig,
+ devBusType );
+ cdromDiskDevice = this.vmConfig.addDiskCdromDevice();
+ cdromDiskDevice.setBusType( devBusType );
+ cdromDiskDevice.setTargetDevice( targetDevName );
+ cdromDiskDevice.setReadOnly( true );
+
+ if ( image == null ) {
+ cdromDiskDevice.setStorage( StorageType.BLOCK, CDROM_DEFAULT_PHYSICAL_DRIVE );
+ } else {
+ if ( image.isEmpty() ) {
+ cdromDiskDevice.removeStorage();
+ } 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 {
+ if ( image.isEmpty() ) {
+ cdromDiskDevice.removeStorage();
+ } else {
+ cdromDiskDevice.setStorage( StorageType.FILE, image );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean addCpuCoreCount( int nrOfCores )
+ {
+ this.vmConfig.setVCpu( nrOfCores );
+
+ boolean isVCpuSet = this.vmConfig.getVCpu() == nrOfCores;
+
+ return isVCpuSet;
+ }
+
+ class QemuGfxModel extends VirtOptionValue
+ {
+ public QemuGfxModel( Video.Model model, String displayName )
+ {
+ super( model.toString(), displayName );
+ }
+
+ @Override
+ public void apply()
+ {
+ final ArrayList<Video> videoDevices = vmConfig.getVideoDevices();
+
+ if ( videoDevices.isEmpty() ) {
+ // add new video device with disabled acceleration to VM configuration
+ final Video videoDevice = vmConfig.addVideoDevice();
+ videoDevice.setModel( Video.Model.fromString( this.getId() ) );
+ videoDevice.set2DAcceleration( false );
+ videoDevice.set3DAcceleration( false );
+ } else {
+ // change graphics model of existing video devices
+ for ( final Video videoDevice : videoDevices ) {
+ // remove all old model-related XML attributes
+ videoDevice.removeXmlElement( "model" );
+ // set new model
+ videoDevice.setModel( Video.Model.fromString( this.getId() ) );
+ }
+ }
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ final ArrayList<Video> videoDevices = vmConfig.getVideoDevices();
+ boolean isActive = true;
+
+ for ( final Video videoDevice : videoDevices ) {
+ if ( !videoDevice.getModel().toString().equals( this.getId() ) ) {
+ isActive = false;
+ break;
+ }
+ }
+
+ return isActive;
+ }
+ }
+
+ class QemuGfxType extends VirtOptionValue
+ {
+ public QemuGfxType( String id, String displayName )
+ {
+ super( id, displayName );
+ }
+
+ @Override
+ public void apply()
+ {
+ ArrayList<Graphics> graphicDevices = vmConfig.getGraphicDevices();
+ ArrayList<Video> videoDevices = vmConfig.getVideoDevices();
+ final boolean accelerationEnabled = this.id.equals( "true" );
+
+ boolean acceleratedGraphicsAvailable = false;
+
+ if ( graphicDevices.isEmpty() ) {
+ // add new graphics device with enabled acceleration to VM configuration
+ GraphicsSpice graphicSpiceDevice = vmConfig.addGraphicsSpiceDevice();
+ graphicSpiceDevice.setOpenGl( true );
+ acceleratedGraphicsAvailable = true;
+ } else {
+ // enable graphic acceleration of existing graphics devices
+ for ( Graphics graphicDevice : graphicDevices ) {
+ // set hardware acceleration for SPICE based graphics output
+ // other graphic devices do not support hardware acceleration
+ if ( graphicDevice instanceof GraphicsSpice ) {
+ GraphicsSpice.class.cast( graphicDevice ).setOpenGl( true );
+ acceleratedGraphicsAvailable = true;
+ }
+ }
+ }
+
+ // only configure hardware acceleration of video card(s) if graphics with hardware acceleration is available
+ if ( acceleratedGraphicsAvailable ) {
+ if ( videoDevices.isEmpty() ) {
+ // add new video device with enabled acceleration to VM configuration
+ Video videoDevice = vmConfig.addVideoDevice();
+ videoDevice.setModel( Video.Model.VIRTIO );
+ videoDevice.set2DAcceleration( true );
+ videoDevice.set3DAcceleration( true );
+ } else {
+ // enable graphic acceleration of existing graphics and video devices
+ for ( Video videoDevice : videoDevices ) {
+ // set hardware acceleration for Virtio GPUs
+ // other GPUs do not support hardware acceleration
+ if ( videoDevice.getModel() == Video.Model.VIRTIO ) {
+ videoDevice.set2DAcceleration( accelerationEnabled );
+ videoDevice.set3DAcceleration( accelerationEnabled );
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ ArrayList<Graphics> graphicsDevices = vmConfig.getGraphicDevices();
+ ArrayList<Video> videoDevices = vmConfig.getVideoDevices();
+
+ boolean acceleratedGraphicsAvailable = false;
+ boolean acceleratedVideoDevAvailable = false;
+
+ // search for hardware accelerated graphics
+ for ( Graphics graphicDevice : graphicsDevices ) {
+ // only SPICE based graphic devices support hardware acceleration
+ if ( graphicDevice instanceof GraphicsSpice ) {
+ acceleratedGraphicsAvailable = true;
+ break;
+ }
+ }
+
+ // search for hardware accelerated video devices
+ for ( Video videoDevice : videoDevices ) {
+ // only Virtio based video devices support hardware acceleration
+ if ( videoDevice.getModel() == Video.Model.VIRTIO ) {
+ acceleratedVideoDevAvailable = true;
+ break;
+ }
+ }
+
+ // hardware acceleration is available if at least one accelerated graphics and video device is available
+ if ( acceleratedGraphicsAvailable && acceleratedVideoDevAvailable ) {
+ return this.id.equals( "true" );
+ } else {
+ return this.id.equals( "false" );
+ }
+ }
+ }
+
+ @Override
+ public void setVirtualizerVersion( Version type )
+ {
+ if ( type != null ) {
+ final String osMachine = this.vmConfig.getOsMachine();
+ final String osMachineName = VirtualizationConfigurationQemuUtils.getOsMachineName( osMachine );
+
+ if ( osMachineName != null && !osMachineName.isEmpty() ) {
+ final String modifiedOsMachineVersion = VirtualizationConfigurationQemuUtils.getOsMachineVersion( type );
+ final String modifiedOsMachine = VirtualizationConfigurationQemuUtils.getOsMachine( osMachineName,
+ modifiedOsMachineVersion );
+ this.vmConfig.setOsMachine( modifiedOsMachine );
+ }
+ }
+ }
+
+ @Override
+ public Version getVirtualizerVersion()
+ {
+ final String osMachine = this.vmConfig.getOsMachine();
+ final Version uncheckedVersion = VirtualizationConfigurationQemuUtils.getOsMachineVersion( osMachine );
+ final Version checkedVersion;
+
+ if ( uncheckedVersion == null ) {
+ checkedVersion = null;
+ } else {
+ checkedVersion = Version.getInstanceByMajorMinorFromVersions( uncheckedVersion.getMajor(),
+ uncheckedVersion.getMinor(), this.getVirtualizer().getSupportedVersions() );
+ }
+
+ return checkedVersion;
+ }
+
+ class QemuNicModel extends VirtOptionValue
+ {
+ private final int cardIndex;
+
+ public QemuNicModel( int cardIndex, Interface.Model model, String displayName )
+ {
+ super( model.toString(), displayName ); // XXX: toString/fromString would disappear if
+ this.cardIndex = cardIndex; // this were AbstractConfigurableOption<T extends Enum<T>>
+ }
+
+ @Override
+ public void apply()
+ {
+ ArrayList<Interface> networkDevices = vmConfig.getInterfaceDevices();
+ Interface networkDevice = VirtualizationConfigurationQemuUtils.getArrayIndex( networkDevices, cardIndex );
+
+ if ( networkDevice != null ) {
+ networkDevice.setModel( Interface.Model.fromString( id ) );
+ }
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ ArrayList<Interface> networkDevices = vmConfig.getInterfaceDevices();
+ Interface networkDevice = VirtualizationConfigurationQemuUtils.getArrayIndex( networkDevices, cardIndex );
+
+ if ( networkDevice == null ) {
+ // network interface device is not present
+ return Util.isEmptyString( this.id ); // XXX: would be more explicit with enum.NONE
+ }
+ // get model of existing network interface device
+ Interface.Model networkDeviceModel = networkDevice.getModel();
+ if ( networkDeviceModel == null ) {
+ return Util.isEmptyString( this.id ); // see above
+ }
+ // Success
+ return networkDeviceModel.toString().equals( this.id ); // XXX: enum would allow simple ==
+ }
+ }
+
+ class QemuSoundCardModel extends VirtOptionValue
+ {
+ public QemuSoundCardModel( Sound.Model id, String displayName )
+ {
+ super( id.toString(), displayName );
+ }
+
+ @Override
+ public void apply()
+ {
+ ArrayList<Sound> soundDevices = vmConfig.getSoundDevices();
+ Sound.Model soundDeviceModel = Sound.Model.fromString( this.id );
+
+ if ( soundDevices.isEmpty() ) {
+ // create new sound device with 'soundDeviceModel' hardware
+ Sound soundDevice = vmConfig.addSoundDevice();
+ soundDevice.setModel( soundDeviceModel );
+ } else {
+ // update sound device model type of existing sound devices
+ for ( Sound soundDevice : soundDevices ) {
+ soundDevice.setModel( soundDeviceModel );
+ }
+ }
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ ArrayList<Sound> soundDevices = vmConfig.getSoundDevices();
+
+ if ( soundDevices.isEmpty() ) {
+ // the VM configuration does not contain a sound card device
+ return Util.isEmptyString( this.id );
+ }
+ // the VM configuration at least one sound card device, so return the type of the first one
+ Sound.Model soundDeviceModel = soundDevices.get( 0 ).getModel();
+ return soundDeviceModel != null && soundDeviceModel.toString().equals( this.id );
+ }
+
+ }
+
+ class QemuUsbSpeed extends VirtOptionValue
+ {
+ public QemuUsbSpeed( ControllerUsb.Model id, String displayName )
+ {
+ super( id.toString(), displayName );
+ }
+
+ @Override
+ public void apply()
+ {
+ ArrayList<ControllerUsb> usbControllerDevices = vmConfig.getUsbControllerDevices();
+ ControllerUsb.Model usbControllerModel = ControllerUsb.Model.fromString( this.id );
+
+ if ( usbControllerDevices.isEmpty() ) {
+ // add new USB controller with specified speed 'usbControllerModel'
+ ControllerUsb usbControllerDevice = vmConfig.addControllerUsbDevice();
+ usbControllerDevice.setModel( usbControllerModel );
+ } else {
+ // update model of all USB controller devices to support the maximum speed
+ for ( ControllerUsb usbControllerDevice : usbControllerDevices ) {
+ usbControllerDevice.setModel( usbControllerModel );
+ }
+ }
+ }
+
+ @Override
+ public boolean isActive()
+ {
+ ArrayList<ControllerUsb> usbControllerDevices = vmConfig.getUsbControllerDevices();
+
+ for ( ControllerUsb usbControllerDevice : usbControllerDevices ) {
+ ControllerUsb.Model usbControllerModel = usbControllerDevice.getModel();
+
+ // TODO Need something to map from chip to usb speed. But this is conceptually broken anyways since
+ // it's modeled after vmware, where you only cannot configure different controllers at the same time
+ // anyways XXX
+ if ( usbControllerModel.toString().equals( this.id ) )
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ @Override
+ public byte[] getConfigurationAsByteArray()
+ {
+ String configuration = this.vmConfig.toString();
+
+ if ( configuration == null ) {
+ return null;
+ } else {
+ // append newline at the end of the XML content to match the structure of an original Libvirt XML file
+ //configuration += LibvirtXmlDocument.LINE_SEPARATOR;
+ return configuration.getBytes( StandardCharsets.UTF_8 );
+ }
+ }
+
+ @Override
+ public boolean addEthernet( EtherType type )
+ {
+ int index = this.vmConfig.getInterfaceDevices().size() - 1;
+ index = ( index > 0 ) ? index : 0;
+ return this.addEthernet( index, type );
+ }
+
+ /**
+ * Adds an ethernet card to the QEMU virtual machine configuration.
+ *
+ * @param index current index of the ethernet card to be added to the virtual machine
+ * configuration.
+ * @param type card model of the ethernet card.
+ * @return result state of adding the ethernet card.
+ */
+ public boolean addEthernet( int index, EtherType type )
+ {
+ ArrayList<Interface> interfaceDevices = this.vmConfig.getInterfaceDevices();
+ Interface interfaceDevice = VirtualizationConfigurationQemuUtils.getArrayIndex( interfaceDevices, index );
+
+ final Interface.Model defaultNetworkDeviceModel = Interface.Model.VIRTIO_NET_PCI;
+
+ if ( interfaceDevice == null ) {
+ // network interface device does not exist, so create new network interface device
+ switch ( type ) {
+ case BRIDGED:
+ // add network bridge interface device
+ interfaceDevice = this.vmConfig.addInterfaceBridgeDevice();
+ interfaceDevice.setModel( defaultNetworkDeviceModel );
+ interfaceDevice.setSource( VirtualizationConfigurationQemu.NETWORK_BRIDGE_LAN_DEFAULT );
+ break;
+ case HOST_ONLY:
+ // add network interface device with link to the isolated host network
+ interfaceDevice = this.vmConfig.addInterfaceBridgeDevice();
+ interfaceDevice.setModel( defaultNetworkDeviceModel );
+ interfaceDevice.setSource( VirtualizationConfigurationQemu.NETWORK_BRIDGE_HOST_ONLY_DEFAULT );
+ break;
+ case NAT:
+ // add network interface device with link to the NAT network
+ interfaceDevice = this.vmConfig.addInterfaceBridgeDevice();
+ interfaceDevice.setModel( defaultNetworkDeviceModel );
+ interfaceDevice.setSource( VirtualizationConfigurationQemu.NETWORK_BRIDGE_NAT_DEFAULT );
+ break;
+ }
+ } else {
+ // network interface device exists, so update existing network interface device
+ switch ( type ) {
+ case BRIDGED:
+ interfaceDevice.setType( Interface.Type.BRIDGE );
+ interfaceDevice.setSource( VirtualizationConfigurationQemu.NETWORK_BRIDGE_LAN_DEFAULT );
+ break;
+ case HOST_ONLY:
+ interfaceDevice.setType( Interface.Type.BRIDGE );
+ interfaceDevice.setSource( VirtualizationConfigurationQemu.NETWORK_BRIDGE_HOST_ONLY_DEFAULT );
+ break;
+ case NAT:
+ interfaceDevice.setType( Interface.Type.BRIDGE );
+ interfaceDevice.setSource( VirtualizationConfigurationQemu.NETWORK_BRIDGE_NAT_DEFAULT );
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void transformNonPersistent() throws VirtualizationConfigurationException
+ {
+ // NOT implemented yet
+ }
+
+ @Override
+ public void transformPrivacy() throws VirtualizationConfigurationException
+ {
+ // removes all referenced storage files of all specified CDROMs, Floppy drives and HDDs
+ this.vmConfig.removeDiskDevicesStorage();
+
+ // remove specified NVRAM file of OS loader (firmware)
+ this.vmConfig.removeOsNvram();
+ }
+
+ @Override
+ public void registerVirtualHW()
+ {
+ // XXX Add missing qemu-only types/models
+ List<VirtOptionValue> list;
+ // @formatter:off
+ list = new ArrayList<>();
+ //list.add( new QemuSoundCardModel( Sound.Model.NONE, SoundCard.NONE ) ); // XXX TODO
+ list.add( new QemuSoundCardModel( Sound.Model.ICH9, SoundCard.DEFAULT ) );
+ list.add( new QemuSoundCardModel( Sound.Model.SB16, SoundCard.SOUND_BLASTER ) );
+ list.add( new QemuSoundCardModel( Sound.Model.ES1370, SoundCard.ES ) );
+ list.add( new QemuSoundCardModel( Sound.Model.AC97, SoundCard.AC ) );
+ list.add( new QemuSoundCardModel( Sound.Model.ICH9, SoundCard.HD_AUDIO ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.SOUND_CARD_MODEL, list ) );
+
+ list = new ArrayList<>();
+ list.add( new QemuGfxModel( Video.Model.VGA, "VGA" ) );
+ list.add( new QemuGfxModel( Video.Model.QXL, "QXL" ) );
+ list.add( new QemuGfxModel( Video.Model.VMVGA, "VMware VGA" ) );
+ list.add( new QemuGfxModel( Video.Model.VIRTIO, "virtio-gpu" ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.GFX_MODEL, list ) );
+
+ list = new ArrayList<>();
+ list.add( new QemuGfxType( "false", "2D" ) );
+ list.add( new QemuGfxType( "true", "3D" ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.GFX_TYPE, list ) );
+
+ list = new ArrayList<>();
+ // XXX Represent NONE; can add missing models now with new approach (add human readable strings)
+ list.add( new QemuNicModel( 0, Interface.Model.VIRTIO, Ethernet.AUTO ) );
+ list.add( new QemuNicModel( 0, Interface.Model.PCNET, Ethernet.PCNETPCI2 ) );
+ list.add( new QemuNicModel( 0, Interface.Model.E1000, Ethernet.E1000 ) );
+ list.add( new QemuNicModel( 0, Interface.Model.E1000E, Ethernet.E1000E ) );
+ list.add( new QemuNicModel( 0, Interface.Model.VMXNET3, Ethernet.VMXNET3 ) );
+ list.add( new QemuNicModel( 0, Interface.Model.VIRTIO_NET_PCI, Ethernet.PARAVIRT ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.NIC_MODEL, list ) );
+
+ list = new ArrayList<>();
+ list.add( new QemuUsbSpeed( ControllerUsb.Model.NONE, Usb.NONE ) );
+ list.add( new QemuUsbSpeed( ControllerUsb.Model.ICH9_UHCI1, Usb.USB1_1 ) );
+ list.add( new QemuUsbSpeed( ControllerUsb.Model.ICH9_EHCI1, Usb.USB2_0 ) );
+ list.add( new QemuUsbSpeed( ControllerUsb.Model.QEMU_XHCI, Usb.USB3_0 ) );
+ configurableOptions.add( new ConfigurableOptionGroup( ConfigurationGroups.USB_SPEED, list ) );
+ // @formatter:on
+ }
+
+ @Override
+ public String getFileNameExtension()
+ {
+ return VirtualizationConfigurationQemu.FILE_NAME_EXTENSION;
+ }
+
+ @Override
+ public void validate() throws VirtualizationConfigurationException
+ {
+ try {
+ this.vmConfig.validateXml();
+ } catch ( LibvirtXmlValidationException e ) {
+ throw new VirtualizationConfigurationException( e.getLocalizedMessage() );
+ }
+ }
+
+ @Override
+ public void disableUsb()
+ {
+ new QemuUsbSpeed( ControllerUsb.Model.NONE, Usb.NONE ).apply();
+ }
+}