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() );
}
}
}