diff options
Diffstat (limited to 'core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer')
6 files changed, 495 insertions, 0 deletions
diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/Viewer.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/Viewer.java new file mode 100644 index 00000000..d23a9e11 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/Viewer.java @@ -0,0 +1,118 @@ +package org.openslx.runvirt.viewer; + +import org.openslx.runvirt.virtualization.LibvirtHypervisor; +import org.openslx.runvirt.virtualization.LibvirtVirtualMachine; +import org.openslx.virtualization.Version; + +/** + * Representation of an viewer for virtual machines running on a host system. + * + * @author Manuel Bentele + * @version 1.0 + */ +public abstract class Viewer +{ + /** + * Name of the viewer. + */ + private final String name; + + /** + * Number of supported displays by the viewer. + */ + private final int numSupportedDisplays; + + /** + * The virtual machine to display. + */ + private final LibvirtVirtualMachine machine; + + /** + * Remote (hypervisor) endpoint for the viewer to connect to. + */ + private final LibvirtHypervisor hypervisor; + + /** + * Creates a new viewer for a Libvirt virtual machine running on a Libvirt hypervisor. + * + * @param name textual name of the viewer. + * @param numSupportedDisplays number of supported displays by the viewer. + * @param machine virtual machine to display. + * @param hypervisor remote (hypervisor) endpoint for the viewer to connect to. + */ + public Viewer( String name, int numSupportedDisplays, LibvirtVirtualMachine machine, LibvirtHypervisor hypervisor ) + { + this.name = name; + this.numSupportedDisplays = numSupportedDisplays; + this.machine = machine; + this.hypervisor = hypervisor; + } + + /** + * Returns the name of the viewer. + * + * @return name of the viewer. + */ + public String getName() + { + return this.name; + } + + /** + * Returns the number of supported displays by the viewer. + * + * @return number of supported displays by the viewer. + */ + public int getNumberOfSupportedDisplays() + { + return this.numSupportedDisplays; + } + + /** + * Returns the virtual machine to display. + * + * @return virtual machine to display. + */ + public LibvirtVirtualMachine getMachine() + { + return this.machine; + } + + /** + * Returns the remote (hypervisor) endpoint for the viewer to connect to. + * + * @return remote (hypervisor) endpoint for the viewer to connect to. + */ + public LibvirtHypervisor getHypervisor() + { + return this.hypervisor; + } + + /** + * Displays all virtual machine's displays. + * + * @throws ViewerException failed to display all displays of a virtual machine. + * + * @apiNote A call to this method blocks until the implemented {@link #render()} process + * terminates. + */ + public void display() throws ViewerException + { + this.render(); + } + + /** + * Returns the version of the viewer. + * + * @return version of the viewer. + * @throws ViewerException failed to get version of the viewer. + */ + public abstract Version getVersion() throws ViewerException; + + /** + * Renders the content of all displays from the virtual machine. + * + * @throws ViewerException failed to render all displays of a virtual machine. + */ + protected abstract void render() throws ViewerException; +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerException.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerException.java new file mode 100644 index 00000000..0c178375 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerException.java @@ -0,0 +1,35 @@ +package org.openslx.runvirt.viewer; + +/** + * An exception of a viewer error during displaying all displays of a virtual machine. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class ViewerException extends Exception +{ + /** + * Version for serialization. + */ + private static final long serialVersionUID = 161091514643380414L; + + /** + * Creates a new viewer exception including an error message. + * + * @param errorMsg message to describe a specific viewer error. + */ + public ViewerException( String errorMsg ) + { + super( errorMsg ); + } + + /** + * Creates a new viewer exception by a copy of an existing viewer exception. + * + * @param e existing viewer exception. + */ + public ViewerException( ViewerException e ) + { + super( e ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerLookingGlassClient.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerLookingGlassClient.java new file mode 100644 index 00000000..cea9ccd8 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerLookingGlassClient.java @@ -0,0 +1,105 @@ +package org.openslx.runvirt.viewer; + +import org.openslx.runvirt.virtualization.LibvirtHypervisor; +import org.openslx.runvirt.virtualization.LibvirtVirtualMachine; +import org.openslx.virtualization.Version; + +/** + * Looking Glass Client to view the exposed framebuffer (through a shared memory) of a virtual + * machine running the Looking Glass Host application. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class ViewerLookingGlassClient extends Viewer +{ + /** + * Name of the Looking Glass Client program. + */ + private final static String NAME = "looking-glass-client"; + + /** + * Maximum number of supported displays by the Looking Glass Client. + */ + private final static int NUM_SUPPORTED_DISPLAYS = 1; + + /** + * File name of the shared memory file to receive display content from the Looking Glass Host. + */ + private final static String SHARED_MEMORY_FILENAME = "/dev/shm/looking-glass"; + + /** + * State whether showing debug information during virtual machine rendering or not. + */ + private final boolean debug; + + /** + * Creates a new Looking Glass Client for a Libvirt virtual machine running on a Libvirt + * hypervisor. + * + * @param machine virtual machine to display. + * @param hypervisor remote (hypervisor) endpoint for the viewer to connect to. + */ + public ViewerLookingGlassClient( LibvirtVirtualMachine machine, LibvirtHypervisor hypervisor ) + { + this( machine, hypervisor, false ); + } + + /** + * Creates a new Looking Glass Client for a Libvirt virtual machine running on a Libvirt + * hypervisor. + * + * @param machine virtual machine to display. + * @param hypervisor remote (hypervisor) endpoint for the viewer to connect to. + * @param debug state whether showing debug information during virtual machine rendering or not. + */ + public ViewerLookingGlassClient( LibvirtVirtualMachine machine, LibvirtHypervisor hypervisor, boolean debug ) + { + super( ViewerLookingGlassClient.NAME, ViewerLookingGlassClient.NUM_SUPPORTED_DISPLAYS, machine, hypervisor ); + + this.debug = debug; + } + + /** + * Returns the state whether showing debug information during virtual machine rendering or not. + * + * @return state whether showing debug information during virtual machine rendering or not. + */ + public boolean isDebugEnabled() + { + return this.debug; + } + + @Override + public Version getVersion() throws ViewerException + { + return null; + } + + @Override + public void render() throws ViewerException + { + // execute viewer process with arguments: + // in non-debug mode: + // "looking-glass-client app:shmFile=<SHARED-MEM-FILE> win:fullScreen=yes spice:enable=yes win:alerts=no" + // in debug mode: + // "looking-glass-client app:shmFile=<SHARED-MEM-FILE> win:fullScreen=yes spice:enable=yes win:alerts=yes win:showFPS=yes" + final String[] viewerParameters; + if ( this.isDebugEnabled() ) { + viewerParameters = new String[] { + "app:shmFile=" + ViewerLookingGlassClient.SHARED_MEMORY_FILENAME, + "win:fullScreen=yes", + "spice:enable=yes", + "win:alerts=no" }; + } else { + viewerParameters = new String[] { + "app:shmFile=" + ViewerLookingGlassClient.SHARED_MEMORY_FILENAME, + "win:fullScreen=yes", + "spice:enable=yes", + "win:alerts=yes", + "win:showFPS=yes" }; + } + + ViewerUtils.executeViewer( ViewerLookingGlassClient.NAME, viewerParameters ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerUtils.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerUtils.java new file mode 100644 index 00000000..1ed5947a --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerUtils.java @@ -0,0 +1,65 @@ +package org.openslx.runvirt.viewer; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.exec.CommandLine; +import org.apache.commons.exec.DefaultExecutor; +import org.apache.commons.exec.PumpStreamHandler; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.ByteArrayOutputStream; + +/** + * Utils for viewing displays of virtual machines. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class ViewerUtils +{ + /** + * Synchronously executes a viewer program specified by a command line call. + * <p> + * The command line call of the viewer program consists of the program name and an optional list + * of submitted command line arguments for the viewer program. The result of the executed viewer + * program from the standard output is returned after the program has exited. + * + * @param viewerProgram name of the viewer program. + * @param viewerArguments optional command line arguments for the viewer program. + * @return result of the executed viewer program from the standard output. + * @throws ViewerException failed to execute the viewer program. + */ + @SuppressWarnings( "deprecation" ) + public static String executeViewer( String viewerProgram, String[] viewerArguments ) throws ViewerException + { + final CommandLine viewerCommandLine = new CommandLine( viewerProgram ); + final DefaultExecutor viewerExecutor = new DefaultExecutor(); + + // prepare viewer command to execute + viewerCommandLine.addArguments( viewerArguments ); + + // set up temporary working directory for the viewer process + viewerExecutor.setWorkingDirectory( FileUtils.getTempDirectory() ); + + // set expected exit value of the viewer process indicating a successful operation + viewerExecutor.setExitValue( 0 ); + + // set up output stream handler to retrieve the content from the viewer's standard output + final ByteArrayOutputStream viewerOutputStream = new ByteArrayOutputStream(); + final PumpStreamHandler viewerOutputStreamHandler = new PumpStreamHandler( viewerOutputStream ); + viewerExecutor.setStreamHandler( viewerOutputStreamHandler ); + + // execute the viewer command as blocking process + try { + viewerExecutor.execute( viewerCommandLine ); + } catch ( IOException e ) { + throw new ViewerException( "Failed to execute '" + viewerProgram + "': " + e.getLocalizedMessage() ); + } + + final String viewerOuput = viewerOutputStream.toString( StandardCharsets.UTF_8 ); + IOUtils.closeQuietly( viewerOutputStream ); + + return viewerOuput; + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerVirtManager.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerVirtManager.java new file mode 100644 index 00000000..1848d975 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerVirtManager.java @@ -0,0 +1,75 @@ +package org.openslx.runvirt.viewer; + +import org.openslx.runvirt.virtualization.LibvirtHypervisor; +import org.openslx.runvirt.virtualization.LibvirtHypervisorException; +import org.openslx.runvirt.virtualization.LibvirtVirtualMachine; +import org.openslx.virtualization.Version; + +/** + * Virtual Machine Manager (virt-manager) to control and view a display of a virtual machine. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class ViewerVirtManager extends Viewer +{ + /** + * Name of the Virtual Machine Manager program. + */ + private final static String NAME = "virt-manager"; + + /** + * Maximum number of supported displays by the Virtual Machine Manager. + */ + private final static int NUM_SUPPORTED_DISPLAYS = 1; + + /** + * Creates a new Virtual Machine Manager for a virtual machine running on a hypervisor. + * + * @param machine virtual machine to display. + * @param hypervisor remote (hypervisor) endpoint for the viewer to connect to. + */ + public ViewerVirtManager( LibvirtVirtualMachine machine, LibvirtHypervisor hypervisor ) + { + super( ViewerVirtManager.NAME, ViewerVirtManager.NUM_SUPPORTED_DISPLAYS, machine, hypervisor ); + } + + @Override + public Version getVersion() throws ViewerException + { + // execute viewer process with arguments: + // "virt-manager --version" + final String versionOutput = ViewerUtils.executeViewer( ViewerVirtManager.NAME, + new String[] { "--version" } ); + + return Version.valueOf( versionOutput ); + } + + @Override + public void render() throws ViewerException + { + String connectionUri = null; + String machineUuid = null; + + // get URI of the hypervisor connection and UUID of the machine + try { + connectionUri = this.getHypervisor().getConnectionUri(); + machineUuid = this.getMachine().getConfiguration().getUuid(); + } catch ( LibvirtHypervisorException e ) { + throw new ViewerException( + "Failed to retrieve the URI of the hypervisor backend or the UUID of the machine to display: " + + e.getLocalizedMessage() ); + } + + // check if URI of the hypervisor connection and UUID of the machine is specified, otherwise abort + if ( connectionUri == null || connectionUri.isEmpty() || machineUuid == null || machineUuid.isEmpty() ) { + throw new ViewerException( + "The URI of the hypervisor backend or the UUID of the machine to display is missing!" ); + } + + // execute viewer process with arguments: + // "virt-viewer --connect=<URI> --show-domain-console <DOMAIN-UUID>" + ViewerUtils.executeViewer( ViewerVirtManager.NAME, + new String[] { "--connect=" + connectionUri, "--show-domain-console", machineUuid } ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerVirtViewer.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerVirtViewer.java new file mode 100644 index 00000000..8f6e9481 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/viewer/ViewerVirtViewer.java @@ -0,0 +1,97 @@ +package org.openslx.runvirt.viewer; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.openslx.runvirt.virtualization.LibvirtHypervisor; +import org.openslx.runvirt.virtualization.LibvirtHypervisorException; +import org.openslx.runvirt.virtualization.LibvirtVirtualMachine; +import org.openslx.virtualization.Version; + +/** + * Virtual Viewer (virt-viewer) to view one or several displays of a virtual machine. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class ViewerVirtViewer extends Viewer +{ + /** + * Name of the Virtual Machine Manager program. + */ + private final static String NAME = "virt-viewer"; + + /** + * Maximum number of supported displays by the Virtual Viewer. + */ + private final static int NUM_SUPPORTED_DISPLAYS = Integer.MAX_VALUE; + + /** + * Creates a new Virtual Viewer for a Libvirt virtual machine running on a Libvirt hypervisor. + * + * @param machine virtual machine to display. + * @param hypervisor remote (hypervisor) endpoint for the viewer to connect to. + */ + public ViewerVirtViewer( LibvirtVirtualMachine machine, LibvirtHypervisor hypervisor ) + { + super( ViewerVirtViewer.NAME, ViewerVirtViewer.NUM_SUPPORTED_DISPLAYS, machine, hypervisor ); + } + + @Override + public Version getVersion() throws ViewerException + { + final Version version; + + // execute viewer process with arguments: + // "virt-viewer --version" + final String versionOutput = ViewerUtils.executeViewer( ViewerVirtViewer.NAME, + new String[] { "--version" } ); + + if ( versionOutput == null ) { + version = null; + } else { + // parse version from the viewer's process output + final Pattern viewerVersionPattern = Pattern.compile( "(\\d+).(\\d+)" ); + final Matcher viewerVersionMatcher = viewerVersionPattern.matcher( versionOutput ); + + // check if version pattern was found + if ( viewerVersionMatcher.find() ) { + final short major = Short.valueOf( viewerVersionMatcher.group( 1 ) ); + final short minor = Short.valueOf( viewerVersionMatcher.group( 2 ) ); + version = new Version( major, minor ); + } else { + version = null; + } + } + + return version; + } + + @Override + public void render() throws ViewerException + { + String connectionUri = null; + String machineUuid = null; + + // get URI of the hypervisor connection and UUID of the machine + try { + connectionUri = this.getHypervisor().getConnectionUri(); + machineUuid = this.getMachine().getConfiguration().getUuid(); + } catch ( LibvirtHypervisorException e ) { + throw new ViewerException( + "Failed to retrieve the URI of the hypervisor backend or the UUID of the machine to display: " + + e.getLocalizedMessage() ); + } + + // check if URI of the hypervisor connection and UUID of the machine is specified, otherwise abort + if ( connectionUri == null || connectionUri.isEmpty() || machineUuid == null || machineUuid.isEmpty() ) { + throw new ViewerException( + "The URI of the hypervisor backend or the UUID of the machine to display is missing!" ); + } + + // execute viewer process with arguments: + // "virt-viewer --full-screen --reconnect --wait --attach --connect=<URI> --domain-name -- <DOMAIN-UUID>" + ViewerUtils.executeViewer( ViewerVirtViewer.NAME, new String[] { "--full-screen", "--reconnect", "--wait", + "--attach", "--connect=" + connectionUri, "--uuid", "--", machineUuid } ); + } +} |