summaryrefslogblamecommitdiffstats
path: root/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java
blob: 5ea7b7206fe745d05510daa9b706877bda300c5c (plain) (tree)
1
2
3
4
5
6
7
8

                                        
                    
                        
 


                                          



                                                                


                                                                         











                                                                                             
                                                                                                    

                                                                                            

                                                  
                                                           
                                                    
                                                   



                                                                         

                                                                                       
 
   



                                                                                           


                


                                                                       
                                                                     



                                                                              
                                                                                                                    



                                                                                          
                                                                                                               










                                                                                                     

                                                





















                                                                                                               
 




















                                                                                                                                 
















                                                                                                            


                                                                                                                    

                                                                                                                              
                                                                                                                                      



                                                             

                                                          

















                                                                                                                           
                                                                 








                                                                                                                  
                                           



                                                                                                 



                                           

                                                               


                                                                                                          
                        







                                                                                                          


                                                                            



                                                                                           









                                                                                            
                                         


                                                 

                                   

                                                   









































                                                                                                                                  
                                                     

                                                                                               





                                                                                                       
                                
                                                                                                

                         
                                                                                                                
                 

         
package org.openslx.runvirt.plugin.qemu;

import java.io.File;
import java.util.Arrays;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.openslx.libvirt.domain.Domain;
import org.openslx.libvirt.xml.LibvirtXmlDocumentException;
import org.openslx.libvirt.xml.LibvirtXmlSerializationException;
import org.openslx.libvirt.xml.LibvirtXmlValidationException;
import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs;
import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs.CmdLnOption;
import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgsException;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericCpu;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericDiskCdromDevices;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericDiskFloppyDevices;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericDiskStorageDevices;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericFileSystemDevices;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericInterfaceDevices;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericMemory;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericName;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericParallelDevices;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuSerialDevices;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericUuid;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuArchitecture;
import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuGpuPassthroughNvidia;
import org.openslx.runvirt.plugin.qemu.virtualization.LibvirtHypervisorQemu;
import org.openslx.runvirt.plugin.qemu.virtualization.LibvirtHypervisorQemu.QemuSessionType;
import org.openslx.runvirt.viewer.Viewer;
import org.openslx.runvirt.viewer.ViewerException;
import org.openslx.runvirt.viewer.ViewerLookingGlassClient;
import org.openslx.runvirt.viewer.ViewerVirtManager;
import org.openslx.runvirt.viewer.ViewerVirtViewer;
import org.openslx.runvirt.virtualization.LibvirtHypervisor;
import org.openslx.runvirt.virtualization.LibvirtHypervisorException;
import org.openslx.runvirt.virtualization.LibvirtVirtualMachine;
import org.openslx.runvirt.virtualization.LibvirtVirtualMachineException;
import org.openslx.virtualization.configuration.transformation.TransformationException;
import org.openslx.virtualization.configuration.transformation.TransformationManager;

/**
 * Run-virt QEMU plugin (command line tool) to finalize a Libvirt domain XML configuration.
 * 
 * @author Manuel Bentele
 * @version 1.0
 */
public class App
{
	/**
	 * Stores name of the run-virt QEMU plugin (command line tool).
	 */
	public static final String APP_NAME = "run-virt QEMU plugin";

	/**
	 * Stores description of the run-virt QEMU plugin (command line tool).
	 */
	public static final String APP_DESC = "Finalize a Libvirt VM (domain XML) configuration and manage the VM.";

	/**
	 * Stores additional information for the run-virt QEMU plugin (command line tool).
	 */
	public static final String APP_INFO = "The " + APP_NAME + " is part of the bwLehrpool infrastructure.";

	/**
	 * Instance of a logger to log messages.
	 */
	private static final Logger LOGGER = LogManager.getLogger( App.class );

	/**
	 * Entry point of the run-virt QEMU plugin (command line tool).
	 * 
	 * @param args command line arguments passed to the run-virt QEMU plugin (command line tool).
	 */
	public static void main( String[] args )
	{
		// initialize logging
		BasicConfigurator.configure();

		// parse command line arguments
		CommandLineArgs cmdLn = new CommandLineArgs();

		try {
			cmdLn.parseCmdLnArgs( args );
		} catch ( CommandLineArgsException e ) {
			LOGGER.error( "Parsing of command line arguments failed: " + e.getLocalizedMessage() );
			App.printUsage( cmdLn );
			System.exit( 1 );
		}

		// show help if 'help' command line option is set
		if ( cmdLn.isHelpAquired() ) {
			App.printUsage( cmdLn );
			System.exit( 0 );
		}

		// print command line arguments for debugging purposes
		App.printCmdLnArgs( cmdLn );

		// create connection to the QEMU hypervisor via Libvirt
		LibvirtHypervisor hypervisor = null;
		try {
			hypervisor = new LibvirtHypervisorQemu( QemuSessionType.LOCAL_USER_SESSION );
		} catch ( LibvirtHypervisorException e ) {
			LOGGER.error( "Failed to connect to the QEMU virtualizer (Libvirt daemon): " + e.getLocalizedMessage() );
			System.exit( 2 );
		}

		// read Libvirt XML domain configuration template
		final String xmlInputFileName = cmdLn.getVmCfgInpFileName();
		Domain config = null;
		try {
			final File xmlInputFile = new File( xmlInputFileName );
			config = new Domain( xmlInputFile );
		} catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) {
			LOGGER.error( "Failed to read VM input configuration file: " + e.getLocalizedMessage() );
			hypervisor.close();
			System.exit( 3 );
		}

		// create transformation manager to finalize VM configuration
		final TransformationManager<Domain, CommandLineArgs> transformationManager;
		transformationManager = new TransformationManager<Domain, CommandLineArgs>( config, cmdLn );

		// register necessary transformations to finalize configuration template
		transformationManager.register( new TransformationGenericName(), true );
		transformationManager.register( new TransformationGenericUuid(), true );
		transformationManager.register( new TransformationGenericCpu(), true );
		transformationManager.register( new TransformationGenericMemory(), true );
		transformationManager.register( new TransformationGenericDiskStorageDevices(), true );
		transformationManager.register( new TransformationGenericDiskCdromDevices(), true );
		transformationManager.register( new TransformationGenericDiskFloppyDevices(), true );
		transformationManager.register( new TransformationGenericInterfaceDevices(), true );
		transformationManager.register( new TransformationGenericParallelDevices(), true );
		transformationManager.register( new TransformationGenericFileSystemDevices(), true );

		// register QEMU specific transformations to finalize configuration template
		if ( hypervisor instanceof LibvirtHypervisorQemu ) {
			final LibvirtHypervisorQemu hypervisorQemu = LibvirtHypervisorQemu.class.cast( hypervisor );

			transformationManager.register( new TransformationSpecificQemuArchitecture( hypervisorQemu ), true );
			transformationManager.register( new TransformationSpecificQemuSerialDevices( hypervisorQemu ), true );
			transformationManager.register( new TransformationSpecificQemuGpuPassthroughNvidia( hypervisorQemu ), false );
		}

		// finalize Libvirt VM configuration template
		try {
			transformationManager.transform();
		} catch ( TransformationException e ) {
			LOGGER.error( "Failed to finalize VM configuration file: " + e.getLocalizedMessage() );
			hypervisor.close();
			System.exit( 4 );
		}

		// write finalized configuration to file if output file is specified
		final String xmlOutputFileName = cmdLn.getVmCfgOutFileName();
		if ( xmlOutputFileName != null && !xmlOutputFileName.isEmpty() ) {
			try {
				final File xmlOutputFile = new File( xmlOutputFileName );
				config.toXml( xmlOutputFile );
			} catch ( LibvirtXmlSerializationException e ) {
				LOGGER.error( "Failed to write VM output configuration file: " + e.getLocalizedMessage() );
				hypervisor.close();
				System.exit( 5 );
			}
		}

		// define Libvirt VM from finalized configuration
		LibvirtVirtualMachine vm = null;
		try {
			vm = hypervisor.registerVm( config );
		} catch ( LibvirtHypervisorException e ) {
			LOGGER.error( "Failed to define VM from configuration file: " + e.getLocalizedMessage() );
			hypervisor.close();
			System.exit( 6 );
		}

		// start defined Libvirt VM
		try {
			vm.start();
		} catch ( LibvirtVirtualMachineException e ) {
			LOGGER.error( "Failed to start defined VM: " + e.getLocalizedMessage() );
			hypervisor.close();
			System.exit( 7 );
		}

		// create specific viewer to display Libvirt VM
		final Viewer vmViewer;
		if ( cmdLn.isNvidiaGpuPassthroughEnabled() ) {
			// viewer for GPU passthrough (framebuffer access) is required
			vmViewer = new ViewerLookingGlassClient( vm, hypervisor, cmdLn.isDebugEnabled() );
		} else {
			// viewer for non-GPU passthrough (no framebuffer access) is required
			if ( cmdLn.isDebugEnabled() ) {
				// create specific Virtual Machine Manager viewer if debug mode is enabled
				vmViewer = new ViewerVirtManager( vm, hypervisor );
			} else {
				// create Virtual Viewer if debug mode is disabled
				vmViewer = new ViewerVirtViewer( vm, hypervisor );
			}
		}

		// display Libvirt VM with the specific viewer on the screen
		try {
			vmViewer.display();
		} catch ( ViewerException e ) {
			LOGGER.error( "Failed to display VM: " + e.getLocalizedMessage() );
			hypervisor.close();
			System.exit( 8 );
		}

		// undefine VM after usage
		try {
			hypervisor.deregisterVm( vm );
		} catch ( LibvirtHypervisorException | LibvirtVirtualMachineException e ) {
			LOGGER.error( "Failed to undefine VM: " + e.getLocalizedMessage() );
			hypervisor.close();
			System.exit( 9 );
		}

		// close connection to hypervisor
		hypervisor.close();

		// return with successful exit code
		System.exit( 0 );
	}

	/**
	 * Helper utility to print the run-virt QEMU plugin help text.
	 * 
	 * @param cmdLn parsed command line arguments.
	 */
	public static void printUsage( CommandLineArgs cmdLn )
	{
		final String newLine = System.lineSeparator();
		final String fullAppDesc = newLine + App.APP_DESC + newLine + newLine;
		final String fullAppInfo = newLine + App.APP_INFO;

		cmdLn.printHelp( App.APP_NAME, fullAppDesc, fullAppInfo );
	}

	/**
	 * Helper utility to log the run-virt QEMU plugin's submitted command line arguments.
	 * 
	 * @param cmdLn parsed command line arguments.
	 * 
	 * @implNote This method is intended to be used for debugging purposes.
	 */
	public static void printCmdLnArgs( CommandLineArgs cmdLn )
	{
		// determine length of longest command line option
		int longOptionLengthMax = 0;

		for ( CmdLnOption option : CmdLnOption.values() ) {
			// store length of current long option
			final int longOptionLength = option.getLongOption().length();

			// if length is longer than every length before, store this length as longest length
			if ( longOptionLength > longOptionLengthMax ) {
				longOptionLengthMax = longOptionLength;
			}
		}

		LOGGER.debug( "Command line arguments: --------------" );

		for ( CmdLnOption option : CmdLnOption.values() ) {
			final String paddedLongOption = String.format( "%-" + longOptionLengthMax + "s", option.getLongOption() );
			String[] longOptionArguments;

			// only request and log argument if option has an command line argument
			if ( option.getNumArguments() > 0 ) {
				longOptionArguments = cmdLn.getArguments( option );

				if ( longOptionArguments == null ) {
					longOptionArguments = new String[] { "no argument specified" };
				}
			} else {
				longOptionArguments = new String[] { "option has no argument" };
			}

			LOGGER.debug( "\t" + paddedLongOption + ": " + Arrays.toString( longOptionArguments ) );
		}
	}
}