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.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.vm.disk.DiskImage; import org.openslx.vm.disk.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 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"; /** * 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 VirtualizationConfigurationException Libvirt XML configuration cannot be processed. */ public VirtualizationConfigurationQemu( List osList, File file ) throws VirtualizationConfigurationException { super( 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 osList, byte[] vmContent, int length ) throws VirtualizationConfigurationException { super( osList ); try { // read and parse Libvirt domain XML configuration document this.vmConfig = new Domain( new String( vmContent ) ); } 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 ); } // start of privacy filters to filter out sensitive information like name of users in absolute paths, ... // removes all referenced storage files of all specified CDROMs, Floppy drives and HDDs this.vmConfig.removeDiskDevicesStorage(); } /** * 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 ) ); } @Override public byte[] getFilteredDefinitionArray() { // removes all specified boot order entries this.vmConfig.removeBootOrder(); // 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 VirtualizationConfigurationQemu.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 = VirtualizationConfigurationQemuUtils.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 = VirtualizationConfigurationQemuUtils.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 = VirtualizationConfigurationQemuUtils.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 = VirtualizationConfigurationQemuUtils.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 = VirtualizationConfigurationQemuUtils.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 = VirtualizationConfigurationQemuUtils.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 = VirtualizationConfigurationQemuUtils.convertSoundDeviceModel( soundDeviceModel ); } return soundDeviceType; } @Override public void setDDAcceleration( DDAcceleration type ) { QemuDDAccelMeta accelerationConfig = this.ddacc.get( type ); ArrayList graphicDevices = this.vmConfig.getGraphicDevices(); ArrayList