diff options
author | Manuel Bentele | 2021-06-25 14:23:19 +0200 |
---|---|---|
committer | Manuel Bentele | 2021-06-25 14:23:19 +0200 |
commit | c820bd818c488fb2ab14d51afa4d241b762d2fc6 (patch) | |
tree | 5f0c364c10820219ca418e6436c425074afc35e0 | |
parent | [debug-report-bwlp] add blkid output (diff) | |
parent | [libvirt] Enforce libvirt UIDs/GIDs to not collide with LDAP UIDs/GIDs (diff) | |
download | mltk-c820bd818c488fb2ab14d51afa4d241b762d2fc6.tar.gz mltk-c820bd818c488fb2ab14d51afa4d241b762d2fc6.tar.xz mltk-c820bd818c488fb2ab14d51afa4d241b762d2fc6.zip |
Merge branch 'feature/qemu-integration'
88 files changed, 5734 insertions, 284 deletions
diff --git a/core/modules/libvirt-users/module.build b/core/modules/libvirt-users/module.build new file mode 100644 index 00000000..cab41b98 --- /dev/null +++ b/core/modules/libvirt-users/module.build @@ -0,0 +1,43 @@ +#!/bin/bash +fetch_source() { + : +} + +build() { + : +} + +post_copy() { + # Create libvirt users before installing libvirt packages since the + # libvirt DEB package hook script will create system users with an + # UID/GID greater or equal than 1000. Those default libvirt UIDs/GIDs + # are not allowed since they will collide with LDAP UIDs/GIDs. + + # add system groups to run libvirt + if ! getent group libvirt-qemu >/dev/null; then + addgroup --quiet --system libvirt-qemu + fi + + if ! getent group kvm >/dev/null; then + addgroup --quiet --system kvm + fi + + # add system user libvirt runs qemu/kvm instances with + if ! getent passwd libvirt-qemu >/dev/null; then + adduser --quiet \ + --system \ + --ingroup kvm \ + --quiet \ + --disabled-login \ + --disabled-password \ + --home /var/lib/libvirt \ + --no-create-home \ + --gecos "Libvirt Qemu" \ + libvirt-qemu + fi + + # add libvirt system user to the libvirt system group + if ! getent group libvirt-qemu >/dev/null; then + adduser --quiet libvirt-qemu libvirt-qemu + fi +} diff --git a/core/modules/libvirt-users/module.conf b/core/modules/libvirt-users/module.conf new file mode 100644 index 00000000..668ddf88 --- /dev/null +++ b/core/modules/libvirt-users/module.conf @@ -0,0 +1,5 @@ +#!/bin/bash + +REQUIRED_BINARIES="" +REQUIRED_LIBRARIES="" +REQUIRED_DIRECTORIES="" diff --git a/core/modules/libvirt/data/addon-init b/core/modules/libvirt/data/addon-init new file mode 100755 index 00000000..131a03f7 --- /dev/null +++ b/core/modules/libvirt/data/addon-init @@ -0,0 +1,6 @@ +#!/bin/ash + +# register and start libvirt services +systemctl daemon-reload +systemctl start libvirtd.service +systemctl start libvirt-guests.service diff --git a/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/br0.xml b/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/br0.xml new file mode 120000 index 00000000..e31f4a71 --- /dev/null +++ b/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/br0.xml @@ -0,0 +1 @@ +../br0.xml
\ No newline at end of file diff --git a/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/nat1.xml b/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/nat1.xml new file mode 120000 index 00000000..9e35bd99 --- /dev/null +++ b/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/nat1.xml @@ -0,0 +1 @@ +../nat1.xml
\ No newline at end of file diff --git a/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/vsw2.xml b/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/vsw2.xml new file mode 120000 index 00000000..1600f8a6 --- /dev/null +++ b/core/modules/libvirt/data/etc/libvirt/qemu/networks/autostart/vsw2.xml @@ -0,0 +1 @@ +../vsw2.xml
\ No newline at end of file diff --git a/core/modules/libvirt/data/etc/libvirt/qemu/networks/br0.xml b/core/modules/libvirt/data/etc/libvirt/qemu/networks/br0.xml new file mode 100644 index 00000000..14acd6ec --- /dev/null +++ b/core/modules/libvirt/data/etc/libvirt/qemu/networks/br0.xml @@ -0,0 +1,6 @@ +<network ipv6='no'> + <name>br0</name> + <forward mode='bridge'/> + <bridge name='br0'/> + <dns enable="no"/> +</network> diff --git a/core/modules/libvirt/data/etc/libvirt/qemu/networks/nat1.xml b/core/modules/libvirt/data/etc/libvirt/qemu/networks/nat1.xml new file mode 100644 index 00000000..689b3640 --- /dev/null +++ b/core/modules/libvirt/data/etc/libvirt/qemu/networks/nat1.xml @@ -0,0 +1,6 @@ +<network ipv6='no'> + <name>nat1</name> + <forward mode='bridge'/> + <bridge name='nat1'/> + <dns enable="no"/> +</network> diff --git a/core/modules/libvirt/data/etc/libvirt/qemu/networks/vsw2.xml b/core/modules/libvirt/data/etc/libvirt/qemu/networks/vsw2.xml new file mode 100644 index 00000000..a2c43fea --- /dev/null +++ b/core/modules/libvirt/data/etc/libvirt/qemu/networks/vsw2.xml @@ -0,0 +1,6 @@ +<network ipv6='no'> + <name>vsw2</name> + <forward mode='bridge'/> + <bridge name='vsw2'/> + <dns enable="no"/> +</network> diff --git a/core/modules/libvirt/module.build b/core/modules/libvirt/module.build new file mode 100644 index 00000000..dd868159 --- /dev/null +++ b/core/modules/libvirt/module.build @@ -0,0 +1,14 @@ +#!/bin/bash +fetch_source() { + : +} + +build() { + : +} + +post_copy() { + # remove default network configuration + rm "${MODULE_BUILD_DIR}/etc/libvirt/qemu/networks/default.xml" + rm "${MODULE_BUILD_DIR}/etc/libvirt/qemu/networks/autostart/default.xml" +} diff --git a/core/modules/libvirt/module.conf b/core/modules/libvirt/module.conf new file mode 100644 index 00000000..d67344f7 --- /dev/null +++ b/core/modules/libvirt/module.conf @@ -0,0 +1,9 @@ +#!/bin/bash + +REQUIRED_MODULES=" + libvirt-users +" + +REQUIRED_BINARIES="" +REQUIRED_LIBRARIES="" +REQUIRED_DIRECTORIES="" diff --git a/core/modules/libvirt/module.conf.debian b/core/modules/libvirt/module.conf.debian new file mode 100644 index 00000000..84be983c --- /dev/null +++ b/core/modules/libvirt/module.conf.debian @@ -0,0 +1,14 @@ +#!/bin/bash +REQUIRED_INSTALLED_PACKAGES=" + libvirt-daemon-system + libvirt-daemon + libvirt-clients + ebtables +" + +REQUIRED_CONTENT_PACKAGES=" + libvirt-daemon-system + libvirt-daemon + libvirt-clients + ebtables +" diff --git a/core/modules/libvirt/module.conf.ubuntu b/core/modules/libvirt/module.conf.ubuntu new file mode 100644 index 00000000..84be983c --- /dev/null +++ b/core/modules/libvirt/module.conf.ubuntu @@ -0,0 +1,14 @@ +#!/bin/bash +REQUIRED_INSTALLED_PACKAGES=" + libvirt-daemon-system + libvirt-daemon + libvirt-clients + ebtables +" + +REQUIRED_CONTENT_PACKAGES=" + libvirt-daemon-system + libvirt-daemon + libvirt-clients + ebtables +" diff --git a/core/modules/openjdk-8-jre-headless/module.build b/core/modules/openjdk-8-jre-headless/module.build new file mode 100644 index 00000000..61fd9a57 --- /dev/null +++ b/core/modules/openjdk-8-jre-headless/module.build @@ -0,0 +1,13 @@ +#!/bin/bash + +fetch_source() { + : +} + +build() { + : +} + +post_copy() { + : +} diff --git a/core/modules/openjdk-8-jre-headless/module.conf b/core/modules/openjdk-8-jre-headless/module.conf new file mode 100644 index 00000000..668ddf88 --- /dev/null +++ b/core/modules/openjdk-8-jre-headless/module.conf @@ -0,0 +1,5 @@ +#!/bin/bash + +REQUIRED_BINARIES="" +REQUIRED_LIBRARIES="" +REQUIRED_DIRECTORIES="" diff --git a/core/modules/openjdk-8-jre-headless/module.conf.ubuntu b/core/modules/openjdk-8-jre-headless/module.conf.ubuntu new file mode 100644 index 00000000..aa99a07c --- /dev/null +++ b/core/modules/openjdk-8-jre-headless/module.conf.ubuntu @@ -0,0 +1,9 @@ +#!/bin/bash + +REQUIRED_INSTALLED_PACKAGES=" + openjdk-8-jre-headless +" + +REQUIRED_CONTENT_PACKAGES=" + openjdk-8-jre-headless +"
\ No newline at end of file diff --git a/core/modules/qemu/data/addon-init b/core/modules/qemu/data/addon-init new file mode 100755 index 00000000..c7c96ae4 --- /dev/null +++ b/core/modules/qemu/data/addon-init @@ -0,0 +1,4 @@ +#!/bin/ash + +systemctl daemon-reload +systemctl start qemu.service diff --git a/core/modules/qemu/data/etc/systemd/system/graphical.target.wants/qemu.service b/core/modules/qemu/data/etc/systemd/system/graphical.target.wants/qemu.service new file mode 120000 index 00000000..a301779d --- /dev/null +++ b/core/modules/qemu/data/etc/systemd/system/graphical.target.wants/qemu.service @@ -0,0 +1 @@ +../qemu.service
\ No newline at end of file diff --git a/core/modules/qemu/data/etc/systemd/system/qemu.service b/core/modules/qemu/data/etc/systemd/system/qemu.service new file mode 100644 index 00000000..7a1d3b4d --- /dev/null +++ b/core/modules/qemu/data/etc/systemd/system/qemu.service @@ -0,0 +1,11 @@ +[Unit] +Description=Sets up the QEMU hypervisor +Requires=run-virt-env.service +Requires=libvirtd.service +After=run-virt-env.service +After=libvirtd.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/opt/openslx/scripts/systemd-qemu_env start diff --git a/core/modules/qemu/data/opt/openslx/scripts/systemd-qemu_env b/core/modules/qemu/data/opt/openslx/scripts/systemd-qemu_env new file mode 100755 index 00000000..3355b7a0 --- /dev/null +++ b/core/modules/qemu/data/opt/openslx/scripts/systemd-qemu_env @@ -0,0 +1,46 @@ +#!/bin/bash +# ----------------------------------------------------------------------------- +# +# Copyright (c) 2017..2021 bwLehrpool-Projektteam +# +# This program/file is free software distributed under the GPL version 2. +# See https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html +# +# If you have any feedback please consult https://bwlehrpool.de and +# send your feedback to bwlehrpool@hs-offenburg.de. +# +# General information about bwLehrpool can be found at https://bwlehrpool.de +# +# ----------------------------------------------------------------------------- +# systemd-qemu_env +# - This is the preparation script for the configuration of QEMU on Linux. +################################################################################ + +# +# load general KVM module +# +modprobe "kvm" || slxlog "qemu" "Could not load 'kvm' kernel modul!" + +# +# load CPU specific KVM implementation +# +virt=$(egrep -m1 -w '^flags[[:blank:]]*:' /proc/cpuinfo | egrep -wo '(vmx|svm)') || true + +if [ "${virt}" = "vmx" ]; then + kmod="kvm_intel" +elif [ "${virt}" = "svm" ]; then + kmod="kvm_amd" +else + slxlog "qemu" "CPU does not support KVM extensions!" + exit 1 +fi + +modprobe "${kmod}" || slxlog "qemu" "Could not load '${kmod}' kernel modul!" + +# +# check that the KVM exposed device exists +# +if [ ! -e /dev/kvm ]; then + slxlog "qemu" "/dev/kvm not found! Missing kvm kernel module(s)?" + exit 1 +fi diff --git a/core/modules/qemu/data/opt/openslx/vmchooser/plugins/qemu/run-virt.include b/core/modules/qemu/data/opt/openslx/vmchooser/plugins/qemu/run-virt.include new file mode 100644 index 00000000..b7e43df9 --- /dev/null +++ b/core/modules/qemu/data/opt/openslx/vmchooser/plugins/qemu/run-virt.include @@ -0,0 +1,53 @@ +# ----------------------------------------------------------------------------- +# +# Copyright (c) 2009..2021 bwLehrpool-Projektteam +# +# This program/file is free software distributed under the GPL version 2. +# See https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html +# +# If you have any feedback please consult https://bwlehrpool.de and +# send your feedback to bwlehrpool@hs-offenburg.de. +# +# General information about bwLehrpool can be found at https://bwlehrpool.de +# +# ----------------------------------------------------------------------------- +# run-virt.include +# - qemu plugin for vmchooser run-virt +################################################################################ + +# BASH_SOURCE[0] contains the file being sourced, namely this one +declare -rg QEMU_PLUGIN_DIR="$(dirname "${BASH_SOURCE[0]}")" +declare -rg QEMU_INCLUDE_DIR="${QEMU_PLUGIN_DIR}/includes" + +# Define which features the QEMU plugin supports +declare -rg PLUGIN_FEATURES="firewall printer usb slxfloppy sound netshares" + +run_plugin() { + # write finalized config to temporary folder for debugging purposes + local vm_final_run_file="/tmp/qemu-last-config.xml" + + # call the Libvirt Java tool to finalize configuration and start VM + declare -rg VIRTCMD="java -jar /opt/openslx/share/java/runvirt-plugin-qemu.jar" + + isset DEBUG && VIRTCMDOPTS+=( "-debug" "${DEBUG}" ) + isset VM_CLEANNAME && VIRTCMDOPTS+=( "-vmname" "${VM_CLEANNAME}" ) + isset VM_DISPLAYNAME && VIRTCMDOPTS+=( "-vmdsplname" "${VM_DISPLAYNAME}" ) + isset VM_OS_TYPE && VIRTCMDOPTS+=( "-vmos" "${VM_OS_TYPE}" ) + isset VM_RUN_FILE && VIRTCMDOPTS+=( "-vmcfginp" "${VM_RUN_FILE}" ) + isset vm_final_run_file && VIRTCMDOPTS+=( "-vmcfgout" "${vm_final_run_file}" ) + isset IMGUUID && VIRTCMDOPTS+=( "-vmuuid" "${IMGUUID}" ) + isset CPU_CORES && VIRTCMDOPTS+=( "-vmncpus" "${CPU_CORES}" ) + isset VM_MEM && VIRTCMDOPTS+=( "-vmmem" "${VM_MEM}" ) + isset VM_MAC_ADDR && VIRTCMDOPTS+=( "-vmmac0" "${VM_MAC_ADDR}" ) + isset VM_DISKFILE_RO && VIRTCMDOPTS+=( "-vmhdd0" "${VM_DISKFILE_RO}" ) + isset FLOPPY_0 && VIRTCMDOPTS+=( "-vmfloppy0" "${FLOPPY_0}" ) + isset SLX_FLOPPY_IMG && VIRTCMDOPTS+=( "-vmfloppy1" "${SLX_FLOPPY_IMG}" ) + isset CDROM_0 && VIRTCMDOPTS+=( "-vmcdrom0" "${CDROM_0}" ) + isset CDROM_1 && VIRTCMDOPTS+=( "-vmcdrom1" "${CDROM_1}" ) + isset SERIAL0 && VIRTCMDOPTS+=( "-vmserial0" "${SERIAL0}" ) + isset PARALLEL0 && VIRTCMDOPTS+=( "-vmparallel0" "${PARALLEL0}" ) + isset HOME_SHARE_PATH && VIRTCMDOPTS+=( "-vmfssrc0" "${HOME_SHARE_PATH}" ) + isset HOME_SHARE_NAME && VIRTCMDOPTS+=( "-vmfstgt0" "${HOME_SHARE_NAME}" ) + isset COMMON_SHARE_PATH && VIRTCMDOPTS+=( "-vmfssrc1" "${COMMON_SHARE_PATH}" ) + isset COMMON_SHARE_NAME && VIRTCMDOPTS+=( "-vmfstgt1" "${COMMON_SHARE_NAME}" ) +} diff --git a/core/modules/qemu/module.build b/core/modules/qemu/module.build new file mode 100644 index 00000000..4d86de96 --- /dev/null +++ b/core/modules/qemu/module.build @@ -0,0 +1,26 @@ +#!/bin/bash + +fetch_source() { + # copy qemu runvirt plugin source to working directory + local src_dir="${MODULE_DIR}/runvirt-plugin-qemu" + local dst_dir="${MODULE_WORK_DIR}/src" + mkdir -p "${dst_dir}" || perror "Could not create source folder for qemu runvirt plugin!" + cp -r "${src_dir}" "${dst_dir}" || perror "Could not copy source files for qemu runvirt plugin!" +} + +build() { + # build qemu runvirt plugin + local build_dir="${MODULE_WORK_DIR}/src/runvirt-plugin-qemu" + mvn -f "${build_dir}" clean package || perror "Could not build runvirt-plugin-qemu with Maven!" + + # install qemu runvirt plugin + local build_artifact_filename="runvirt-plugin-qemu-1.0-SNAPSHOT.jar" + local install_dir="${MODULE_BUILD_DIR}/opt/openslx/share/java" + local install_filename="runvirt-plugin-qemu.jar" + mkdir -p "${install_dir}" + cp "${build_dir}/target/${build_artifact_filename}" "${install_dir}/${install_filename}" || perror "Could not install runvirt-plugin-qemu!" +} + +post_copy() { + : +} diff --git a/core/modules/qemu/module.conf b/core/modules/qemu/module.conf new file mode 100644 index 00000000..c1cdf05d --- /dev/null +++ b/core/modules/qemu/module.conf @@ -0,0 +1,10 @@ +#!/bin/bash + +REQUIRED_MODULES=" + libvirt + openjdk-8-jre-headless +" + +REQUIRED_BINARIES="" +REQUIRED_LIBRARIES="" +REQUIRED_DIRECTORIES="" diff --git a/core/modules/qemu/module.conf.ubuntu b/core/modules/qemu/module.conf.ubuntu new file mode 100644 index 00000000..3b1089e0 --- /dev/null +++ b/core/modules/qemu/module.conf.ubuntu @@ -0,0 +1,17 @@ +#!/bin/bash + +REQUIRED_INSTALLED_PACKAGES=" + maven + openjdk-8-jdk-headless +" + +REQUIRED_CONTENT_PACKAGES=" + qemu + qemu-kvm + qemu-system-common + qemu-system-arm + qemu-system-x86 + qemu-utils + seabios + ovmf +" diff --git a/core/modules/qemu/runvirt-plugin-qemu/.gitignore b/core/modules/qemu/runvirt-plugin-qemu/.gitignore new file mode 100644 index 00000000..9c74211c --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/.gitignore @@ -0,0 +1,15 @@ +/testing +/.settings +/.project +/.classpath +/target +/gen-java +*~ +!src/ +!test/ +*.swp + +# Ignore IntelliJ Project Files +/.idea +*.iml +/bin/ diff --git a/core/modules/qemu/runvirt-plugin-qemu/pom.xml b/core/modules/qemu/runvirt-plugin-qemu/pom.xml new file mode 100644 index 00000000..d8bb9743 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/pom.xml @@ -0,0 +1,179 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.openslx.runvirt.plugin.qemu</groupId> + <artifactId>runvirt-plugin-qemu</artifactId> + <version>1.0-SNAPSHOT</version> + + <name>runvirt-plugin-qemu</name> + <url>https://git.openslx.org/openslx-ng/mltk.git/</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> + + <repositories> + <repository> + <id>mltk-repo-snapshot</id> + <url>http://mltk-services.ruf.uni-freiburg.de:8081/nexus/content/repositories/snapshots/</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + <updatePolicy>always</updatePolicy> + </snapshots> + </repository> + <repository> + <id>mltk-repo-release</id> + <url>http://mltk-services.ruf.uni-freiburg.de:8081/nexus/content/repositories/releases/</url> + <releases> + <enabled>true</enabled> + <updatePolicy>always</updatePolicy> + </releases> + <snapshots> + <enabled>false</enabled> + </snapshots> + </repository> + </repositories> + + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>5.7.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>5.7.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-cli</groupId> + <artifactId>commons-cli</artifactId> + <version>1.4</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-exec</artifactId> + <version>1.3</version> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <version>[1.2.10,1.2.20]</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <version>1.7.25</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>com.ginsberg</groupId> + <artifactId>junit5-system-exit</artifactId> + <version>1.0.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.libvirt</groupId> + <artifactId>libvirt</artifactId> + <version>0.5.2</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna</artifactId> + <version>4.2.2</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.openslx.bwlp</groupId> + <artifactId>master-sync-shared</artifactId> + <version>1.1-SNAPSHOT</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.openslx.bwlp</groupId> + <artifactId>master-sync-shared</artifactId> + <version>1.1-SNAPSHOT</version> + <classifier>tests</classifier> + <type>test-jar</type> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-clean-plugin</artifactId> + <version>3.1.0</version> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>3.0.2</version> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.0</version> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.22.1</version> + </plugin> + <plugin> + <artifactId>maven-failsafe-plugin</artifactId> + <version>2.22.1</version> + </plugin> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + <configuration> + <descriptorRefs> + <descriptorRef>jar-with-dependencies</descriptorRef> + </descriptorRefs> + </configuration> + </plugin> + <plugin> + <artifactId>maven-install-plugin</artifactId> + <version>2.5.2</version> + </plugin> + <plugin> + <artifactId>maven-deploy-plugin</artifactId> + <version>2.8.2</version> + </plugin> + <plugin> + <artifactId>maven-site-plugin</artifactId> + <version>3.7.1</version> + </plugin> + <plugin> + <artifactId>maven-project-info-reports-plugin</artifactId> + <version>3.0.0</version> + </plugin> + </plugins> + <testResources> + <testResource> + <directory>${basedir}/src/test/resources</directory> + <includes> + <include>libvirt/xml/*</include> + </includes> + </testResource> + </testResources> + </build> +</project> diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java new file mode 100644 index 00000000..5ea7b720 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java @@ -0,0 +1,285 @@ +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 ) ); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgs.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgs.java new file mode 100644 index 00000000..589dd197 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgs.java @@ -0,0 +1,511 @@ +package org.openslx.runvirt.plugin.qemu.cmdln; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** + * Command line argument parser for the run-virt QEMU plugin (command line tool). + * + * @author Manuel Bentele + * @version 1.0 + */ +public class CommandLineArgs +{ + /** + * Parser for parsing command line arguments. + */ + private CommandLineParser cmdLnParser = null; + + /** + * Stores specified command line options. + */ + private Options cmdLnOptions = null; + + /** + * Stores the parsed command line arguments. + */ + private CommandLine cmdLn = null; + + /** + * Creates a new command line argument parser for the run-virt QEMU plugin. + * + * @implNote Please call {@link CommandLineArgs#parseCmdLnArgs(String[])} manually after + * obtaining the command line argument parser from this method. + */ + public CommandLineArgs() + { + this.createCmdLnParser(); + this.createCmdLnOptions(); + } + + /** + * Creates a new command line argument parser for the run-virt QEMU plugin and parses the command + * line arguments. + * + * @param args command line arguments submitted to the application. + * + * @throws CommandLineArgsException parsing of command line arguments failed. + */ + public CommandLineArgs( String[] args ) throws CommandLineArgsException + { + this(); + this.parseCmdLnArgs( args ); + } + + /** + * Creates a new parser and empty command line options for parsing command line arguments. + */ + private void createCmdLnParser() + { + this.cmdLnParser = new DefaultParser(); + this.cmdLnOptions = new Options(); + } + + /** + * Creates command line options specified by {@link CmdLnOption}. + */ + private void createCmdLnOptions() + { + for ( CmdLnOption option : CmdLnOption.values() ) { + final Option cmdlnOption; + + final boolean hasArg = ( option.getNumArguments() > 0 ) ? true : false; + cmdlnOption = new Option( option.getShortOption(), option.getLongOption(), hasArg, option.getDescription() ); + cmdlnOption.setValueSeparator( ',' ); + cmdlnOption.setArgs( option.getNumArguments() ); + + this.cmdLnOptions.addOption( cmdlnOption ); + } + } + + /** + * Prints command line help for the current application. + * + * @param appName name of the current application. + * @param header header for the command line help. + * @param footer footer for the command line help. + */ + public void printHelp( String appName, String header, String footer ) + { + HelpFormatter formatter = new HelpFormatter(); + formatter.setLeftPadding( 2 ); + formatter.printHelp( appName, header, this.cmdLnOptions, footer, true ); + } + + /** + * Parses command line arguments from a given argument {@link String}. + * + * @param args command line arguments submitted to the application. + * + * @throws CommandLineArgsException parsing of command line arguments failed. + */ + public void parseCmdLnArgs( String[] args ) throws CommandLineArgsException + { + try { + this.cmdLn = this.cmdLnParser.parse( this.cmdLnOptions, args ); + } catch ( ParseException e ) { + throw new CommandLineArgsException( e.getLocalizedMessage() ); + } + } + + /** + * Returns the parsed argument of the specified command line option. + * + * @param cmdLnOption command line option for that the parsed argument should be returned. + * @return parsed argument of the command line option. + */ + public String getArgument( CmdLnOption cmdLnOption ) + { + return this.cmdLn.getOptionValue( cmdLnOption.getShortOption() ); + } + + /** + * Returns the parsed arguments of the specified command line option. + * + * @param cmdLnOption command line option for that the parsed arguments should be returned. + * @return parsed argument of the command line option. + */ + public String[] getArguments( CmdLnOption cmdLnOption ) + { + return this.cmdLn.getOptionValues( cmdLnOption.getShortOption() ); + } + + /** + * Returns the presence of the command line option {@link CmdLnOption#HELP}. + * + * @return presence of the command line option {@link CmdLnOption#HELP}. + */ + public boolean isHelpAquired() + { + return this.cmdLn.hasOption( CmdLnOption.HELP.getShortOption() ); + } + + /** + * Returns the state of the command line option {@link CmdLnOption#DEBUG}. + * + * @return state of the command line option {@link CmdLnOption#DEBUG}. + */ + public boolean isDebugEnabled() + { + final String debugArg = this.getArgument( CmdLnOption.DEBUG ); + return ( "true".equals( debugArg ) ) ? true : false; + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_CFGINP}. + * + * @return argument of the command line option {@link CmdLnOption#VM_CFGINP}. + */ + public String getVmCfgInpFileName() + { + return this.getArgument( CmdLnOption.VM_CFGINP ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_CFGOUT}. + * + * @return argument of the command line option {@link CmdLnOption#VM_CFGOUT}. + */ + public String getVmCfgOutFileName() + { + return this.getArgument( CmdLnOption.VM_CFGOUT ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_NAME}. + * + * @return argument of the command line option {@link CmdLnOption#VM_NAME}. + */ + public String getVmName() + { + return this.getArgument( CmdLnOption.VM_NAME ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_UUID}. + * + * @return argument of the command line option {@link CmdLnOption#VM_UUID}. + */ + public String getVmUuid() + { + return this.getArgument( CmdLnOption.VM_UUID ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_DSPLNAME}. + * + * @return argument of the command line option {@link CmdLnOption#VM_DSPLNAME}. + */ + public String getVmDisplayName() + { + return this.getArgument( CmdLnOption.VM_DSPLNAME ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_OS}. + * + * @return argument of the command line option {@link CmdLnOption#VM_OS}. + */ + public String getVmOperatingSystem() + { + return this.getArgument( CmdLnOption.VM_OS ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_NCPUS}. + * + * @return argument of the command line option {@link CmdLnOption#VM_NCPUS}. + */ + public int getVmNumCpus() + { + final String numCpuArg = this.getArgument( CmdLnOption.VM_NCPUS ); + int numCpus = 0; + + if ( numCpuArg != null ) { + numCpus = Integer.parseInt( numCpuArg ); + } + + return numCpus; + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_MEM}. + * + * @return argument of the command line option {@link CmdLnOption#VM_MEM}. + */ + public String getVmMemory() + { + return this.getArgument( CmdLnOption.VM_MEM ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_HDD0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_HDD0}. + */ + public String getVmDiskFileNameHDD0() + { + return this.getArgument( CmdLnOption.VM_HDD0 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_FLOPPY0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_FLOPPY0}. + */ + public String getVmDiskFileNameFloppy0() + { + return this.getArgument( CmdLnOption.VM_FLOPPY0 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_FLOPPY1}. + * + * @return argument of the command line option {@link CmdLnOption#VM_FLOPPY1}. + */ + public String getVmDiskFileNameFloppy1() + { + return this.getArgument( CmdLnOption.VM_FLOPPY1 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_CDROM0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_CDROM0}. + */ + public String getVmDiskFileNameCdrom0() + { + return this.getArgument( CmdLnOption.VM_CDROM0 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_CDROM1}. + * + * @return argument of the command line option {@link CmdLnOption#VM_CDROM1}. + */ + public String getVmDiskFileNameCdrom1() + { + return this.getArgument( CmdLnOption.VM_CDROM1 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_PARALLEL0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_SERIAL0}. + */ + public String getVmDeviceParallel0() + { + return this.getArgument( CmdLnOption.VM_PARALLEL0 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_SERIAL0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_SERIAL0}. + */ + public String getVmDeviceSerial0() + { + return this.getArgument( CmdLnOption.VM_SERIAL0 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_MAC0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_MAC0}. + */ + public String getVmMacAddress0() + { + return this.getArgument( CmdLnOption.VM_MAC0 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_FSSRC0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_FSSRC0}. + */ + public String getVmFsSrc0() + { + return this.getArgument( CmdLnOption.VM_FSSRC0 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_FSTGT0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_FSTGT0}. + */ + public String getVmFsTgt0() + { + return this.getArgument( CmdLnOption.VM_FSTGT0 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_FSSRC1}. + * + * @return argument of the command line option {@link CmdLnOption#VM_FSSRC1}. + */ + public String getVmFsSrc1() + { + return this.getArgument( CmdLnOption.VM_FSSRC1 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_FSTGT1}. + * + * @return argument of the command line option {@link CmdLnOption#VM_FSTGT1}. + */ + public String getVmFsTgt1() + { + return this.getArgument( CmdLnOption.VM_FSTGT1 ); + } + + /** + * Returns the argument of the command line option {@link CmdLnOption#VM_NVGPUIDS0}. + * + * @return argument of the command line option {@link CmdLnOption#VM_NVGPUIDS0}. + */ + public List<String> getVmNvGpuIds0() + { + final String[] nvidiaPciIdsRaw = this.getArguments( CmdLnOption.VM_NVGPUIDS0 ); + final ArrayList<String> nvidiaPciIds; + + if ( nvidiaPciIdsRaw == null || nvidiaPciIdsRaw.length <= 0 ) { + nvidiaPciIds = new ArrayList<String>(); + } else { + nvidiaPciIds = new ArrayList<String>( Arrays.asList( nvidiaPciIdsRaw ) ); + } + + return nvidiaPciIds; + } + + /** + * Returns the state whether a passthrough of a NVIDIA GPU is required. + * + * @return state whether a passthrough of a NVIDIA GPU is required. + */ + public boolean isNvidiaGpuPassthroughEnabled() + { + return this.getVmNvGpuIds0().size() > 0; + } + + /** + * Command line options for the run-virt QEMU plugin (command line tool). + * + * @author Manuel Bentele + * @version 1.0 + */ + public enum CmdLnOption + { + // @formatter:off + HELP ( 'h', "help", 0, "" ), + DEBUG ( 'b', "debug", 1, "Enable or disable debug mode" ), + VM_CFGINP ( 'i', "vmcfginp", 1, "File name of an existing and filtered Libvirt domain XML configuration file" ), + VM_CFGOUT ( 'o', "vmcfgout", 1, "File name to output a finalized Libvirt domain XML configuration file" ), + VM_NAME ( 'n', "vmname", 1, "Name for the virtual machine" ), + VM_UUID ( 'u', "vmuuid", 1, "UUID for the virtual machine" ), + VM_DSPLNAME ( 'd', "vmdsplname", 1, "Display name for the virtual machine" ), + VM_OS ( 's', "vmos", 1, "Operating system running in the virtual machine" ), + VM_NCPUS ( 'c', "vmncpus", 1, "Number of virtual CPUs for the virtual machine" ), + VM_MEM ( 'm', "vmmem", 1, "Amount of memory for the virtual machine" ), + VM_HDD0 ( 'r', "vmhdd0", 1, "Disk image for the first HDD device" ), + VM_FLOPPY0 ( 'f', "vmfloppy0", 1, "Disk image for the first floppy drive" ), + VM_FLOPPY1 ( 'g', "vmfloppy1", 1, "Disk image for the second floppy drive" ), + VM_CDROM0 ( 'k', "vmcdrom0", 1, "Disk image for the first CDROM drive" ), + VM_CDROM1 ( 'l', "vmcdrom1", 1, "Disk image for the second CDROM drive" ), + VM_PARALLEL0( 'p', "vmparallel0", 1, "Device for the first parallel port interface" ), + VM_SERIAL0 ( 'q', "vmserial0", 1, "Device for the first serial port interface" ), + VM_MAC0 ( 'a', "vmmac0", 1, "MAC address for the first network interface" ), + VM_FSSRC0 ( 't', "vmfssrc0", 1, "Source directory for first file system passthrough (shared folder)" ), + VM_FSTGT0 ( 'e', "vmfstgt0", 1, "Target directory for first file system passthrough (shared folder)" ), + VM_FSSRC1 ( 'v', "vmfssrc1", 1, "Source directory for second file system passthrough (shared folder)" ), + VM_FSTGT1 ( 'w', "vmfstgt1", 1, "Target directory for second file system passthrough (shared folder)" ), + VM_NVGPUIDS0( 'y', "vmnvgpuids0", 2, "PCI device description and address for passthrough of the first Nvidia GPU. " + + "The argument follow the pattern: " + + "\"<VENDOR ID>:<PRODUCT ID>,<PCI DOMAIN>:<PCI DEVICE>:<PCI DEVICE>.<PCI FUNCTION>\"" ); + // @formatter:on + + /** + * Stores the {@link Character} of the short command line option. + */ + private final char shortOption; + + /** + * Stores the {@link String} of the long command line option. + */ + private final String longOption; + + /** + * Stores the number of arguments for the command line option. + */ + private final int numArguments; + + /** + * Stores the textual description of the command line option. + */ + private final String description; + + /** + * Creates a new command line option for the run-virt QEMU plugin (command line tool). + * + * @param shortOption {@link Character} for the short command line option. + * @param longOption {@link String} for the long command line option. + * @param numArguments number of arguments for the command line option. + * @param description textual description of the command line option. + */ + CmdLnOption( char shortOption, String longOption, int numArguments, String description ) + { + this.shortOption = shortOption; + this.longOption = longOption; + this.numArguments = numArguments; + this.description = description; + } + + /** + * Returns the {@link Character} of the short command line option. + * + * @return {@link Character} of the short command line option. + */ + public String getShortOption() + { + return Character.toString( this.shortOption ); + } + + /** + * Returns the {@link String} of the long command line option. + * + * @return {@link String} of the long command line option. + */ + public String getLongOption() + { + return this.longOption; + } + + /** + * Returns the number of arguments for the command line option. + * + * @return number of arguments for the command line option. + */ + public int getNumArguments() + { + return this.numArguments; + } + + /** + * Returns the textual description of the command line option. + * + * @return textual description of the command line option. + */ + public String getDescription() + { + return this.description; + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgsException.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgsException.java new file mode 100644 index 00000000..a327b813 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgsException.java @@ -0,0 +1,25 @@ +package org.openslx.runvirt.plugin.qemu.cmdln; + +/** + * An exception during the parsing of command line arguments. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class CommandLineArgsException extends Exception +{ + /** + * Version number for serialization. + */ + private static final long serialVersionUID = 8371924151602194406L; + + /** + * Creates an command line argument parsing exception including an error message. + * + * @param errorMsg message to describe a specific parsing error. + */ + public CommandLineArgsException( String errorMsg ) + { + super( errorMsg ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericCpu.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericCpu.java new file mode 100644 index 00000000..9d9237c7 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericCpu.java @@ -0,0 +1,57 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.Domain.CpuCheck; +import org.openslx.libvirt.domain.Domain.CpuMode; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic CPU transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericCpu extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "CPU [number of cores, mode, ...]"; + + /** + * Creates a new generic CPU transformation for Libvirt/QEMU virtualization configurations. + */ + public TransformationGenericCpu() + { + super( TransformationGenericCpu.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } else if ( args.getVmNumCpus() < 1 ) { + throw new TransformationException( "Invalid number of CPUs specified! Expected a number n > 0!" ); + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + config.setVCpu( args.getVmNumCpus() ); + config.setCpuMode( CpuMode.HOST_PASSTHROUGH ); + config.setCpuCheck( CpuCheck.PARTIAL ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskCdromDevices.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskCdromDevices.java new file mode 100644 index 00000000..643c40ed --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskCdromDevices.java @@ -0,0 +1,110 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.util.ArrayList; + +import org.openslx.libvirt.domain.Domain; +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.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemuUtils; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic CDROM drive transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericDiskCdromDevices extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "Disk CDROM devices"; + + /** + * Creates a new generic CDROM drive transformation for Libvirt/QEMU virtualization + * configurations. + */ + public TransformationGenericDiskCdromDevices() + { + super( TransformationGenericDiskCdromDevices.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } + } + + /** + * Transforms a CDROM drive in a virtualization configuration selected by its {@code index}. + * + * @param config virtualization configuration for the transformation. + * @param fileName name of the image file for the CDROM drive. + * @param index number of the CDROM drive in the virtualization configuration that is selected. + * @throws TransformationException transformation has failed. + */ + private void transformDiskCdromDevice( Domain config, String fileName, int index ) throws TransformationException + { + final ArrayList<DiskCdrom> devices = config.getDiskCdromDevices(); + final DiskCdrom disk = VirtualizationConfigurationQemuUtils.getArrayIndex( devices, index ); + + if ( disk == null ) { + if ( fileName != null ) { + // CDROM drive does not exist, so create new CDROM drive + final DiskCdrom newDisk = config.addDiskCdromDevice(); + newDisk.setBusType( BusType.SATA ); + String targetDevName = VirtualizationConfigurationQemuUtils.createAlphabeticalDeviceName( "sd", index ); + newDisk.setTargetDevice( targetDevName ); + + if ( fileName.isEmpty() ) { + // remove storage source if empty string is specified to emulate an empty CDROM drive + newDisk.removeStorage(); + } else { + // set disk image file as storage source of the disk CDROM drive + newDisk.setStorage( StorageType.FILE, fileName ); + } + } + } else { + // CDROM drive exists, so update existing CDROM drive + if ( fileName == null ) { + // remove disk storage device if disk image file name is not set + disk.remove(); + } else if ( fileName.isEmpty() ) { + // remove storage source if empty string is specified to emulate an empty CDROM drive + disk.removeStorage(); + } else { + // set disk image file as storage source of the disk CDROM drive + disk.setStorage( StorageType.FILE, fileName ); + } + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // alter CDROM drives + this.transformDiskCdromDevice( config, args.getVmDiskFileNameCdrom0(), 0 ); + this.transformDiskCdromDevice( config, args.getVmDiskFileNameCdrom1(), 1 ); + + // remove all additional disk CDROM devices + final ArrayList<DiskCdrom> devices = config.getDiskCdromDevices(); + for ( int i = 2; i < devices.size(); i++ ) { + devices.get( i ).remove(); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskFloppyDevices.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskFloppyDevices.java new file mode 100644 index 00000000..fe3d3c34 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskFloppyDevices.java @@ -0,0 +1,104 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.util.ArrayList; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Disk.BusType; +import org.openslx.libvirt.domain.device.Disk.StorageType; +import org.openslx.libvirt.domain.device.DiskFloppy; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemuUtils; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic floppy drive transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericDiskFloppyDevices extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "Disk floppy devices"; + + /** + * Creates a new floppy drive transformation for Libvirt/QEMU virtualization configurations. + */ + public TransformationGenericDiskFloppyDevices() + { + super( TransformationGenericDiskFloppyDevices.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } + } + + /** + * Transforms a floppy drive in a virtualization configuration selected by its {@code index}. + * + * @param config virtualization configuration for the transformation. + * @param fileName name of the image file for the floppy drive. + * @param index number of the floppy drive in the virtualization configuration that is selected. + * @throws TransformationException transformation has failed. + */ + private void transformDiskFloppyDevice( Domain config, String fileName, int index ) throws TransformationException + { + final ArrayList<DiskFloppy> devices = config.getDiskFloppyDevices(); + final DiskFloppy disk = VirtualizationConfigurationQemuUtils.getArrayIndex( devices, index ); + + if ( disk == null ) { + if ( fileName != null ) { + // floppy device does not exist, so create new floppy device + final DiskFloppy newDisk = config.addDiskFloppyDevice(); + newDisk.setBusType( BusType.FDC ); + String targetDevName = VirtualizationConfigurationQemuUtils.createAlphabeticalDeviceName( "fd", index ); + newDisk.setTargetDevice( targetDevName ); + + if ( fileName.isEmpty() ) { + newDisk.removeStorage(); + } else { + newDisk.setStorage( StorageType.FILE, fileName ); + } + } + } else { + // floppy device exists, so update existing floppy device + if ( fileName == null ) { + disk.remove(); + } else if ( fileName.isEmpty() ) { + disk.removeStorage(); + } else { + disk.setStorage( StorageType.FILE, fileName ); + } + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // alter floppy drives + this.transformDiskFloppyDevice( config, args.getVmDiskFileNameFloppy0(), 0 ); + this.transformDiskFloppyDevice( config, args.getVmDiskFileNameFloppy1(), 1 ); + + // remove all additional disk storage devices + final ArrayList<DiskFloppy> devices = config.getDiskFloppyDevices(); + for ( int i = 2; i < devices.size(); i++ ) { + devices.get( i ).remove(); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskStorageDevices.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskStorageDevices.java new file mode 100644 index 00000000..9bd1edbb --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskStorageDevices.java @@ -0,0 +1,102 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.util.ArrayList; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Disk.BusType; +import org.openslx.libvirt.domain.device.Disk.StorageType; +import org.openslx.libvirt.domain.device.DiskFloppy; +import org.openslx.libvirt.domain.device.DiskStorage; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemuUtils; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic storage device (HDD, SSD, ...) transformation for Libvirt/QEMU virtualization + * configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericDiskStorageDevices extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "Disk storage devices [HDD, SSD, ...]"; + + /** + * Creates a new storage device (HDD, SSD, ...) transformation for Libvirt/QEMU virtualization + * configurations. + */ + public TransformationGenericDiskStorageDevices() + { + super( TransformationGenericDiskStorageDevices.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } + } + + /** + * Transforms a storage device in a virtualization configuration selected by its {@code index}. + * + * @param config virtualization configuration for the transformation. + * @param fileName name of the image file for the storage device. + * @param index number of the storage device in the virtualization configuration that is + * selected. + * @throws TransformationException transformation has failed. + */ + private void transformDiskStorageDevice( Domain config, String fileName, int index ) throws TransformationException + { + final ArrayList<DiskStorage> devices = config.getDiskStorageDevices(); + final DiskStorage disk = VirtualizationConfigurationQemuUtils.getArrayIndex( devices, index ); + + if ( disk == null ) { + if ( fileName != null && !fileName.isEmpty() ) { + // storage device does not exist, so create new storage device + final DiskFloppy newDisk = config.addDiskFloppyDevice(); + newDisk.setBusType( BusType.VIRTIO ); + String targetDevName = VirtualizationConfigurationQemuUtils.createAlphabeticalDeviceName( "vd", index ); + newDisk.setTargetDevice( targetDevName ); + newDisk.setStorage( StorageType.FILE, fileName ); + } + } else { + // storage device exists, so update existing storage device + if ( fileName == null || fileName.isEmpty() ) { + // remove disk storage device if disk image file name is not set + disk.remove(); + } else { + // set image file of disk storage if disk storage device is available + disk.setStorage( StorageType.FILE, fileName ); + } + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // alter storage device + this.transformDiskStorageDevice( config, args.getVmDiskFileNameHDD0(), 0 ); + + // remove all additional disk storage devices + final ArrayList<DiskStorage> devices = config.getDiskStorageDevices(); + for ( int i = 1; i < devices.size(); i++ ) { + devices.get( i ).remove(); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericFileSystemDevices.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericFileSystemDevices.java new file mode 100644 index 00000000..a4f77b0d --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericFileSystemDevices.java @@ -0,0 +1,107 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.util.ArrayList; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.FileSystem; +import org.openslx.libvirt.domain.device.FileSystem.AccessMode; +import org.openslx.libvirt.domain.device.FileSystem.Type; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemuUtils; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic file system device (shared folder) transformation for Libvirt/QEMU virtualization + * configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericFileSystemDevices extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "File system devices"; + + /** + * Creates a new file system device (shared folder) transformation for Libvirt/QEMU + * virtualization configurations. + */ + public TransformationGenericFileSystemDevices() + { + super( TransformationGenericFileSystemDevices.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } + } + + /** + * Transforms a storage device in a virtualization configuration selected by its {@code index}. + * + * @param config virtualization configuration for the transformation. + * @param source path of the file system source on a host system. + * @param target path of the file system destination in a virtualization guest. + * @param index number of the file system device in the virtualization configuration that is + * selected. + * @throws TransformationException transformation has failed. + */ + private void transformFileSystemDevice( Domain config, String source, String target, int index ) + throws TransformationException + { + final ArrayList<FileSystem> devices = config.getFileSystemDevices(); + final FileSystem fileSystem = VirtualizationConfigurationQemuUtils.getArrayIndex( devices, index ); + + if ( fileSystem == null ) { + // check if file system device source directory is specified + if ( source != null && !source.isEmpty() && target != null && !target.isEmpty() ) { + // file system device does not exist, so create new file system device + final FileSystem newFileSystem = config.addFileSystemDevice(); + newFileSystem.setType( Type.MOUNT ); + newFileSystem.setAccessMode( AccessMode.MAPPED ); + newFileSystem.setSource( source ); + newFileSystem.setTarget( target ); + } + } else { + if ( source == null || source.isEmpty() || target == null || target.isEmpty() ) { + // remove file system device since device source or target is not specified + fileSystem.remove(); + } else { + // change type, access mode, source and target of existing file system device + fileSystem.setType( Type.MOUNT ); + fileSystem.setAccessMode( AccessMode.MAPPED ); + fileSystem.setSource( source ); + fileSystem.setTarget( target ); + } + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // alter file system devices + this.transformFileSystemDevice( config, args.getVmFsSrc0(), args.getVmFsTgt0(), 0 ); + this.transformFileSystemDevice( config, args.getVmFsSrc1(), args.getVmFsTgt1(), 1 ); + + // remove all additional file system devices + final ArrayList<FileSystem> devices = config.getFileSystemDevices(); + for ( int i = 2; i < devices.size(); i++ ) { + devices.get( i ).remove(); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericInterfaceDevices.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericInterfaceDevices.java new file mode 100644 index 00000000..6cf12ce2 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericInterfaceDevices.java @@ -0,0 +1,101 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.util.ArrayList; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Interface; +import org.openslx.libvirt.domain.device.InterfaceBridge; +import org.openslx.libvirt.domain.device.Interface.Model; +import org.openslx.libvirt.domain.device.Interface.Type; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemu; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemuUtils; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic network interface transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericInterfaceDevices extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "Network interface devices"; + + /** + * Creates a new network interface transformation for Libvirt/QEMU virtualization configurations. + */ + public TransformationGenericInterfaceDevices() + { + super( TransformationGenericInterfaceDevices.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } + } + + /** + * Transforms a network interface in a virtualization configuration selected by its + * {@code index}. + * + * @param config virtualization configuration for the transformation. + * @param macAddress MAC address for the network interface. + * @param index number of the network interface in the virtualization configuration that is + * selected. + * @throws TransformationException transformation has failed. + */ + private void transformInterfaceDevice( Domain config, String macAddress, int index ) throws TransformationException + { + final ArrayList<Interface> devices = config.getInterfaceDevices(); + final Interface device = VirtualizationConfigurationQemuUtils.getArrayIndex( devices, index ); + + if ( device == null ) { + if ( macAddress != null && !macAddress.isEmpty() ) { + // create network interface if it does not exists + final InterfaceBridge newDevice = config.addInterfaceBridgeDevice(); + newDevice.setType( Type.BRIDGE ); + newDevice.setModel( Model.VIRTIO ); + newDevice.setMacAddress( macAddress ); + newDevice.setSource( VirtualizationConfigurationQemu.NETWORK_BRIDGE_NAT_DEFAULT ); + } + } else { + if ( macAddress == null || macAddress.isEmpty() ) { + // remove network interface device if MAC address is not set + device.remove(); + } else { + // set MAC address of network interface device if network interface device is available + device.setMacAddress( macAddress ); + } + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // alter network interface + this.transformInterfaceDevice( config, args.getVmMacAddress0(), 0 ); + + // remove all additional disk storage devices + final ArrayList<Interface> devices = config.getInterfaceDevices(); + for ( int i = 1; i < devices.size(); i++ ) { + devices.get( i ).remove(); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericMemory.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericMemory.java new file mode 100644 index 00000000..fce373f7 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericMemory.java @@ -0,0 +1,59 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.math.BigInteger; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.DomainUtils; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic memory transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericMemory extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "Memory [normal, current (balloning)]"; + + /** + * Creates a new memory transformation for Libvirt/QEMU virtualization configurations. + */ + public TransformationGenericMemory() + { + super( TransformationGenericMemory.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } else if ( args.getVmMemory() == null || args.getVmMemory().isEmpty() ) { + throw new TransformationException( "Amount of memory in MiB is not specified!" ); + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + BigInteger memory = DomainUtils.decodeMemory( args.getVmMemory(), "MiB" ); + + config.setMemory( memory ); + config.setCurrentMemory( memory ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericName.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericName.java new file mode 100644 index 00000000..b96793d5 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericName.java @@ -0,0 +1,57 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic name transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericName extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "Name [(display) name]"; + + /** + * Creates a new name transformation for Libvirt/QEMU virtualization configurations. + */ + public TransformationGenericName() + { + super( TransformationGenericName.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } else if ( args.getVmName() == null || args.getVmName().isEmpty() ) { + throw new TransformationException( "Name is not specified!" ); + } else if ( args.getVmDisplayName() == null || args.getVmDisplayName().isEmpty() ) { + throw new TransformationException( "Display name is not specified!" ); + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // alter names in the configuration + config.setName( args.getVmName() ); + config.setTitle( args.getVmDisplayName() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericParallelDevices.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericParallelDevices.java new file mode 100644 index 00000000..08d43ef0 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericParallelDevices.java @@ -0,0 +1,97 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.util.ArrayList; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Parallel; +import org.openslx.libvirt.domain.device.Parallel.Type; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemuUtils; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic parallel device transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericParallelDevices extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "Parallel devices"; + + /** + * Creates a new parallel device transformation for Libvirt/QEMU virtualization configurations. + */ + public TransformationGenericParallelDevices() + { + super( TransformationGenericParallelDevices.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } + } + + /** + * Transforms a parallel device in a virtualization configuration selected by its {@code index}. + * + * @param config virtualization configuration for the transformation. + * @param fileName path to the parallel device file on the host system. + * @param index number of the parallel device in the virtualization configuration that is + * selected. + * @throws TransformationException transformation has failed. + */ + private void transformParallelDevice( Domain config, String fileName, int index ) throws TransformationException + { + final ArrayList<Parallel> devices = config.getParallelDevices(); + final Parallel device = VirtualizationConfigurationQemuUtils.getArrayIndex( devices, index ); + + if ( device == null ) { + // check if device file name is specified + if ( fileName != null ) { + // parallel port device does not exist, so create new parallel port device + final Parallel newDevice = config.addParallelDevice(); + newDevice.setType( Type.DEV ); + newDevice.setSource( fileName ); + } + } else { + if ( fileName == null || fileName.isEmpty() ) { + // remove device since device file is not specified + device.remove(); + } else { + // change type and source of existing parallel port device + device.setType( Type.DEV ); + device.setSource( fileName ); + } + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // alter parallel device + this.transformParallelDevice( config, args.getVmDeviceParallel0(), 0 ); + + // remove all additional parallel devices + final ArrayList<Parallel> devices = config.getParallelDevices(); + for ( int i = 1; i < devices.size(); i++ ) { + devices.get( i ).remove(); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericUuid.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericUuid.java new file mode 100644 index 00000000..43fb6412 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericUuid.java @@ -0,0 +1,53 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationGeneric; + +/** + * Generic UUID transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationGenericUuid extends TransformationGeneric<Domain, CommandLineArgs> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "UUID"; + + /** + * Creates a new UUID transformation for Libvirt/QEMU virtualization configurations. + */ + public TransformationGenericUuid() + { + super( TransformationGenericUuid.NAME ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } else if ( args.getVmUuid() == null || args.getVmUuid().isEmpty() ) { + throw new TransformationException( "UUID is not specified!" ); + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + config.setUuid( args.getVmUuid() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuArchitecture.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuArchitecture.java new file mode 100644 index 00000000..a51c829d --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuArchitecture.java @@ -0,0 +1,261 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.util.ArrayList; +import java.util.List; + +import org.openslx.libvirt.capabilities.Capabilities; +import org.openslx.libvirt.capabilities.guest.Guest; +import org.openslx.libvirt.capabilities.guest.Machine; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.Domain.OsType; +import org.openslx.libvirt.domain.Domain.Type; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.runvirt.plugin.qemu.virtualization.LibvirtHypervisorQemu; +import org.openslx.runvirt.virtualization.LibvirtHypervisorException; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationSpecific; + +/** + * Specific architecture transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationSpecificQemuArchitecture + extends TransformationSpecific<Domain, CommandLineArgs, LibvirtHypervisorQemu> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "QEMU Architecture [CPU architecture, machine type, ...]"; + + /** + * Creates a new architecture transformation for Libvirt/QEMU virtualization configurations. + * + * @param hypervisor Libvirt/QEMU hypervisor. + */ + public TransformationSpecificQemuArchitecture( LibvirtHypervisorQemu virtualizer ) + { + super( TransformationSpecificQemuArchitecture.NAME, virtualizer ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null ) { + throw new TransformationException( "Virtualization configuration is missing!" ); + } + } + + /** + * Queries and returns the capabilities of the Libvirt/QEMU hypervisor. + * + * @return capabilities of the Libvirt/QEMU hypervisor. + * @throws TransformationException failed to query and return the capabilities of the + * Libvirt/QEMU hypervisor. + */ + protected Capabilities getCapabilities() throws TransformationException + { + final Capabilities capabilities; + + try { + capabilities = this.getVirtualizer().getCapabilites(); + } catch ( LibvirtHypervisorException e ) { + final String errorMsg = new String( + "Failed to retrieve host capabilities from QEMU virtualizer: " + e.getLocalizedMessage() ); + throw new TransformationException( errorMsg ); + } + + return capabilities; + } + + /** + * Returns a guest capability of the hypervisor's host system based on a given target + * architecture name. + * + * @param architectureName target architecture of the guest that is returned + * @return guest capability of the hypervisor's host system with target architecture name. + * @throws TransformationException failed to return guest capability of the hypervisor's host. + */ + private Guest getTargetGuestFromArchName( String architectureName ) throws TransformationException + { + final List<Guest> guests = this.getCapabilities().getGuests(); + Guest targetGuest = null; + + if ( architectureName == null ) { + return targetGuest; + } + + for ( Guest guest : guests ) { + final String guestArchitectureName = guest.getArchName(); + if ( architectureName.equals( guestArchitectureName ) ) { + targetGuest = guest; + break; + } + } + + return targetGuest; + } + + /** + * Returns the target machine description of a host system's guest capability based on a given + * target machine name. + * + * @param guest guest capability of a host system. + * @param machineName name of the machine description. + * @return target machine description of a host system's guest capability. + * @throws TransformationException failed to return the target machine description of a host + * system's guest capabilities. + */ + private Machine getTargetMachineFromGuest( Guest guest, String machineName ) throws TransformationException + { + final List<Machine> machines = guest.getArchMachines(); + Machine targetMachine = null; + + if ( machineName == null ) { + return targetMachine; + } + + for ( Machine machine : machines ) { + if ( machineName.equals( machine.getName() ) ) { + targetMachine = machine; + break; + } + } + + return targetMachine; + } + + /** + * Returns the canonical names of a target machine description of a host system's guest + * capability. + * + * @param guest guest capability of a host system. + * @return canonical names of a target machine description of a host system's guest capability. + * @throws TransformationException failed to return the canonical names of a target machine + * description of a host system's guest capability + */ + private List<String> getCanonicalNamesFromTargetMachines( Guest guest ) throws TransformationException + { + final List<Machine> machines = guest.getArchMachines(); + final List<String> canonicalNames = new ArrayList<String>(); + + for ( Machine machine : machines ) { + final String canonicalName = machine.getCanonicalMachine(); + if ( canonicalName != null ) { + canonicalNames.add( canonicalName ); + } + } + + return canonicalNames; + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // get source architecture, machine- and OS type + final String sourceArchitectureName = config.getOsArch(); + final String sourceMachine = config.getOsMachine(); + final OsType sourceOsType = config.getOsType(); + final Type sourceDomainType = config.getType(); + + // check if source architecture is supported by one of the hypervisor's guests + Guest targetGuest = null; + if ( sourceArchitectureName == null ) { + final String errorMsg = new String( "Source architecture is not specified!" ); + throw new TransformationException( errorMsg ); + } else { + targetGuest = this.getTargetGuestFromArchName( sourceArchitectureName ); + if ( targetGuest == null ) { + final String errorMsg = new String( "Source architecture is not supported by the virtualizer!" ); + throw new TransformationException( errorMsg ); + } + } + + // check if source machine is supported by the hypervisor + Machine targetMachine = null; + if ( sourceMachine == null ) { + final String errorMsg = new String( "Source machine type is not specified!" ); + throw new TransformationException( errorMsg ); + } else { + // get all possible machine type for supported source architecture + targetMachine = this.getTargetMachineFromGuest( targetGuest, sourceMachine ); + + if ( targetMachine == null ) { + // source machine is not directly supported by the hypervisor + // check if up- or downgraded version of the chipset is supported by the hypervisor + List<String> targetMachineCanonicalNames = this.getCanonicalNamesFromTargetMachines( targetGuest ); + + // retrieve overwrite chipset name from canonical machine names + String sourceMachineOverwrite = null; + for ( String targetMachineCanonicalName : targetMachineCanonicalNames ) { + if ( sourceMachine.contains( targetMachineCanonicalName ) ) { + sourceMachineOverwrite = targetMachineCanonicalName; + break; + } + } + + // if overwrite available, patch the machine type + if ( sourceMachineOverwrite != null ) { + config.setOsMachine( sourceMachineOverwrite ); + } else { + final String errorMsg = new String( "Source machine type is not supported by the virtualizer!" ); + throw new TransformationException( errorMsg ); + } + } + } + + // check if source OS type is supported by the hypervisor's architecture + if ( sourceOsType == null ) { + final String errorMsg = new String( "OS type is not specified!" ); + throw new TransformationException( errorMsg ); + } else { + if ( !sourceOsType.toString().equals( targetGuest.getOsType().toString() ) ) { + final String errorMsg = new String( "OS type is not supported by the virtualizer!" ); + throw new TransformationException( errorMsg ); + } + } + + // check if source domain type is supported by the hypervisor's architecture + Type targetDomainType = null; + if ( sourceDomainType == null ) { + final String errorMsg = new String( "Source domain type is not specified!" ); + throw new TransformationException( errorMsg ); + } else { + final List<org.openslx.libvirt.capabilities.guest.Domain> targetDomains = targetGuest.getArchDomains(); + + // retrieve supported domain type + for ( org.openslx.libvirt.capabilities.guest.Domain domain : targetDomains ) { + final Type domainType = domain.getType(); + if ( domainType == sourceDomainType ) { + targetDomainType = domainType; + break; + } + } + + // check supported domain type + if ( targetDomainType == null ) { + final String errorMsg = new String( "Source domain type is not supported by the virtualizer!" ); + throw new TransformationException( errorMsg ); + } + } + + // patch path of QEMU emulator binary + final String archEmulator = targetGuest.getArchEmulator(); + if ( archEmulator == null ) { + final String errorMsg = new String( "Emulation of source architecture is not supported by the virtualizer!" ); + throw new TransformationException( errorMsg ); + } else { + config.setDevicesEmulator( targetGuest.getArchEmulator() ); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidia.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidia.java new file mode 100644 index 00000000..a22bf027 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidia.java @@ -0,0 +1,232 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import org.openslx.libvirt.capabilities.Capabilities; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.HostdevPci; +import org.openslx.libvirt.domain.device.HostdevPciDeviceAddress; +import org.openslx.libvirt.domain.device.HostdevPciDeviceDescription; +import org.openslx.libvirt.domain.device.Shmem; +import org.openslx.libvirt.domain.device.Video; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.runvirt.plugin.qemu.virtualization.LibvirtHypervisorQemu; +import org.openslx.runvirt.virtualization.LibvirtHypervisorException; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationSpecific; + +/** + * Specific Nvidia GPU passthrough transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationSpecificQemuGpuPassthroughNvidia + extends TransformationSpecific<Domain, CommandLineArgs, LibvirtHypervisorQemu> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "QEMU GPU passthrough [Nvidia]"; + + /** + * Vendor identifier of PCI devices from Nvidia. + */ + private static final int NVIDIA_PCI_VENDOR_ID = 0x10de; + + /** + * Vendor identifier of the Hyper-V enlightenment for hypervisor shadowing. + */ + public static final String HYPERV_VENDOR_ID = "62776c706277"; + + /** + * Maximum width in pixel of the GPU passthrough rendered display. + */ + private static final long MAX_DISPLAY_WIDTH = 2560; + + /** + * Maximum height in pixel of the GPU passthrough rendered display. + */ + private static final long MAX_DISPLAY_HEIGHT = 1440; + + /** + * Reserved memory for framebuffer meta data of the Looking Glass shared memory device in MiB. + */ + private static final long RESERVED_MEMORY_FRAMEBUFFER = 10; + + /** + * Creates a new Nvidia GPU passthrough transformation for Libvirt/QEMU virtualization + * configurations. + * + * @param hypervisor Libvirt/QEMU hypervisor. + */ + public TransformationSpecificQemuGpuPassthroughNvidia( LibvirtHypervisorQemu hypervisor ) + { + super( TransformationSpecificQemuGpuPassthroughNvidia.NAME, hypervisor ); + } + + /** + * Validates a PCI device description and address of a PCI device from a Nvidia GPU and parses + * the validated PCI device addresses. + * + * @param pciIds textual PCI device description and address to be validated. + * + * @return list of validated and parsed PCI device addresses for a NVIDIA GPU passthrough. + * + * @throws TransformationException validation of PCI device description and address failed. + */ + private static List<HostdevPciDeviceAddress> validateParseNvidiaPciIds( List<String> pciIds ) + throws TransformationException + { + final List<HostdevPciDeviceAddress> parsedPciAddresses = new ArrayList<HostdevPciDeviceAddress>(); + + if ( pciIds != null && pciIds.size() > 0 ) { + // abort if arguments do not follow the pattern: + // + // [0]: <VENDOR ID 0>:<DEVICE ID 0> + // [1]: <PCI DOMAIN 0>:<PCI BUS 0>:<PCI DEVICE 0>.<PCI FUNCTION 0> + // [2]: <VENDOR ID 1>:<DEVICE ID 1> + // [3]: <PCI DOMAIN 1>:<PCI BUS 1>:<PCI DEVICE 1>.<PCI FUNCTION 1> + // ... + // + if ( pciIds.size() % 2 != 0 ) { + throw new TransformationException( + "Arguments of PCI IDs are not follow the pattern for a GPU passthrough!" ); + } + + // parse PCI device description and PCI device address + for ( int i = 0; i < pciIds.size(); i += 2 ) { + // parse vendor and device ID + HostdevPciDeviceDescription deviceDescription = null; + try { + deviceDescription = HostdevPciDeviceDescription.valueOf( pciIds.get( i ) ); + } catch ( IllegalArgumentException e ) { + throw new TransformationException( "Invalid vendor or device ID of the PCI device description!" ); + } + + // validate vendor ID + final int vendorId = deviceDescription.getVendorId(); + if ( TransformationSpecificQemuGpuPassthroughNvidia.NVIDIA_PCI_VENDOR_ID != vendorId ) { + final String errorMsg = "Vendor ID '" + vendorId + "' of the PCI device is not from Nvidia!"; + throw new TransformationException( errorMsg ); + } + + // parse PCI domain, PCI bus, PCI device and PCI function + final HostdevPciDeviceAddress parsedPciAddress = HostdevPciDeviceAddress.valueOf( pciIds.get( i + 1 ) ); + if ( parsedPciAddress != null ) { + parsedPciAddresses.add( parsedPciAddress ); + } + } + } + + return parsedPciAddresses; + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } + + TransformationSpecificQemuGpuPassthroughNvidia.validateParseNvidiaPciIds( args.getVmNvGpuIds0() ); + } + + /** + * Queries and returns the capabilities of the Libvirt/QEMU hypervisor. + * + * @return capabilities of the Libvirt/QEMU hypervisor. + * @throws TransformationException failed to query and return the capabilities of the + * Libvirt/QEMU hypervisor. + */ + protected Capabilities getCapabilities() throws TransformationException + { + Capabilities capabilities = null; + + try { + capabilities = this.getVirtualizer().getCapabilites(); + } catch ( LibvirtHypervisorException e ) { + final String errorMsg = new String( + "Failed to retrieve host capabilities from QEMU virtualizer: " + e.getLocalizedMessage() ); + throw new TransformationException( errorMsg ); + } + + return capabilities; + } + + private static BigInteger roundToNearestPowerOf2( BigInteger value ) + { + BigInteger k = BigInteger.valueOf( 1 ); + + while ( k.compareTo( value ) == -1 ) { + k = k.multiply( BigInteger.valueOf( 2 ) ); + } + + return k; + } + + /** + * Calculates the framebuffer memory size for the Looking Glass shared memory device. + * + * @return framebuffer memory size in bytes for the Looking Glass shared memory device. + */ + private static BigInteger calculateFramebufferSize() + { + final long totalBytesFramebuffer = MAX_DISPLAY_WIDTH * MAX_DISPLAY_HEIGHT * 4 * 2; + final long totalBytesReserved = RESERVED_MEMORY_FRAMEBUFFER * 1048576; + + // round sum of total memory in bytes to nearest power of two + return roundToNearestPowerOf2( BigInteger.valueOf( totalBytesFramebuffer + totalBytesReserved ) ); + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // check if passthrough of Nvidia GPU takes place + if ( args.isNvidiaGpuPassthroughEnabled() ) { + // validate submitted PCI IDs + final List<HostdevPciDeviceAddress> pciDeviceAddresses = TransformationSpecificQemuGpuPassthroughNvidia + .validateParseNvidiaPciIds( args.getVmNvGpuIds0() ); + + // check if IOMMU support is available on the host + if ( !this.getCapabilities().hasHostIommuSupport() ) { + final String errorMsg = "IOMMU support is not available on the hypervisor but required for GPU passthrough!"; + throw new TransformationException( errorMsg ); + } + + // passthrough PCI devices of the GPU + for ( final HostdevPciDeviceAddress pciDeviceAddress : pciDeviceAddresses ) { + final HostdevPci pciDevice = config.addHostdevPciDevice(); + pciDevice.setManaged( true ); + pciDevice.setSource( pciDeviceAddress ); + } + + // add shared memory device for Looking Glass + final Shmem shmemDevice = config.addShmemDevice(); + shmemDevice.setName( "looking-glass" ); + shmemDevice.setModel( Shmem.Model.IVSHMEM_PLAIN ); + shmemDevice.setSize( TransformationSpecificQemuGpuPassthroughNvidia.calculateFramebufferSize() ); + + // enable hypervisor shadowing to avoid error code 43 of Nvidia drivers in virtual machines + config.setFeatureHypervVendorIdValue( TransformationSpecificQemuGpuPassthroughNvidia.HYPERV_VENDOR_ID ); + config.setFeatureHypervVendorIdState( true ); + config.setFeatureKvmHiddenState( true ); + + // disable all software video devices by disable them + for ( Video videoDevice : config.getVideoDevices() ) { + videoDevice.disable(); + } + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuSerialDevices.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuSerialDevices.java new file mode 100644 index 00000000..30f60289 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuSerialDevices.java @@ -0,0 +1,117 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.util.ArrayList; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Serial.Type; +import org.openslx.libvirt.domain.device.Serial; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.runvirt.plugin.qemu.virtualization.LibvirtHypervisorQemu; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemuUtils; +import org.openslx.virtualization.configuration.transformation.TransformationException; +import org.openslx.virtualization.configuration.transformation.TransformationSpecific; + +/** + * Specific serial device transformation for Libvirt/QEMU virtualization configurations. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class TransformationSpecificQemuSerialDevices + extends TransformationSpecific<Domain, CommandLineArgs, LibvirtHypervisorQemu> +{ + /** + * Name of the configuration transformation. + */ + private static final String NAME = "Serial devices"; + + /** + * Creates a new serial device transformation for Libvirt/QEMU virtualization configurations. + * + * @param hypervisor Libvirt/QEMU hypervisor. + */ + public TransformationSpecificQemuSerialDevices( LibvirtHypervisorQemu hypervisor ) + { + super( TransformationSpecificQemuSerialDevices.NAME, hypervisor ); + } + + /** + * Validates a virtualization configuration and input arguments for this transformation. + * + * @param config virtualization configuration for the validation. + * @param args input arguments for the validation. + * @throws TransformationException validation has failed. + */ + private void validateInputs( Domain config, CommandLineArgs args ) throws TransformationException + { + if ( config == null || args == null ) { + throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); + } + } + + /** + * Returns all serial devices from a virtualization configuration that link to a host system's + * serial device. + * + * @param config virtualization configuration. + * @return all serial devices that link to a host system's serial device. + */ + private ArrayList<Serial> getSerialDevDevices( Domain config ) + { + final ArrayList<Serial> devices = config.getSerialDevices(); + final Predicate<Serial> byDeviceTypeDev = device -> device.getType() == Type.DEV; + + return devices.stream().filter( byDeviceTypeDev ).collect( Collectors.toCollection( ArrayList::new ) ); + } + + /** + * Transforms a serial device in a virtualization configuration selected by its {@code index}. + * + * @param config virtualization configuration for the transformation. + * @param fileName path to the serial device file on the host system. + * @param index number of the serial device in the virtualization configuration that is selected. + * @throws TransformationException transformation has failed. + */ + private void transformSerialDevice( Domain config, String fileName, int index ) throws TransformationException + { + final ArrayList<Serial> devices = this.getSerialDevDevices( config ); + final Serial device = VirtualizationConfigurationQemuUtils.getArrayIndex( devices, index ); + + if ( device == null ) { + // check if device file name is specified + if ( fileName != null && !fileName.isEmpty() ) { + // serial port device is not available, so create new serial port device + final Serial newDevice = config.addSerialDevice(); + newDevice.setType( Type.DEV ); + newDevice.setSource( fileName ); + } + } else { + if ( fileName == null || fileName.isEmpty() ) { + // remove serial port device if device file name is not set + device.remove(); + } else { + // set type and source of existing serial port device + device.setType( Type.DEV ); + device.setSource( fileName ); + } + } + } + + @Override + public void transform( Domain config, CommandLineArgs args ) throws TransformationException + { + // validate configuration and input arguments + this.validateInputs( config, args ); + + // alter serial device + this.transformSerialDevice( config, args.getVmDeviceSerial0(), 0 ); + + // remove all additional serial devices + final ArrayList<Serial> devices = this.getSerialDevDevices( config ); + for ( int i = 1; i < devices.size(); i++ ) { + devices.get( i ).remove(); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/virtualization/LibvirtHypervisorQemu.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/virtualization/LibvirtHypervisorQemu.java new file mode 100644 index 00000000..34cf33cf --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/virtualization/LibvirtHypervisorQemu.java @@ -0,0 +1,63 @@ +package org.openslx.runvirt.plugin.qemu.virtualization; + +import org.openslx.runvirt.virtualization.LibvirtHypervisor; +import org.openslx.runvirt.virtualization.LibvirtHypervisorException; + +/** + * Representation of the Libvirt QEMU hypervisor backend. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class LibvirtHypervisorQemu extends LibvirtHypervisor +{ + /** + * Creates a new Libvirt QEMU hypervisor backend and connects to the specified backend. + * + * @param type session type of the connection to the Libvirt QEMU hypervisor backend. + * @throws LibvirtHypervisorException failed to connect to the Libvirt QEMU hypervisor backend. + */ + public LibvirtHypervisorQemu( QemuSessionType type ) throws LibvirtHypervisorException + { + super( type.getConnectionUri() ); + } + + /** + * Type of Libvirt QEMU hypervisor backend session. + * + * @author Manuel Bentele + * @version 1.0 + */ + public enum QemuSessionType + { + // @formatter:off + LOCAL_SYSTEM_SESSION( "qemu:///system" ), + LOCAL_USER_SESSION ( "qemu:///session" ); + // @formatter:on + + /** + * Connection URI of the QEMU session type. + */ + private final String connectionUri; + + /** + * Creates a new QEMU session type. + * + * @param connectionUri URI for the connection of the session. + */ + QemuSessionType( String connectionUri ) + { + this.connectionUri = connectionUri; + } + + /** + * Returns the URI of the connection for the session. + * + * @return URI of the connection for the session. + */ + public String getConnectionUri() + { + return this.connectionUri; + } + } +} 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 } ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtHypervisor.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtHypervisor.java new file mode 100644 index 00000000..757fc706 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtHypervisor.java @@ -0,0 +1,209 @@ +package org.openslx.runvirt.virtualization; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.List; + +import org.libvirt.Connect; +import org.libvirt.LibvirtException; +import org.openslx.libvirt.capabilities.Capabilities; +import org.openslx.libvirt.xml.LibvirtXmlDocumentException; +import org.openslx.libvirt.xml.LibvirtXmlSerializationException; +import org.openslx.libvirt.xml.LibvirtXmlValidationException; +import org.openslx.virtualization.Version; + +/** + * Representation of a Libvirt hypervisor backend (e.g. QEMU or VMware). + * <p> + * The representation allows to connect to a running Libvirt service and query the host system's + * capabilities or manage virtual machines. + * + * @implNote This class is the abstract representation to implement various Libvirt hypervisor + * backends using inheritance. + * + * @author Manuel Bentele + * @version 1.0 + */ +public abstract class LibvirtHypervisor implements Closeable +{ + /** + * Connection to a Libvirt hypervisor backend. + */ + protected Connect hypervisor = null; + + /** + * List of registered machines on the Libvirt hypervisor backend. + */ + private List<LibvirtVirtualMachine> machines; + + /** + * Creates a new Libvirt hypervisor backend specified by an URI and connects to the specified + * backend. + * + * @param connectionUri URI of a specific Libvirt hypervisor backend. + * @throws LibvirtHypervisorException failed to connect to the specified Libvirt hypervisor + * backend. + */ + public LibvirtHypervisor( String connectionUri ) throws LibvirtHypervisorException + { + this.connect( connectionUri ); + this.machines = new ArrayList<LibvirtVirtualMachine>(); + } + + /** + * Connects to the Libvirt hypervisor backend specified by an URI. + * + * @param connectionUri URI of a specific Libvirt hypervisor backend. + * @throws LibvirtHypervisorException failed to connect to the specified Libvirt hypervisor + * backend. + */ + protected void connect( String connectionUri ) throws LibvirtHypervisorException + { + try { + this.hypervisor = new Connect( connectionUri ); + } catch ( LibvirtException e ) { + throw new LibvirtHypervisorException( e.getLocalizedMessage() ); + } + } + + /** + * Returns the URI of the connection to the Libvirt hypervisor backend. + * + * @return URI of the connection to the hypervisor. + * @throws LibvirtHypervisorException failed to return the connection URI of the Libvirt + * hypervisor backend. + */ + public String getConnectionUri() throws LibvirtHypervisorException + { + String connectionUri = null; + + try { + connectionUri = this.hypervisor.getURI(); + } catch ( LibvirtException e ) { + throw new LibvirtHypervisorException( e.getLocalizedMessage() ); + } + + return connectionUri; + } + + /** + * Returns the queried Libvirt hypervisor's host system capabilities. + * + * @return queried Libvirt hypervisor's host system capabilities. + * @throws LibvirtHypervisorException failed to query and return the Libvirt hypervisor's host + * system capabilities. + */ + public Capabilities getCapabilites() throws LibvirtHypervisorException + { + Capabilities hypervisorCapabilities = null; + + try { + final String hypervisorCapabilitiesString = this.hypervisor.getCapabilities(); + hypervisorCapabilities = new Capabilities( hypervisorCapabilitiesString ); + } catch ( LibvirtException | LibvirtXmlDocumentException | LibvirtXmlSerializationException + | LibvirtXmlValidationException e ) { + throw new LibvirtHypervisorException( e.getLocalizedMessage() ); + } + + return hypervisorCapabilities; + } + + /** + * Returns the version of the Libvirt hypervisor backend. + * + * @return version of the Libvirt hypervisor backend. + * @throws LibvirtHypervisorException failed to get the version of the Libvirt hypervisor + * backend. + */ + public Version getVersion() throws LibvirtHypervisorException + { + long hypervisorVersionRaw = 0; + Version hypervisorVersion = null; + + try { + hypervisorVersionRaw = this.hypervisor.getVersion(); + } catch ( LibvirtException e ) { + throw new LibvirtHypervisorException( e.getLocalizedMessage() ); + } + + if ( hypervisorVersionRaw > 0 ) { + final short major = Long.valueOf( hypervisorVersionRaw / Long.valueOf( 1000000 ) ).shortValue(); + hypervisorVersionRaw %= Long.valueOf( 1000000 ); + final short minor = Long.valueOf( hypervisorVersionRaw / Long.valueOf( 1000 ) ).shortValue(); + hypervisorVersion = new Version( major, minor ); + } + + return hypervisorVersion; + } + + /** + * Register a virtual machine by the Libvirt hypervisor based on a virtualization configuration. + * + * @param vmConfiguration virtualization configuration for the virtual machine. + * @return instance of the registered and defined virtual machine. + * @throws LibvirtHypervisorException failed to register and define virtual machine. + */ + public LibvirtVirtualMachine registerVm( org.openslx.libvirt.domain.Domain vmConfiguration ) + throws LibvirtHypervisorException + { + final String xmlVmConfiguration = vmConfiguration.toString(); + org.libvirt.Domain internalConfiguration = null; + + try { + internalConfiguration = this.hypervisor.domainDefineXML( xmlVmConfiguration ); + } catch ( LibvirtException e ) { + throw new LibvirtHypervisorException( e.getLocalizedMessage() ); + } + + final LibvirtVirtualMachine vm = new LibvirtVirtualMachine( internalConfiguration, vmConfiguration ); + this.machines.add( vm ); + + return vm; + } + + /** + * Deregisters an already registered virtual machine by the Libvirt hypervisor. + * + * @param vm virtual machine that should be deregistered + * @throws LibvirtHypervisorException failed to deregister virtual machine by the Libvirt + * hypervisor. + * @throws LibvirtVirtualMachineException failed to check and stop the virtual machine. + */ + public void deregisterVm( LibvirtVirtualMachine vm ) + throws LibvirtHypervisorException, LibvirtVirtualMachineException + { + // stop virtual machine if machine is running + if ( vm.isRunning() ) { + vm.stop(); + } + + // deregister and remove virtual machine from hypervisor + try { + vm.getLibvirtDomain().undefine(); + } catch ( LibvirtException e ) { + throw new LibvirtHypervisorException( e.getLocalizedMessage() ); + } + + this.machines.remove( vm ); + } + + @Override + public void close() + { + // deregister all VMs defined on the hypervisor + for ( LibvirtVirtualMachine vm : this.machines ) { + try { + this.deregisterVm( vm ); + } catch ( LibvirtHypervisorException | LibvirtVirtualMachineException e ) { + e.printStackTrace(); + } + } + + // close connection to the hypervisor + try { + this.hypervisor.close(); + } catch ( LibvirtException e ) { + e.printStackTrace(); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtHypervisorException.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtHypervisorException.java new file mode 100644 index 00000000..64ae6b20 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtHypervisorException.java @@ -0,0 +1,25 @@ +package org.openslx.runvirt.virtualization; + +/** + * An exception of a Libvirt hypervisor error during acquiring the hypervisor's functionality. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class LibvirtHypervisorException extends Exception +{ + /** + * Version for serialization. + */ + private static final long serialVersionUID = -3631452625806770209L; + + /** + * Creates a Libvirt hypervisor exception including an error message. + * + * @param errorMsg message to describe a specific Libvirt hypervisor error. + */ + LibvirtHypervisorException( String errorMsg ) + { + super( errorMsg ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtVirtualMachine.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtVirtualMachine.java new file mode 100644 index 00000000..3bcec2f8 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtVirtualMachine.java @@ -0,0 +1,150 @@ +package org.openslx.runvirt.virtualization; + +import org.libvirt.Domain; +import org.libvirt.LibvirtException; + +/** + * Representation of a Libvirt virtual machine. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class LibvirtVirtualMachine +{ + /** + * Internal Libvirt virtualization configuration of the virtual machine. + */ + private Domain domain; + + /** + * Libvirt virtualization configuration of the virtual machine. + */ + private org.openslx.libvirt.domain.Domain configuration; + + /** + * Creates a new Libvirt virtual machine specified by a virtualization configuration. + * + * @param internalConfiguration internal Libvirt virtualization configuration to specify the + * Libvirt virtual machine. + * @param configuration Libvirt virtualization configuration to specify the Libvirt virtual + * machine. + */ + LibvirtVirtualMachine( Domain internalConfiguration, org.openslx.libvirt.domain.Domain configuration ) + { + this.domain = internalConfiguration; + this.configuration = configuration; + } + + /** + * Returns the internal Libvirt virtualization configuration of the Libvirt virtual machine. + * + * @return internal Libvirt virtualization configuration of the Libvirt virtual machine. + */ + Domain getLibvirtDomain() + { + return this.domain; + } + + /** + * Returns the Libvirt virtualization configuration of the Libvirt virtual machine. + * + * @return Libvirt virtualization configuration of the Libvirt virtual machine. + */ + public org.openslx.libvirt.domain.Domain getConfiguration() + { + return this.configuration; + } + + /** + * Checks if the Libvirt virtual machine is running. + * + * @return state of the Libvirt virtual machine whether it is running or not. + * @throws LibvirtVirtualMachineException failed to check if Libvirt machine is running or not. + */ + public boolean isRunning() throws LibvirtVirtualMachineException + { + int state = 0; + + try { + state = this.domain.isActive(); + } catch ( LibvirtException e ) { + throw new LibvirtVirtualMachineException( e.getLocalizedMessage() ); + } + + return ( state == 0 ) ? false : true; + } + + /** + * Starts the Libvirt virtual machine. + * + * @throws LibvirtVirtualMachineException failed to start the Libvirt virtual machine. + */ + public void start() throws LibvirtVirtualMachineException + { + if ( !this.isRunning() ) { + try { + this.domain.create(); + } catch ( LibvirtException e ) { + throw new LibvirtVirtualMachineException( e.getLocalizedMessage() ); + } + } + } + + /** + * Stops the Libvirt virtual machine. + * + * @throws LibvirtVirtualMachineException failed to stop the Libvirt virtual machine. + */ + public void stop() throws LibvirtVirtualMachineException + { + if ( this.isRunning() ) { + try { + this.domain.shutdown(); + } catch ( LibvirtException e ) { + throw new LibvirtVirtualMachineException( e.getLocalizedMessage() ); + } + } + } + + /** + * Suspends the Libvirt virtual machine. + * + * @throws LibvirtVirtualMachineException failed to suspend the Libvirt virtual machine. + */ + public void suspend() throws LibvirtVirtualMachineException + { + try { + this.domain.suspend(); + } catch ( LibvirtException e ) { + throw new LibvirtVirtualMachineException( e.getLocalizedMessage() ); + } + } + + /** + * Resumes the Libvirt virtual machine. + * + * @throws LibvirtVirtualMachineException faild to resume the Libvirt virtual machine. + */ + public void resume() throws LibvirtVirtualMachineException + { + try { + this.domain.resume(); + } catch ( LibvirtException e ) { + throw new LibvirtVirtualMachineException( e.getLocalizedMessage() ); + } + } + + /** + * Reboot the Libvirt virtual machine. + * + * @throws LibvirtVirtualMachineException failed to reboot the Libvirt virtual machine. + */ + public void reboot() throws LibvirtVirtualMachineException + { + try { + this.domain.reboot( 0 ); + } catch ( LibvirtException e ) { + throw new LibvirtVirtualMachineException( e.getLocalizedMessage() ); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtVirtualMachineException.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtVirtualMachineException.java new file mode 100644 index 00000000..dd490be3 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/virtualization/LibvirtVirtualMachineException.java @@ -0,0 +1,25 @@ +package org.openslx.runvirt.virtualization; + +/** + * An exception of a Libvirt virtual machine error during controlling the virtual machine. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class LibvirtVirtualMachineException extends Exception +{ + /** + * Version for serialization. + */ + private static final long serialVersionUID = -5371327391243047616L; + + /** + * Creates a Libvirt virtual machine exception including an error message. + * + * @param errorMsg message to describe a specific Libvirt virtual machine error. + */ + public LibvirtVirtualMachineException( String errorMsg ) + { + super( errorMsg ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/AppTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/AppTest.java new file mode 100644 index 00000000..d0eef82a --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/AppTest.java @@ -0,0 +1,202 @@ +package org.openslx.runvirt.plugin.qemu; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs.CmdLnOption; + +import com.ginsberg.junit.exit.ExpectSystemExit; +import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; + +public class AppTest +{ + @BeforeAll + private static void setUp() + { + // disable logging with log4j + LogManager.getRootLogger().setLevel( Level.OFF ); + } + + @Nested + public class CmdLnTest + { + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + private final ByteArrayOutputStream err = new ByteArrayOutputStream(); + + private void setUp() + { + // redirect output and error stream content + System.setOut( new PrintStream( this.out ) ); + System.setErr( new PrintStream( this.err ) ); + } + + @Test + @DisplayName( "Test ouput of correct 'help' command line option (short version)" ) + @ExpectSystemExit + public void testCmdLnOptionHelpShortCorrect() + { + String[] argsShortHelpOptionCorrect = { "-" + CmdLnOption.HELP.getShortOption() }; + + this.setUp(); + + // test correct usage of the short help option + try { + App.main( argsShortHelpOptionCorrect ); + } catch ( Exception e ) { + // do nothing and check output afterwards + } + + final String shortHelpOptionCorrectOutput = new String( this.out.toString() ); + final String shortHelpOptionCorrectErrOutput = new String( this.err.toString() ); + assertTrue( shortHelpOptionCorrectOutput.contains( "usage" ) ); + assertTrue( shortHelpOptionCorrectOutput.contains( App.APP_NAME ) ); + assertTrue( shortHelpOptionCorrectOutput.contains( App.APP_INFO ) ); + assertTrue( shortHelpOptionCorrectOutput.contains( App.APP_DESC ) ); + + // test that no error was logged and output is available + assertEquals( 2503, shortHelpOptionCorrectOutput.length() ); + assertEquals( 0, shortHelpOptionCorrectErrOutput.length() ); + } + + @Test + @DisplayName( "Test ouput of correct 'help' command line option (long version)" ) + @ExpectSystemExit + public void testCmdLnOptionHelpLongCorrect() + { + String[] argsLongHelpOptionCorrect = { "--" + CmdLnOption.HELP.getLongOption() }; + + this.setUp(); + + // test correct usage of the long help option + try { + App.main( argsLongHelpOptionCorrect ); + } catch ( Exception e ) { + // do nothing and check output afterwards + } + + final String longHelpOptionCorrectOutput = this.out.toString(); + final String longHelpOptionCorrectErrOutput = this.err.toString(); + assertTrue( longHelpOptionCorrectOutput.contains( "usage" ) ); + assertTrue( longHelpOptionCorrectOutput.contains( App.APP_NAME ) ); + assertTrue( longHelpOptionCorrectOutput.contains( App.APP_INFO ) ); + assertTrue( longHelpOptionCorrectOutput.contains( App.APP_DESC ) ); + + // test that no error was logged and output is available + assertEquals( 2503, longHelpOptionCorrectOutput.length() ); + assertEquals( 0, longHelpOptionCorrectErrOutput.length() ); + } + + @Test + @DisplayName( "Test ouput of incorrect 'help' command line option (short version)" ) + @ExpectSystemExit + public void testCmdLnOptionHelpShortIncorrect() + { + String[] argsShortHelpOptionIncorrect = { "---" + CmdLnOption.HELP.getShortOption() }; + + this.setUp(); + + // test incorrect usage of the short help option + try { + App.main( argsShortHelpOptionIncorrect ); + } catch ( Exception e ) { + // do nothing and check output afterwards + } + + final String shortHelpOptionIncorrectOutput = this.out.toString(); + final String shortHelpOptionIncorrectErrOutput = this.err.toString(); + assertTrue( shortHelpOptionIncorrectOutput.contains( "usage" ) ); + assertTrue( shortHelpOptionIncorrectOutput.contains( App.APP_NAME ) ); + assertTrue( shortHelpOptionIncorrectOutput.contains( App.APP_INFO ) ); + assertTrue( shortHelpOptionIncorrectOutput.contains( App.APP_DESC ) ); + + // test that error was logged and output is available + assertEquals( 2503, shortHelpOptionIncorrectOutput.length() ); + assertEquals( 0, shortHelpOptionIncorrectErrOutput.length() ); + } + + @Test + @DisplayName( "Test ouput of incorrect 'help' command line option (long version)" ) + @ExpectSystemExit + public void testCmdLnOptionHelpLongIncorrect() + { + String[] argsLongHelpOptionIncorrect = { "---" + CmdLnOption.HELP.getLongOption() }; + + this.setUp(); + + // test incorrect usage of the long help option + try { + App.main( argsLongHelpOptionIncorrect ); + } catch ( Exception e ) { + // do nothing and check output afterwards + } + + final String longHelpOptionIncorrectOutput = this.out.toString(); + final String longHelpOptionIncorrectErrOutput = this.err.toString(); + assertTrue( longHelpOptionIncorrectOutput.contains( "usage" ) ); + assertTrue( longHelpOptionIncorrectOutput.contains( App.APP_NAME ) ); + assertTrue( longHelpOptionIncorrectOutput.contains( App.APP_INFO ) ); + assertTrue( longHelpOptionIncorrectOutput.contains( App.APP_DESC ) ); + + // test that error was logged and output is available + assertEquals( 2503, longHelpOptionIncorrectOutput.length() ); + assertEquals( 0, longHelpOptionIncorrectErrOutput.length() ); + } + + @Test + @DisplayName( "Test exit status of application invoked with correct 'help' command line option (short version)" ) + @ExpectSystemExitWithStatus( 0 ) + public void testCmdLnOptionHelpShortCorrectExit() + { + String[] argsShortHelpOptionCorrect = { "-" + CmdLnOption.HELP.getShortOption() }; + + this.setUp(); + + App.main( argsShortHelpOptionCorrect ); + } + + @Test + @DisplayName( "Test exit status of application invoked with correct 'help' command line option (long version)" ) + @ExpectSystemExitWithStatus( 0 ) + public void testCmdLnOptionHelpLongCorrectExit() + { + String[] argsLongHelpOptionCorrect = { "--" + CmdLnOption.HELP.getLongOption() }; + + this.setUp(); + + App.main( argsLongHelpOptionCorrect ); + } + + @Test + @DisplayName( "Test exit status of application invoked with incorrect 'help' command line option (short version)" ) + @ExpectSystemExitWithStatus( 1 ) + public void testCmdLnOptionHelpShortIncorrectExit() + { + String[] argsShortHelpOptionCorrect = { "---" + CmdLnOption.HELP.getShortOption() }; + + this.setUp(); + + App.main( argsShortHelpOptionCorrect ); + } + + @Test + @DisplayName( "Test exit status of application invoked with incorrect 'help' command line option (long version)" ) + @ExpectSystemExitWithStatus( 1 ) + public void testCmdLnOptionHelpLongIncorrectExit() + { + String[] argsLongHelpOptionCorrect = { "---" + CmdLnOption.HELP.getLongOption() }; + + this.setUp(); + + App.main( argsLongHelpOptionCorrect ); + } + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgsTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgsTest.java new file mode 100644 index 00000000..77522bd6 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgsTest.java @@ -0,0 +1,752 @@ +package org.openslx.runvirt.plugin.qemu.cmdln; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs.CmdLnOption; + +public class CommandLineArgsTest +{ + // @formatter:off + public static final String CMDLN_PREFIX_OPTION_SHORT = "-"; + public static final String CMDLN_PREFIX_OPTION_LONG = "--"; + + private static final String CMDLN_TEST_DEBUG_OFF = "false"; + private static final String CMDLN_TEST_DEBUG_ON = "true"; + private static final String CMDLN_TEST_NAME = "test"; + private static final String CMDLN_TEST_FILENAME = System.getProperty( "user.dir" ) + File.separator + CMDLN_TEST_NAME; + private static final String CMDLN_TEST_UUID = "c9570672-cbae-4cbd-801a-881b281b8d79"; + private static final String CMDLN_TEST_OS = "Windows 10 (x64)"; + private static final int CMDLN_TEST_NCPUS = 4; + private static final String CMDLN_TEST_MEM = "1024"; + private static final String CMDLN_TEST_PARPORT = "/dev/parport0"; + private static final String CMDLN_TEST_SERPORT = "/dev/ttyS0"; + private static final String CMDLN_TEST_MAC = "02:42:8e:77:1b:e6"; + private static final String CMDLN_TEST_NVGPU_DESC = "10de:0ff9"; + private static final String CMDLN_TEST_NVGPU_ADDR = "0000:00:01.0"; + // @formatter:on + + @Test + @DisplayName( "Test the parsing of wrong command line options" ) + public void testCmdlnOptionsIncorrect() + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + "hello", + "world argument", + CMDLN_PREFIX_OPTION_LONG + "info", + "description" + }; + + CommandLineArgs cmdLn = new CommandLineArgs(); + + assertThrows( CommandLineArgsException.class, () -> cmdLn.parseCmdLnArgs( args ) ); + } + + @Test + @DisplayName( "Test the parsing of the help command line option (short version)" ) + public void testCmdlnOptionHelpShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.HELP.getShortOption() + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertTrue( cmdLn.isHelpAquired() ); + } + + @Test + @DisplayName( "Test the parsing of the help command line option (long version)" ) + public void testCmdlnOptionHelpLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.HELP.getLongOption() + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertTrue( cmdLn.isHelpAquired() ); + } + + @Test + @DisplayName( "Test the parsing of the enabled debug command line option (short version)" ) + public void testCmdlnOptionDebugEnabledShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.DEBUG.getShortOption(), + CMDLN_TEST_DEBUG_ON + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertTrue( cmdLn.isDebugEnabled() ); + } + + @Test + @DisplayName( "Test the parsing of the enabled debug command line option (long version)" ) + public void testCmdlnOptionDebugEnabledLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.DEBUG.getLongOption(), + CMDLN_TEST_DEBUG_ON + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertTrue( cmdLn.isDebugEnabled() ); + } + + @Test + @DisplayName( "Test the parsing of the disabled debug command line option (short version)" ) + public void testCmdlnOptionDebugDisabledShort() throws CommandLineArgsException + { + final String[] argsDebugOff = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.DEBUG.getShortOption(), + CMDLN_TEST_DEBUG_OFF + }; + + final String[] argsDebugMissing = {}; + + CommandLineArgs cmdLnDebugOff = new CommandLineArgs( argsDebugOff ); + CommandLineArgs cmdLnDebugMissing = new CommandLineArgs( argsDebugMissing ); + + assertFalse( cmdLnDebugOff.isDebugEnabled() ); + assertFalse( cmdLnDebugMissing.isDebugEnabled() ); + } + + @Test + @DisplayName( "Test the parsing of the disabled debug command line option (long version)" ) + public void testCmdlnOptionDebugDisabledLong() throws CommandLineArgsException + { + final String[] argsDebugOff = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.DEBUG.getLongOption(), + CMDLN_TEST_DEBUG_OFF + }; + + final String[] argsDebugMissing = {}; + + CommandLineArgs cmdLnDebugOff = new CommandLineArgs( argsDebugOff ); + CommandLineArgs cmdLnDebugMissing = new CommandLineArgs( argsDebugMissing ); + + assertFalse( cmdLnDebugOff.isDebugEnabled() ); + assertFalse( cmdLnDebugMissing.isDebugEnabled() ); + } + + @Test + @DisplayName( "Test the parsing of the VM configuration input command line option (short version)" ) + public void testCmdlnOptionVmCfgInpShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_CFGINP.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmCfgInpFileName() ); + } + + @Test + @DisplayName( "Test the parsing of the VM configuration input command line option (long version)" ) + public void testCmdlnOptionVmCfgInpLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_CFGINP.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmCfgInpFileName() ); + } + + @Test + @DisplayName( "Test the parsing of the VM configuration output command line option (short version)" ) + public void testCmdlnOptionVmCfgOutShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_CFGOUT.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmCfgOutFileName() ); + } + + @Test + @DisplayName( "Test the parsing of the VM configuration output command line option (long version)" ) + public void testCmdlnOptionVmCfgOutLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_CFGOUT.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmCfgOutFileName() ); + } + + @Test + @DisplayName( "Test the parsing of the VM name command line option (short version)" ) + public void testCmdlnOptionVmNameShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_NAME.getShortOption(), + CMDLN_TEST_NAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NAME, cmdLn.getVmName() ); + } + + @Test + @DisplayName( "Test the parsing of the VM name command line option (long version)" ) + public void testCmdlnOptionVmNameLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_NAME.getLongOption(), + CMDLN_TEST_NAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NAME, cmdLn.getVmName() ); + } + + @Test + @DisplayName( "Test the parsing of the VM UUID command line option (short version)" ) + public void testCmdlnOptionVmUuidShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_UUID.getShortOption(), + CMDLN_TEST_UUID + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_UUID, cmdLn.getVmUuid() ); + } + + @Test + @DisplayName( "Test the parsing of the VM UUID command line option (long version)" ) + public void testCmdlnOptionVmUuidLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_UUID.getLongOption(), + CMDLN_TEST_UUID + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_UUID, cmdLn.getVmUuid() ); + } + + @Test + @DisplayName( "Test the parsing of the VM display name command line option (short version)" ) + public void testCmdlnOptionVmDsplNameShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_DSPLNAME.getShortOption(), + CMDLN_TEST_NAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NAME, cmdLn.getVmDisplayName() ); + } + + @Test + @DisplayName( "Test the parsing of the VM display name command line option (long version)" ) + public void testCmdlnOptionVmDsplNameLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_DSPLNAME.getLongOption(), + CMDLN_TEST_NAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NAME, cmdLn.getVmDisplayName() ); + } + + @Test + @DisplayName( "Test the parsing of the VM OS command line option (short version)" ) + public void testCmdlnOptionVmOsShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_OS.getShortOption(), + CMDLN_TEST_OS + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_OS, cmdLn.getVmOperatingSystem() ); + } + + @Test + @DisplayName( "Test the parsing of the VM OS command line option (long version)" ) + public void testCmdlnOptionVmOsLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_OS.getLongOption(), + CMDLN_TEST_OS + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_OS, cmdLn.getVmOperatingSystem() ); + } + + @Test + @DisplayName( "Test the parsing of the VM CPU number command line option (short version)" ) + public void testCmdlnOptionVmNCpusShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_NCPUS.getShortOption(), + Integer.toString( CMDLN_TEST_NCPUS ) + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NCPUS, cmdLn.getVmNumCpus() ); + } + + @Test + @DisplayName( "Test the parsing of the VM CPU number command line option (long version)" ) + public void testCmdlnOptionVmNCpusLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_NCPUS.getLongOption(), + Integer.toString( CMDLN_TEST_NCPUS ) + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NCPUS, cmdLn.getVmNumCpus() ); + } + + @Test + @DisplayName( "Test the parsing of the VM memory command line option (short version)" ) + public void testCmdlnOptionVmMemShort() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_MEM.getShortOption(), + CMDLN_TEST_MEM + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_MEM, cmdLn.getVmMemory() ); + } + + @Test + @DisplayName( "Test the parsing of the VM memory command line option (long version)" ) + public void testCmdlnOptionVmMemLong() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_MEM.getLongOption(), + CMDLN_TEST_MEM + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_MEM, cmdLn.getVmMemory() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first HDD disk image command line option (short version)" ) + public void testCmdlnOptionVmHdd0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_HDD0.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameHDD0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first HDD disk image command line option (long version)" ) + public void testCmdlnOptionVmHdd0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_HDD0.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameHDD0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first floppy disk image command line option (short version)" ) + public void testCmdlnOptionVmFloppy0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_FLOPPY0.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameFloppy0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first floppy disk image command line option (long version)" ) + public void testCmdlnOptionVmFloppy0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FLOPPY0.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameFloppy0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM second floppy disk image command line option (short version)" ) + public void testCmdlnOptionVmFloppy1Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_FLOPPY1.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameFloppy1() ); + } + + @Test + @DisplayName( "Test the parsing of the VM second floppy disk image command line option (long version)" ) + public void testCmdlnOptionVmFloppy1Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FLOPPY1.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameFloppy1() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first CDROM disk image command line option (short version)" ) + public void testCmdlnOptionVmCdrom0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_CDROM0.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameCdrom0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first CDROM disk image command line option (long version)" ) + public void testCmdlnOptionVmCdrom0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_CDROM0.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameCdrom0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM second CDROM disk image command line option (short version)" ) + public void testCmdlnOptionVmCdrom1Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_CDROM1.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameCdrom1() ); + } + + @Test + @DisplayName( "Test the parsing of the VM second CDROM disk image command line option (long version)" ) + public void testCmdlnOptionVmCdrom1Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_CDROM1.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmDiskFileNameCdrom1() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first parallel interface command line option (short version)" ) + public void testCmdlnOptionVmParallel0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_PARALLEL0.getShortOption(), + CMDLN_TEST_PARPORT + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_PARPORT, cmdLn.getVmDeviceParallel0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first parallel interface command line option (long version)" ) + public void testCmdlnOptionVmParallel0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_PARALLEL0.getLongOption(), + CMDLN_TEST_PARPORT + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_PARPORT, cmdLn.getVmDeviceParallel0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first serial interface command line option (short version)" ) + public void testCmdlnOptionVmSerial0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_SERIAL0.getShortOption(), + CMDLN_TEST_SERPORT + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_SERPORT, cmdLn.getVmDeviceSerial0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first serial interface command line option (long version)" ) + public void testCmdlnOptionVmSerial0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_SERIAL0.getLongOption(), + CMDLN_TEST_SERPORT + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_SERPORT, cmdLn.getVmDeviceSerial0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first network interface MAC command line option (short version)" ) + public void testCmdlnOptionVmMac0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_MAC0.getShortOption(), + CMDLN_TEST_MAC + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_MAC, cmdLn.getVmMacAddress0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first network interface MAC command line option (long version)" ) + public void testCmdlnOptionVmMac0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_MAC0.getLongOption(), + CMDLN_TEST_MAC + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_MAC, cmdLn.getVmMacAddress0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first file system source command line option (short version)" ) + public void testCmdlnOptionVmFsSrc0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_FSSRC0.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmFsSrc0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first file system source command line option (long version)" ) + public void testCmdlnOptionVmFsSrc0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FSSRC0.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmFsSrc0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first file system target command line option (short version)" ) + public void testCmdlnOptionVmFsTgt0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_FSTGT0.getShortOption(), + CMDLN_TEST_NAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NAME, cmdLn.getVmFsTgt0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM first file system target command line option (long version)" ) + public void testCmdlnOptionVmFsTgt0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FSTGT0.getLongOption(), + CMDLN_TEST_NAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NAME, cmdLn.getVmFsTgt0() ); + } + + @Test + @DisplayName( "Test the parsing of the VM second file system source command line option (short version)" ) + public void testCmdlnOptionVmFsSrc1Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_FSSRC1.getShortOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmFsSrc1() ); + } + + @Test + @DisplayName( "Test the parsing of the VM second file system source command line option (long version)" ) + public void testCmdlnOptionVmFsSrc1Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FSSRC1.getLongOption(), + CMDLN_TEST_FILENAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_FILENAME, cmdLn.getVmFsSrc1() ); + } + + @Test + @DisplayName( "Test the parsing of the VM second file system target command line option (short version)" ) + public void testCmdlnOptionVmFsTgt1Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_FSTGT1.getShortOption(), + CMDLN_TEST_NAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NAME, cmdLn.getVmFsTgt1() ); + } + + @Test + @DisplayName( "Test the parsing of the VM second file system target command line option (long version)" ) + public void testCmdlnOptionVmFsTgt1Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FSTGT1.getLongOption(), + CMDLN_TEST_NAME + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + assertEquals( CMDLN_TEST_NAME, cmdLn.getVmFsTgt1() ); + } + + @Test + @DisplayName( "Test the parsing of NVIDIA PCI IDs command line option for the first GPU passthrough (short version)" ) + public void testCmdlnOptionVmNvGpuIds0Short() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_SHORT + CmdLnOption.VM_NVGPUIDS0.getShortOption(), + CMDLN_TEST_NVGPU_DESC, CMDLN_TEST_NVGPU_ADDR + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + final List<String> nvidiaGpuIds = cmdLn.getVmNvGpuIds0(); + assertEquals( 2, nvidiaGpuIds.size() ); + assertEquals( CMDLN_TEST_NVGPU_DESC, nvidiaGpuIds.get( 0 ) ); + assertEquals( CMDLN_TEST_NVGPU_ADDR, nvidiaGpuIds.get( 1 ) ); + } + + @Test + @DisplayName( "Test the parsing of NVIDIA PCI IDs command line option for the first GPU passthrough (long version)" ) + public void testCmdlnOptionVmNvGpuIds0Long() throws CommandLineArgsException + { + final String[] args = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_NVGPUIDS0.getLongOption(), + CMDLN_TEST_NVGPU_DESC, CMDLN_TEST_NVGPU_ADDR + }; + + CommandLineArgs cmdLn = new CommandLineArgs( args ); + + final List<String> nvidiaGpuIds = cmdLn.getVmNvGpuIds0(); + assertEquals( 2, nvidiaGpuIds.size() ); + assertEquals( CMDLN_TEST_NVGPU_DESC, nvidiaGpuIds.get( 0 ) ); + assertEquals( CMDLN_TEST_NVGPU_ADDR, nvidiaGpuIds.get( 1 ) ); + } + + @Test + @DisplayName( "Test whether a NVIDIA GPU passthrough is enabled" ) + public void testIsNvidiaGpuPassthroughEnabled() throws CommandLineArgsException + { + final String[] args1 = { + CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_NVGPUIDS0.getLongOption(), + CMDLN_TEST_NVGPU_DESC, CMDLN_TEST_NVGPU_ADDR + }; + final String[] args2 = {}; + + CommandLineArgs cmdLn1 = new CommandLineArgs( args1 ); + CommandLineArgs cmdLn2 = new CommandLineArgs( args2 ); + + assertTrue( cmdLn1.isNvidiaGpuPassthroughEnabled() ); + assertFalse( cmdLn2.isNvidiaGpuPassthroughEnabled() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericCpuTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericCpuTest.java new file mode 100644 index 00000000..d88857a6 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericCpuTest.java @@ -0,0 +1,49 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.Domain.CpuCheck; +import org.openslx.libvirt.domain.Domain.CpuMode; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericCpuTest +{ + @Test + @DisplayName( "Test transformation of VM CPU configuration" ) + public void testTransformationGenericCpu() throws TransformationException + { + final TransformationGenericCpu transformation = new TransformationGenericCpu(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + assertNotEquals( Integer.parseInt( TransformationTestUtils.DEFAULT_VM_NCPUS ), config.getVCpu() ); + assertNotEquals( CpuMode.HOST_PASSTHROUGH, config.getCpuMode() ); + assertEquals( CpuCheck.PARTIAL, config.getCpuCheck() ); + + transformation.transform( config, args ); + + assertEquals( Integer.parseInt( TransformationTestUtils.DEFAULT_VM_NCPUS ), config.getVCpu() ); + assertEquals( CpuMode.HOST_PASSTHROUGH, config.getCpuMode() ); + assertEquals( CpuCheck.PARTIAL, config.getCpuCheck() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM CPU configuration with unspecified input data" ) + public void testTransformationGenericCpuNoData() throws TransformationException + { + final TransformationGenericCpu transformation = new TransformationGenericCpu(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + assertThrows( TransformationException.class, () -> transformation.transform( config, args ) ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskCdromDevicesTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskCdromDevicesTest.java new file mode 100644 index 00000000..73b37c36 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskCdromDevicesTest.java @@ -0,0 +1,65 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.util.ArrayList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Disk.StorageType; +import org.openslx.libvirt.domain.device.DiskCdrom; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericDiskCdromDevicesTest +{ + @Test + @DisplayName( "Test transformation of VM disk CDROM devices configuration with specified input data" ) + public void testTransformationGenericDiskCdromDevices() throws TransformationException + { + final TransformationGenericDiskCdromDevices transformation = new TransformationGenericDiskCdromDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + final ArrayList<DiskCdrom> devicesBeforeTransformation = config.getDiskCdromDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + final DiskCdrom cdromDeviceBeforeTransformation = devicesBeforeTransformation.get( 0 ); + assertEquals( StorageType.FILE, cdromDeviceBeforeTransformation.getStorageType() ); + assertNotEquals( TransformationTestUtils.DEFAULT_VM_CDROM0, cdromDeviceBeforeTransformation.getStorageSource() ); + + transformation.transform( config, args ); + + final ArrayList<DiskCdrom> devicesAfterTransformation = config.getDiskCdromDevices(); + assertEquals( 2, devicesAfterTransformation.size() ); + final DiskCdrom cdromDevice1AfterTransformation = devicesAfterTransformation.get( 0 ); + final DiskCdrom cdromDevice2AfterTransformation = devicesAfterTransformation.get( 1 ); + assertEquals( StorageType.FILE, cdromDevice1AfterTransformation.getStorageType() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_CDROM0, cdromDevice1AfterTransformation.getStorageSource() ); + assertEquals( StorageType.FILE, cdromDevice2AfterTransformation.getStorageType() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_CDROM1, cdromDevice2AfterTransformation.getStorageSource() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM disk CDROM devices configuration with unspecified input data" ) + public void testTransformationGenericDiskCdromDevicesNoData() throws TransformationException + { + final TransformationGenericDiskCdromDevices transformation = new TransformationGenericDiskCdromDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + final ArrayList<DiskCdrom> devicesBeforeTransformation = config.getDiskCdromDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<DiskCdrom> devicesAfterTransformation = config.getDiskCdromDevices(); + assertEquals( 0, devicesAfterTransformation.size() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskFloppyDevicesTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskFloppyDevicesTest.java new file mode 100644 index 00000000..3969e168 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskFloppyDevicesTest.java @@ -0,0 +1,66 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.util.ArrayList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Disk.StorageType; +import org.openslx.libvirt.domain.device.DiskFloppy; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericDiskFloppyDevicesTest +{ + @Test + @DisplayName( "Test transformation of VM disk floppy devices configuration with specified input data" ) + public void testTransformationGenericDiskFloppyDevices() throws TransformationException + { + final TransformationGenericDiskFloppyDevices transformation = new TransformationGenericDiskFloppyDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + final ArrayList<DiskFloppy> devicesBeforeTransformation = config.getDiskFloppyDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + final DiskFloppy floppyDeviceBeforeTransformation = devicesBeforeTransformation.get( 0 ); + assertEquals( StorageType.FILE, floppyDeviceBeforeTransformation.getStorageType() ); + assertNotEquals( TransformationTestUtils.DEFAULT_VM_FLOPPY0, + floppyDeviceBeforeTransformation.getStorageSource() ); + + transformation.transform( config, args ); + + final ArrayList<DiskFloppy> devicesAfterTransformation = config.getDiskFloppyDevices(); + assertEquals( 2, devicesAfterTransformation.size() ); + final DiskFloppy floppyDevice1AfterTransformation = devicesAfterTransformation.get( 0 ); + final DiskFloppy floppyDevice2AfterTransformation = devicesAfterTransformation.get( 1 ); + assertEquals( StorageType.FILE, floppyDevice1AfterTransformation.getStorageType() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_FLOPPY0, floppyDevice1AfterTransformation.getStorageSource() ); + assertEquals( StorageType.FILE, floppyDevice2AfterTransformation.getStorageType() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_FLOPPY1, floppyDevice2AfterTransformation.getStorageSource() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM disk floppy devices configuration with unspecified input data" ) + public void testTransformationGenericDiskFloppyDevicesNoData() throws TransformationException + { + final TransformationGenericDiskFloppyDevices transformation = new TransformationGenericDiskFloppyDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + final ArrayList<DiskFloppy> devicesBeforeTransformation = config.getDiskFloppyDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<DiskFloppy> devicesAfterTransformation = config.getDiskFloppyDevices(); + assertEquals( 0, devicesAfterTransformation.size() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskStorageDevicesTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskStorageDevicesTest.java new file mode 100644 index 00000000..8b52b90b --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericDiskStorageDevicesTest.java @@ -0,0 +1,62 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.util.ArrayList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Disk.StorageType; +import org.openslx.libvirt.domain.device.DiskStorage; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericDiskStorageDevicesTest +{ + @Test + @DisplayName( "Test transformation of VM disk storage devices configuration with specified input data" ) + public void testTransformationGenericDiskStorageDevices() throws TransformationException + { + final TransformationGenericDiskStorageDevices transformation = new TransformationGenericDiskStorageDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + final ArrayList<DiskStorage> devicesBeforeTransformation = config.getDiskStorageDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + final DiskStorage diskDeviceBeforeTransformation = devicesBeforeTransformation.get( 0 ); + assertNotEquals( StorageType.FILE, diskDeviceBeforeTransformation.getStorageType() ); + assertNotEquals( TransformationTestUtils.DEFAULT_VM_HDD0, diskDeviceBeforeTransformation.getStorageSource() ); + + transformation.transform( config, args ); + + final ArrayList<DiskStorage> devicesAfterTransformation = config.getDiskStorageDevices(); + assertEquals( 1, devicesAfterTransformation.size() ); + final DiskStorage diskDeviceAfterTransformation = devicesAfterTransformation.get( 0 ); + assertEquals( StorageType.FILE, diskDeviceAfterTransformation.getStorageType() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_HDD0, diskDeviceAfterTransformation.getStorageSource() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM disk storage devices configuration with unspecified input data" ) + public void testTransformationGenericDiskStorageDevicesNoData() throws TransformationException + { + final TransformationGenericDiskStorageDevices transformation = new TransformationGenericDiskStorageDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + final ArrayList<DiskStorage> devicesBeforeTransformation = config.getDiskStorageDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<DiskStorage> devicesAfterTransformation = config.getDiskStorageDevices(); + assertEquals( 0, devicesAfterTransformation.size() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericFileSystemDevicesTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericFileSystemDevicesTest.java new file mode 100644 index 00000000..7605f778 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericFileSystemDevicesTest.java @@ -0,0 +1,66 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.FileSystem; +import org.openslx.libvirt.domain.device.FileSystem.AccessMode; +import org.openslx.libvirt.domain.device.FileSystem.Type; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericFileSystemDevicesTest +{ + @Test + @DisplayName( "Test transformation of VM file system devices configuration with specified input data" ) + public void testTransformationGenericFileSystemDevices() throws TransformationException + { + final TransformationGenericFileSystemDevices transformation = new TransformationGenericFileSystemDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + final ArrayList<FileSystem> devicesBeforeTransformation = config.getFileSystemDevices(); + assertEquals( 0, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<FileSystem> devicesAfterTransformation = config.getFileSystemDevices(); + assertEquals( 2, devicesAfterTransformation.size() ); + final FileSystem fs1AfterTransformation = devicesAfterTransformation.get( 0 ); + final FileSystem fs2AfterTransformation = devicesAfterTransformation.get( 1 ); + assertEquals( Type.MOUNT, fs1AfterTransformation.getType() ); + assertEquals( AccessMode.MAPPED, fs1AfterTransformation.getAccessMode() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_FSSRC0, fs1AfterTransformation.getSource() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_FSTGT0, fs1AfterTransformation.getTarget() ); + assertEquals( Type.MOUNT, fs2AfterTransformation.getType() ); + assertEquals( AccessMode.MAPPED, fs2AfterTransformation.getAccessMode() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_FSSRC1, fs2AfterTransformation.getSource() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_FSTGT1, fs2AfterTransformation.getTarget() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM file system devices configuration with unspecified input data" ) + public void testTransformationGenericFileSystemDevicesNoData() throws TransformationException + { + final TransformationGenericFileSystemDevices transformation = new TransformationGenericFileSystemDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + final ArrayList<FileSystem> devicesBeforeTransformation = config.getFileSystemDevices(); + assertEquals( 0, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<FileSystem> devicesAfterTransformation = config.getFileSystemDevices(); + assertEquals( 0, devicesAfterTransformation.size() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericInterfaceDevicesTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericInterfaceDevicesTest.java new file mode 100644 index 00000000..e4632784 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericInterfaceDevicesTest.java @@ -0,0 +1,63 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Interface; +import org.openslx.libvirt.domain.device.Interface.Model; +import org.openslx.libvirt.domain.device.Interface.Type; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.VirtualizationConfigurationQemu; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericInterfaceDevicesTest +{ + @Test + @DisplayName( "Test transformation of VM network interface devices configuration with specified input data" ) + public void testTransformationGenericInterfaceDevices() throws TransformationException + { + final TransformationGenericInterfaceDevices transformation = new TransformationGenericInterfaceDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + final ArrayList<Interface> devicesBeforeTransformation = config.getInterfaceDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<Interface> devicesAfterTransformation = config.getInterfaceDevices(); + assertEquals( 1, devicesAfterTransformation.size() ); + final Interface interfaceAfterTransformation = devicesAfterTransformation.get( 0 ); + assertEquals( Type.BRIDGE, interfaceAfterTransformation.getType() ); + assertEquals( Model.VIRTIO, interfaceAfterTransformation.getModel() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_MAC0, interfaceAfterTransformation.getMacAddress() ); + assertEquals( VirtualizationConfigurationQemu.NETWORK_BRIDGE_NAT_DEFAULT, + interfaceAfterTransformation.getSource() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM network interface devices configuration with unspecified input data" ) + public void testTransformationGenericInterfaceDevicesNoData() throws TransformationException + { + final TransformationGenericInterfaceDevices transformation = new TransformationGenericInterfaceDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + final ArrayList<Interface> devicesBeforeTransformation = config.getInterfaceDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<Interface> devicesAfterTransformation = config.getInterfaceDevices(); + assertEquals( 0, devicesAfterTransformation.size() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericMemoryTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericMemoryTest.java new file mode 100644 index 00000000..a93e26a5 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericMemoryTest.java @@ -0,0 +1,50 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.math.BigInteger; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.DomainUtils; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericMemoryTest +{ + @Test + @DisplayName( "Test transformation of VM memory configuration" ) + public void testTransformationGenericMemory() throws TransformationException + { + final TransformationGenericMemory transformation = new TransformationGenericMemory(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + final BigInteger defaultMemory = DomainUtils.decodeMemory( TransformationTestUtils.DEFAULT_VM_MEM, "MiB" ); + + assertNotEquals( defaultMemory.toString(), config.getMemory().toString() ); + assertNotEquals( defaultMemory.toString(), config.getCurrentMemory().toString() ); + + transformation.transform( config, args ); + + assertEquals( defaultMemory.toString(), config.getMemory().toString() ); + assertEquals( defaultMemory.toString(), config.getCurrentMemory().toString() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM memory configuration with unspecified input data" ) + public void testTransformationGenericMemoryNoData() throws TransformationException + { + final TransformationGenericMemory transformation = new TransformationGenericMemory(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + assertThrows( TransformationException.class, () -> transformation.transform( config, args ) ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericNameTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericNameTest.java new file mode 100644 index 00000000..ca272801 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericNameTest.java @@ -0,0 +1,45 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericNameTest +{ + @Test + @DisplayName( "Test transformation of VM (display) name configuration" ) + public void testTransformationGenericName() throws TransformationException + { + final TransformationGenericName transformation = new TransformationGenericName(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + assertNotEquals( TransformationTestUtils.DEFAULT_VM_NAME, config.getName() ); + assertNotEquals( TransformationTestUtils.DEFAULT_VM_DSPLNAME, config.getTitle() ); + + transformation.transform( config, args ); + + assertEquals( TransformationTestUtils.DEFAULT_VM_NAME, config.getName() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_DSPLNAME, config.getTitle() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM (display) name configuration with unspecified input data" ) + public void testTransformationGenericNameNoData() throws TransformationException + { + final TransformationGenericName transformation = new TransformationGenericName(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + assertThrows( TransformationException.class, () -> transformation.transform( config, args ) ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericParallelDevicesTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericParallelDevicesTest.java new file mode 100644 index 00000000..9402b59a --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericParallelDevicesTest.java @@ -0,0 +1,58 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Parallel; +import org.openslx.libvirt.domain.device.Parallel.Type; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericParallelDevicesTest +{ + @Test + @DisplayName( "Test transformation of VM parallel devices configuration with specified input data" ) + public void testTransformationGenericParallelDevices() throws TransformationException + { + final TransformationGenericParallelDevices transformation = new TransformationGenericParallelDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + final ArrayList<Parallel> devicesBeforeTransformation = config.getParallelDevices(); + assertEquals( 0, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<Parallel> devicesAfterTransformation = config.getParallelDevices(); + assertEquals( 1, devicesAfterTransformation.size() ); + final Parallel parallelDeviceAfterTransformation = devicesAfterTransformation.get( 0 ); + assertEquals( Type.DEV, parallelDeviceAfterTransformation.getType() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_PARALLEL0, parallelDeviceAfterTransformation.getSource() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM parallel devices configuration with unspecified input data" ) + public void testTransformationGenericParallelDevicesNoData() throws TransformationException + { + final TransformationGenericParallelDevices transformation = new TransformationGenericParallelDevices(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + final ArrayList<Parallel> devicesBeforeTransformation = config.getParallelDevices(); + assertEquals( 0, devicesBeforeTransformation.size() ); + + transformation.transform( config, args ); + + final ArrayList<Parallel> devicesAfterTransformation = config.getParallelDevices(); + assertEquals( 0, devicesAfterTransformation.size() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericUuidTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericUuidTest.java new file mode 100644 index 00000000..03534d8e --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationGenericUuidTest.java @@ -0,0 +1,43 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +public class TransformationGenericUuidTest +{ + @Test + @DisplayName( "Test transformation of VM UUID configuration" ) + public void testTransformationGenericUuid() throws TransformationException + { + final TransformationGenericUuid transformation = new TransformationGenericUuid(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + assertNotEquals( TransformationTestUtils.DEFAULT_VM_UUID, config.getUuid() ); + + transformation.transform( config, args ); + + assertEquals( TransformationTestUtils.DEFAULT_VM_UUID, config.getUuid() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM UUID configuration with unspecified input data" ) + public void testTransformationGenericUuidNoData() throws TransformationException + { + final TransformationGenericUuid transformation = new TransformationGenericUuid(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + assertThrows( TransformationException.class, () -> transformation.transform( config, args ) ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuArchitectureTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuArchitectureTest.java new file mode 100644 index 00000000..86186247 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuArchitectureTest.java @@ -0,0 +1,105 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.InputStream; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.capabilities.Capabilities; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.Domain.OsType; +import org.openslx.libvirt.domain.Domain.Type; +import org.openslx.libvirt.xml.LibvirtXmlDocumentException; +import org.openslx.libvirt.xml.LibvirtXmlSerializationException; +import org.openslx.libvirt.xml.LibvirtXmlTestResources; +import org.openslx.libvirt.xml.LibvirtXmlValidationException; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +class TransformationSpecificQemuArchitectureStub extends TransformationSpecificQemuArchitecture +{ + final String capabilityFileName; + + public TransformationSpecificQemuArchitectureStub( String capabilityFileName ) + { + super( null ); + + this.capabilityFileName = capabilityFileName; + } + + @Override + protected Capabilities getCapabilities() throws TransformationException + { + final InputStream capabilityContent = LibvirtXmlTestResources.getLibvirtXmlStream( this.capabilityFileName ); + Capabilities capabilites = null; + + try { + capabilites = new Capabilities( capabilityContent ); + } catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) { + fail( "Could not create stub for getCapabilities(): " + e.getLocalizedMessage() ); + } + + return capabilites; + } +} + +public class TransformationSpecificQemuArchitectureTest +{ + @Test + @DisplayName( "Test transformation of VM architecture configuration if KVM required and available" ) + public void testTransformationSpecificQemuArchitectureKvm() throws TransformationException + { + final TransformationSpecificQemuArchitectureStub transformation; + transformation = new TransformationSpecificQemuArchitectureStub( "qemu-kvm_capabilities_default.xml" ); + final Domain config = TransformationTestUtils.getDefaultDomain(); + + assertEquals( Type.KVM, config.getType() ); + assertEquals( "x86_64", config.getOsArch() ); + assertEquals( "pc-q35-5.1", config.getOsMachine() ); + assertEquals( OsType.HVM, config.getOsType() ); + + transformation.transform( config, null ); + + assertEquals( Type.KVM, config.getType() ); + assertEquals( "x86_64", config.getOsArch() ); + assertEquals( "pc-q35-5.1", config.getOsMachine() ); + assertEquals( OsType.HVM, config.getOsType() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM architecture configuration if KVM required but not available" ) + public void testTransformationSpecificQemuArchitectureNoKvm() throws TransformationException + { + final TransformationSpecificQemuArchitectureStub transformation; + transformation = new TransformationSpecificQemuArchitectureStub( "qemu-kvm_capabilities_no-kvm.xml" ); + final Domain config = TransformationTestUtils.getDefaultDomain(); + + assertEquals( Type.KVM, config.getType() ); + assertEquals( "x86_64", config.getOsArch() ); + assertEquals( "pc-q35-5.1", config.getOsMachine() ); + assertEquals( OsType.HVM, config.getOsType() ); + + assertThrows( TransformationException.class, () -> transformation.transform( config, null ) ); + } + + @Test + @DisplayName( "Test transformation of VM architecture configuration if version is not supported (machine version too new)" ) + public void testTransformationSpecificQemuArchitectureMachineVersionDowngrade() throws TransformationException + { + final TransformationSpecificQemuArchitectureStub transformation; + transformation = new TransformationSpecificQemuArchitectureStub( "qemu-kvm_capabilities_old-version.xml" ); + final Domain config = TransformationTestUtils.getDefaultDomain(); + + assertEquals( Type.KVM, config.getType() ); + assertEquals( "x86_64", config.getOsArch() ); + assertEquals( "pc-q35-5.1", config.getOsMachine() ); + assertEquals( OsType.HVM, config.getOsType() ); + + assertThrows( TransformationException.class, () -> transformation.transform( config, null ) ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidiaTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidiaTest.java new file mode 100644 index 00000000..8f50b7df --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidiaTest.java @@ -0,0 +1,134 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.InputStream; +import java.math.BigInteger; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.capabilities.Capabilities; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.HostdevPci; +import org.openslx.libvirt.domain.device.HostdevPciDeviceAddress; +import org.openslx.libvirt.domain.device.Shmem; +import org.openslx.libvirt.domain.device.Video; +import org.openslx.libvirt.xml.LibvirtXmlDocumentException; +import org.openslx.libvirt.xml.LibvirtXmlSerializationException; +import org.openslx.libvirt.xml.LibvirtXmlTestResources; +import org.openslx.libvirt.xml.LibvirtXmlValidationException; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +class TransformationSpecificQemuGpuPassthroughNvidiaStub extends TransformationSpecificQemuGpuPassthroughNvidia +{ + final String capabilityFileName; + + public TransformationSpecificQemuGpuPassthroughNvidiaStub( String capabilityFileName ) + { + super( null ); + + this.capabilityFileName = capabilityFileName; + } + + @Override + protected Capabilities getCapabilities() throws TransformationException + { + final InputStream capabilityContent = LibvirtXmlTestResources.getLibvirtXmlStream( this.capabilityFileName ); + Capabilities capabilites = null; + + try { + capabilites = new Capabilities( capabilityContent ); + } catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) { + fail( "Could not create stub for getCapabilities(): " + e.getLocalizedMessage() ); + } + + return capabilites; + } +} + +public class TransformationSpecificQemuGpuPassthroughNvidiaTest +{ + @Test + @DisplayName( "Test transformation of VM GPU passthrough configuration if NVIDIA GPU passthrouh is required" ) + public void testTransformationSpecificQemuGpuPassthroughNvidia() throws TransformationException + { + final TransformationSpecificQemuGpuPassthroughNvidiaStub transformation; + transformation = new TransformationSpecificQemuGpuPassthroughNvidiaStub( "qemu-kvm_capabilities_default.xml" ); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + transformation.transform( config, args ); + + final List<HostdevPci> pciDevices = config.getHostdevPciDevices(); + assertNotNull( pciDevices ); + assertEquals( 1, pciDevices.size() ); + + final HostdevPci pciDevice = pciDevices.get( 0 ); + assertTrue( pciDevice.isManaged() ); + assertEquals( HostdevPciDeviceAddress.valueOf( TransformationTestUtils.DEFAULT_VM_GPU0_ADDR ), + pciDevice.getSource() ); + + final List<Shmem> shmemDevices = config.getShmemDevices(); + assertNotNull( shmemDevices ); + assertEquals( 1, shmemDevices.size() ); + + final Shmem shmemDevice = shmemDevices.get( 0 ); + assertEquals( "looking-glass", shmemDevice.getName() ); + assertEquals( Shmem.Model.IVSHMEM_PLAIN, shmemDevice.getModel() ); + assertEquals( BigInteger.valueOf( 67108864 ).toString(), shmemDevice.getSize().toString() ); + + assertEquals( TransformationSpecificQemuGpuPassthroughNvidia.HYPERV_VENDOR_ID, + config.getFeatureHypervVendorIdValue() ); + assertTrue( config.isFeatureHypervVendorIdStateOn() ); + assertTrue( config.isFeatureKvmHiddenStateOn() ); + + final List<Video> videoDevices = config.getVideoDevices(); + assertNotNull( videoDevices ); + for ( final Video videoDevice : videoDevices ) { + assertEquals( Video.Model.NONE, videoDevice.getModel() ); + } + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM GPU passthrough configuration if NVIDIA GPU passthrouh is not specified" ) + public void testTransformationSpecificQemuGpuPassthroughNvidiaNoGpu() throws TransformationException + { + final TransformationSpecificQemuGpuPassthroughNvidiaStub transformation; + transformation = new TransformationSpecificQemuGpuPassthroughNvidiaStub( "qemu-kvm_capabilities_default.xml" ); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + transformation.transform( config, args ); + + final List<HostdevPci> pciDevices = config.getHostdevPciDevices(); + assertNotNull( pciDevices ); + assertEquals( 0, pciDevices.size() ); + + final List<Shmem> shmemDevices = config.getShmemDevices(); + assertNotNull( shmemDevices ); + assertEquals( 0, shmemDevices.size() ); + + assertNotEquals( TransformationSpecificQemuGpuPassthroughNvidia.HYPERV_VENDOR_ID, + config.getFeatureHypervVendorIdValue() ); + assertFalse( config.isFeatureHypervVendorIdStateOn() ); + assertFalse( config.isFeatureKvmHiddenStateOn() ); + + final List<Video> videoDevices = config.getVideoDevices(); + assertNotNull( videoDevices ); + for ( final Video videoDevice : videoDevices ) { + assertNotEquals( Video.Model.NONE, videoDevice.getModel() ); + } + + assertDoesNotThrow( () -> config.validateXml() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuSerialDevicesTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuSerialDevicesTest.java new file mode 100644 index 00000000..6beada8d --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuSerialDevicesTest.java @@ -0,0 +1,74 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Serial; +import org.openslx.libvirt.domain.device.Serial.Type; +import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +class TransformationSpecificQemuSerialDevicesStub extends TransformationSpecificQemuSerialDevices +{ + public TransformationSpecificQemuSerialDevicesStub() + { + super( null ); + } +} + +public class TransformationSpecificQemuSerialDevicesTest +{ + @Test + @DisplayName( "Test transformation of VM serial devices configuration with specified input data" ) + public void testTransformationGenericSerialDevices() throws TransformationException + { + final TransformationSpecificQemuSerialDevicesStub transformation = new TransformationSpecificQemuSerialDevicesStub(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getDefaultCmdLnArgs(); + + final ArrayList<Serial> devicesBeforeTransformation = config.getSerialDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + final Serial serialDeviceBeforeTransformation = devicesBeforeTransformation.get( 0 ); + assertEquals( Type.PTY, serialDeviceBeforeTransformation.getType() ); + + transformation.transform( config, args ); + + final ArrayList<Serial> devicesAfterTransformation = config.getSerialDevices(); + assertEquals( 2, devicesAfterTransformation.size() ); + final Serial serialDevice1AfterTransformation = devicesAfterTransformation.get( 0 ); + assertEquals( Type.PTY, serialDevice1AfterTransformation.getType() ); + final Serial serialDevice2AfterTransformation = devicesAfterTransformation.get( 1 ); + assertEquals( Type.DEV, serialDevice2AfterTransformation.getType() ); + assertEquals( TransformationTestUtils.DEFAULT_VM_SERIAL0, serialDevice2AfterTransformation.getSource() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } + + @Test + @DisplayName( "Test transformation of VM serial devices configuration with unspecified input data" ) + public void testTransformationGenericSerialDevicesNoData() throws TransformationException + { + final TransformationSpecificQemuSerialDevicesStub transformation = new TransformationSpecificQemuSerialDevicesStub(); + final Domain config = TransformationTestUtils.getDefaultDomain(); + final CommandLineArgs args = TransformationTestUtils.getEmptyCmdLnArgs(); + + final ArrayList<Serial> devicesBeforeTransformation = config.getSerialDevices(); + assertEquals( 1, devicesBeforeTransformation.size() ); + final Serial serialDeviceBeforeTransformation = devicesBeforeTransformation.get( 0 ); + assertEquals( Type.PTY, serialDeviceBeforeTransformation.getType() ); + + transformation.transform( config, args ); + + final ArrayList<Serial> devicesAfterTransformation = config.getSerialDevices(); + assertEquals( 1, devicesAfterTransformation.size() ); + final Serial serialDeviceAfterTransformation = devicesBeforeTransformation.get( 0 ); + assertEquals( Type.PTY, serialDeviceAfterTransformation.getType() ); + + assertDoesNotThrow( () -> config.validateXml() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationTestResources.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationTestResources.java new file mode 100644 index 00000000..b04685f9 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationTestResources.java @@ -0,0 +1,17 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import java.io.File; +import java.net.URL; + +public class TransformationTestResources +{ + private static final String LIBVIRT_PREFIX_PATH = File.separator + "libvirt"; + private static final String LIBVIRT_PREFIX_PATH_XML = LIBVIRT_PREFIX_PATH + File.separator + "xml"; + + public static File getLibvirtXmlFile( String libvirtXmlFileName ) + { + String libvirtXmlPath = TransformationTestResources.LIBVIRT_PREFIX_PATH_XML + File.separator + libvirtXmlFileName; + URL libvirtXml = TransformationTestResources.class.getResource( libvirtXmlPath ); + return new File( libvirtXml.getFile() ); + } +} diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationTestUtils.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationTestUtils.java new file mode 100644 index 00000000..597fd8d6 --- /dev/null +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationTestUtils.java @@ -0,0 +1,119 @@ +package org.openslx.runvirt.plugin.qemu.configuration; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.xml.LibvirtXmlDocumentException; +import org.openslx.libvirt.xml.LibvirtXmlSerializationException; +import org.openslx.libvirt.xml.LibvirtXmlTestResources; +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.cmdln.CommandLineArgsTest; + +public class TransformationTestUtils +{ + // @formatter:off + public static final String DEFAULT_VM_NAME = "archlinux"; + public static final String DEFAULT_VM_UUID = "4ec504d5-5eac-482f-a344-dbf1dd4956c8"; + public static final String DEFAULT_VM_DSPLNAME = "Archlinux"; + public static final String DEFAULT_VM_OS = "Windows 10 (x64)"; + public static final String DEFAULT_VM_NCPUS = "16"; + public static final String DEFAULT_VM_MEM = "1024"; + public static final String DEFAULT_VM_HDD0 = "/mnt/vm/windows.qcow2"; + public static final String DEFAULT_VM_FLOPPY0 = "/mnt/vm/floppy0.qcow2"; + public static final String DEFAULT_VM_FLOPPY1 = "/mnt/vm/floppy1.qcow2"; + public static final String DEFAULT_VM_CDROM0 = "/dev/sr0"; + public static final String DEFAULT_VM_CDROM1 = "/mnt/vm/cdrom1.qcow2"; + public static final String DEFAULT_VM_PARALLEL0 = "/dev/parport0"; + public static final String DEFAULT_VM_SERIAL0 = "/dev/ttyS0"; + public static final String DEFAULT_VM_MAC0 = "ca:06:29:84:f0:6d"; + public static final String DEFAULT_VM_FSSRC0 = "/mnt/shared/folder0"; + public static final String DEFAULT_VM_FSTGT0 = "folder0"; + public static final String DEFAULT_VM_FSSRC1 = "/mnt/shared/folder1"; + public static final String DEFAULT_VM_FSTGT1 = "folder1"; + public static final String DEFAULT_VM_GPU0_DESC = "10de:1d01"; + public static final String DEFAULT_VM_GPU0_ADDR = "0000:00:02.0"; + public static final String DEFAULT_VM_NVGPUIDS0 = DEFAULT_VM_GPU0_DESC + "," + DEFAULT_VM_GPU0_ADDR; + // @formatter:on + + private static final String[] DEFAULT_CMDLN_ARGS = { + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_NAME.getLongOption(), + TransformationTestUtils.DEFAULT_VM_NAME, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_UUID.getLongOption(), + TransformationTestUtils.DEFAULT_VM_UUID, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_DSPLNAME.getLongOption(), + TransformationTestUtils.DEFAULT_VM_DSPLNAME, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_OS.getLongOption(), + TransformationTestUtils.DEFAULT_VM_OS, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_NCPUS.getLongOption(), + TransformationTestUtils.DEFAULT_VM_NCPUS, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_MEM.getLongOption(), + TransformationTestUtils.DEFAULT_VM_MEM, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_HDD0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_HDD0, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FLOPPY0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_FLOPPY0, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FLOPPY1.getLongOption(), + TransformationTestUtils.DEFAULT_VM_FLOPPY1, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_CDROM0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_CDROM0, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_CDROM1.getLongOption(), + TransformationTestUtils.DEFAULT_VM_CDROM1, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_PARALLEL0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_PARALLEL0, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_SERIAL0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_SERIAL0, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_MAC0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_MAC0, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FSSRC0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_FSSRC0, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FSTGT0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_FSTGT0, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FSSRC1.getLongOption(), + TransformationTestUtils.DEFAULT_VM_FSSRC1, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_FSTGT1.getLongOption(), + TransformationTestUtils.DEFAULT_VM_FSTGT1, + CommandLineArgsTest.CMDLN_PREFIX_OPTION_LONG + CmdLnOption.VM_NVGPUIDS0.getLongOption(), + TransformationTestUtils.DEFAULT_VM_NVGPUIDS0 + }; + + private static CommandLineArgs getCmdLnArgs( String[] args ) + { + final CommandLineArgs cmdLnArgs = new CommandLineArgs(); + + try { + cmdLnArgs.parseCmdLnArgs( args ); + } catch ( CommandLineArgsException e ) { + fail( e.getLocalizedMessage() ); + } + + return cmdLnArgs; + } + + public static CommandLineArgs getDefaultCmdLnArgs() + { + return TransformationTestUtils.getCmdLnArgs( TransformationTestUtils.DEFAULT_CMDLN_ARGS ); + } + + public static CommandLineArgs getEmptyCmdLnArgs() + { + return TransformationTestUtils.getCmdLnArgs( new String[] {} ); + } + + public static Domain getDefaultDomain() + { + Domain domain = null; + + try { + domain = new Domain( LibvirtXmlTestResources + .getLibvirtXmlStream( "qemu-kvm_default-ubuntu-20-04-vm_transform-non-persistent.xml" ) ); + } catch ( LibvirtXmlDocumentException | LibvirtXmlSerializationException | LibvirtXmlValidationException e ) { + fail( "Cannot prepare requested Libvirt domain XML file from the resources folder: " + + e.getLocalizedMessage() ); + } + + return domain; + } +} diff --git a/core/modules/qemukvm/data/addon-init b/core/modules/qemukvm/data/addon-init deleted file mode 100755 index cccc1e8a..00000000 --- a/core/modules/qemukvm/data/addon-init +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/ash - -systemctl daemon-reload -systemctl start qemukvm.service - -# this seems to be necessary due to the misbehaviour of udevd -# in view of the undetected ENV dev_type -systemctl restart systemd-udevd diff --git a/core/modules/qemukvm/data/etc/qemu/bridge.conf b/core/modules/qemukvm/data/etc/qemu/bridge.conf deleted file mode 100644 index b8478434..00000000 --- a/core/modules/qemukvm/data/etc/qemu/bridge.conf +++ /dev/null @@ -1,6 +0,0 @@ -# These needs to be compatible with the -# bridge's names generated by -# /opt/openslx/scripts/systemd-run_virt_env -allow br0 -allow nat1 -allow vsw2 diff --git a/core/modules/qemukvm/data/etc/systemd/system/graphical.target.wants/qemukvm.service b/core/modules/qemukvm/data/etc/systemd/system/graphical.target.wants/qemukvm.service deleted file mode 120000 index 89d8afbf..00000000 --- a/core/modules/qemukvm/data/etc/systemd/system/graphical.target.wants/qemukvm.service +++ /dev/null @@ -1 +0,0 @@ -../qemukvm.service
\ No newline at end of file diff --git a/core/modules/qemukvm/data/etc/systemd/system/qemukvm.service b/core/modules/qemukvm/data/etc/systemd/system/qemukvm.service deleted file mode 100644 index f1f3dec6..00000000 --- a/core/modules/qemukvm/data/etc/systemd/system/qemukvm.service +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=Sets up the virtual box environment -Requires=run-virt-env.service -After=run-virt-env.service - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/opt/openslx/scripts/systemd-qemukvm_env start diff --git a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/README b/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/README deleted file mode 100644 index 549d0037..00000000 --- a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/README +++ /dev/null @@ -1 +0,0 @@ -Not tested since refactoring! (January 2017) diff --git a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/determine_hardware_limitations.inc b/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/determine_hardware_limitations.inc deleted file mode 100644 index dd83b587..00000000 --- a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/determine_hardware_limitations.inc +++ /dev/null @@ -1,89 +0,0 @@ -################################################################## -# qemu/kvm include: determine virtual hardware based on guest OS # -################################################################## - -set_vm_hw_limits() { - # FIXME when we have better metadata for qemu - if isempty VM_OS_TYPE; then - writelog "VM_OS_TYPE is empty! This should have been parsed from the XML file" - EXIT_TYPE="user" EXIT_REASON="Konnte Gastbetriebsystems der virtuellen Maschine nicht ermitteln!" cleanexit 1 - fi - - # define global hardware-related variables - declare -g MAXMEM="9999999" - declare -g MAXCORES="4" - declare -g SOUND_DEV="es1370" - - # determine if we need a 32bit or 64bit machine - # FIXME depending on the guest OS for now - # (sadly does not support all the exotic OSs the - # the world has even seen like vmware plugin :<) - local bits=64 - case "${VM_OS_TYPE}" in - beos*) - bits=32 - SOUND_DEV="sb16" - ;; - win3*|win*3*|Win*3*) - bits=32 - SOUND_DEV="sb16" - MAXMEM=32 - ;; - win95*|Win*95) - bits=32 - MAXMEM=96 - ;; - win98) - bits=32 - MAXMEM=256 - ;; - winme*|windowsme*) - bits=32 - SOUND_DEV="ac97" - MAXMEM=384 - ;; - winxp*64|windowsxp*64) - SOUND_DEV="ac97" - ;; - windows7|windows8|windows9) - SOUND_DEV="ac97" - MAXMEM="8000" - MAXCORES="4" - ;; - windows7-64|windows8-64|windows9-64) - SOUND_DEV="ac97" - MAXMEM="32000" - MAXCORES="8" - ;; - *64) - MAXMEM="16000" - MAXCORES="4" - ;; - *) - bits=32 - MAXMEM="8000" - MAXCORES="1" - ;; - esac - declare -g VIRTCMD - if [ "$bits" = 32 ]; then - declare -rg VIRTCMD="qemu-system-i386" - VIRTCMDOPTS+=( "-machine" "accel=tcg" ) # TODO: 32bit VMs can't use kvm!? - else # 64 bit - declare -rg VIRTCMD="qemu-system-x86_64" - VIRTCMDOPTS+=( "-machine" "accel=kvm" ) - fi - - - # check for allocated cores - declare -g CPU_CORES="${HW_THREADS:-1}" - declare -rg HOST_CORE_COUNT="${CPU_CORES}" - [ "${CPU_CORES}" -gt "${MAXCORES}" ] && CPU_CORES="${MAXCORES}" - - # check if memory set by the generic run-virt is above the threshold - [ "${VM_MEM}" -gt "${MAXMEM}" ] && VM_MEM="${MAXMEM}" - return 0 -} - -## MAIN ## -call_post_source set_vm_hw_limits diff --git a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/log_config_summary.inc b/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/log_config_summary.inc deleted file mode 100644 index 6b4b279f..00000000 --- a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/log_config_summary.inc +++ /dev/null @@ -1,35 +0,0 @@ -################################################## -# Include: Print vm config summary into log file # -################################################## - -log_config_summary() { - writelog "Directories:" - writelog "\tTMPDIR:\t\t\t$TMPDIR" - writelog "Disk image:" - writelog "\tSource disk:\t\t$VM_DISKFILE_RO" - if [ -e "${VM_DISKFILE_RW}" ]; then - writelog "\tDisk mode:\t\twritable" - writelog "\tRW-Layer:\t\t$VM_DISKFILE_RW" - else - writelog "\tDisk mode:\t\tread-only" - fi - - writelog "Virtual Hardware:" - writelog "\tvCPU cores:\t\t${CPU_CORES}" - writelog "\tGuest RAM:\t\t${VM_MEM} MB" - - # echo nur wenn HOST_MEM_REMAINING gesetzt - if isset HOST_MEM_REMAINING; then - writelog "\tHost RAM:\t\t${HOST_MEM_REMAINING} MB" - fi - writelog "\tMAC address:\t\t${VM_MAC_ADDR}" - writelog "\tNetwork card:\t\t${NIC_MODEL}" - writelog "\tNetwork kind:\t\t${NETWORK_MODE}" - writelog "\tCD-ROM1:\t${CDROM_0}" - writelog "\tCD-ROM2:\t${CDROM_1}" - writelog "\tFloppy_A:\t${FLOPPY_0}" - writelog "\tFloppy_B:\t${SLX_FLOPPY_IMG}" - #writelog "\tShared Folders 'home':\t/home/${USER}" -} - -call_post_source log_config_summary diff --git a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/setup_network.inc b/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/setup_network.inc deleted file mode 100644 index 73fb2518..00000000 --- a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/setup_network.inc +++ /dev/null @@ -1,56 +0,0 @@ -################################### -# qemu/kvm include: Network setup # -################################### -# This now makes use of the qemu's bridge helper -# which creates a tap device and adds it to the -# bridge corresponding to the network type -# TODO configurable network type -setup_network() { - # list available models with: - # qemu-system-x86_64 -net nic,model=? - # e.g. as of 2.0.0: - # ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio - declare -rg NIC_MODEL="e1000" - - # add MAC address and network card model - VIRTCMDOPTS+=( "-device" "${NIC_MODEL},mac=${VM_MAC_ADDR},netdev=guestnet0" ) - - # TODO support different network kinds for lectures in bwlehrpool-suite, just NAT for now - declare -g NETWORK_MODE="nat" - - # detect if qemu's bridge helper binary is available - declare -g QEMU_BRIDGE_HELPER= - for HELPER_PATH in /usr/lib/qemu-bridge-helper /usr/local/libexec/qemu-bridge-helper; do - if [ -x "${HELPER_PATH}" ] && [ -u "${HELPER_PATH}" ]; then - QEMU_BRIDGE_HELPER="${HELPER_PATH}" - readonly QEMU_BRIDGE_HELPER - break - fi - done - if isempty QEMU_BRIDGE_HELPER; then - writelog "Could not find qemu-bridge-helper on this machine. Setting network mode to user." - # Even though falling back to creating tap devices ourselves, we should instead - # garantee the existance of qemu's helper on minilinux build time. - # qemu's user network mode allows tcp/udp connections in a nat-fashion and - # it allows access to the web which seems suffisant for a fallback. - NETWORK_MODE="user" - fi - - case "${NETWORK_MODE}" in - nat*) - VIRTCMDOPTS+=( "-netdev" "bridge,br=nat1,id=guestnet0,helper=${QEMU_BRIDGE_HELPER}" ) - ;; - bridge*) - VIRTCMDOPTS+=( "-netdev" "bridge,br=br0,id=guestnet0,helper=${QEMU_BRIDGE_HELPER}" ) - ;; - host*) - VIRTCMDOPTS+=( "-netdev" "bridge,br=vsw2,id=guestnet0,helper=${QEMU_BRIDGE_HELPER}" ) - ;; - user*|*) - VIRTCMDOPTS+=( "-netdev" "user,id=guestnet0" ) - ;; - esac -} - -## MAIN ## -call_post_source setup_network diff --git a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/setup_rw_layer.inc b/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/setup_rw_layer.inc deleted file mode 100644 index 32637e5d..00000000 --- a/core/modules/qemukvm/data/opt/openslx/vmchooser/plugins/qemukvm/includes/setup_rw_layer.inc +++ /dev/null @@ -1,25 +0,0 @@ - -setup_rw_layer() { - if isempty VM_DISKFILE_RO; then - writelog "No source diskfile found! This should be specified in the given XML file. Is it valid?" - EXIT_TYPE="user" EXIT_REASON="Keine virtuelle Festplatte zu dieser Veranstaltung angegeben!" cleanexit 1 - fi - - # setup qcow2 backing file for that disk file - writelog "Creating backing file for '${VM_DISKFILE_RO}'..." - # TODO: WTF? This is fucked up, VM_DISKFILE_RW is set if we want to run in persistent mode, - # this plugin shouldn't mess around with it.... - declare -rg VM_DISKFILE_RW="${TMPDIR}/$(basename ${VM_DISKFILE_RO}).qcow2" - if qemu-img create -f qcow2 -b "${VM_DISKFILE_RO}" "${VM_DISKFILE_RW}"; then - # all good, use it as main disk drive - # TODO: determine the proper type of controller to use, ideally virtio? - VIRTCMDOPTS+=("-drive" "if=virtio,format=qcow2,file=${VM_DISKFILE_RW}" ) - else - writelog "Error creating backing file for '${VM_DISKFILE_RO}'" - # TODO use -snapshot as fallback, test it! - # How is this supposed to even work without a disk? - VIRTCMDOPTS+=( "-snapshot" ) - fi -} - -call_post_source setup_rw_layer diff --git a/core/modules/qemukvm/module.build b/core/modules/qemukvm/module.build deleted file mode 100644 index f2a0df9e..00000000 --- a/core/modules/qemukvm/module.build +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -fetch_source() { - : -} - -build() { - COPYLIST="list_dpkg_output" - [ -e "$COPYLIST" ] && rm "$COPYLIST" - list_packet_files >> "$COPYLIST" - - - tarcopy "$(cat "${COPYLIST}" | sort -u)" "${MODULE_BUILD_DIR}" -} - -post_copy() { - # qemu-bridge-helper needs suid to be called within run-virt - find ${TARGET_BUILD_DIR} -type f -executable -name "*qemu-bridge-helper*" -exec chmod u+s {} \; -} diff --git a/core/modules/qemukvm/module.conf b/core/modules/qemukvm/module.conf deleted file mode 100644 index 0cf394cf..00000000 --- a/core/modules/qemukvm/module.conf +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -REQUIRED_MODULES="" -REQUIRED_DIRECTORIES=" - / -" diff --git a/core/modules/qemukvm/module.conf.debian b/core/modules/qemukvm/module.conf.debian deleted file mode 100644 index 4773a871..00000000 --- a/core/modules/qemukvm/module.conf.debian +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -REQUIRED_INSTALLED_PACKAGES=" - qemu - qemu-kvm -" - -REQUIRED_CONTENT_PACKAGES=" - qemu - qemu-kvm - qemu-system-common - qemu-system-x86 - qemu-user - qemu-utils - seabios -" diff --git a/core/modules/qemukvm/module.conf.ubuntu b/core/modules/qemukvm/module.conf.ubuntu deleted file mode 100644 index 4773a871..00000000 --- a/core/modules/qemukvm/module.conf.ubuntu +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -REQUIRED_INSTALLED_PACKAGES=" - qemu - qemu-kvm -" - -REQUIRED_CONTENT_PACKAGES=" - qemu - qemu-kvm - qemu-system-common - qemu-system-x86 - qemu-user - qemu-utils - seabios -" diff --git a/core/targets/qemu/libvirt b/core/targets/qemu/libvirt new file mode 120000 index 00000000..3e853b08 --- /dev/null +++ b/core/targets/qemu/libvirt @@ -0,0 +1 @@ +../../modules/libvirt
\ No newline at end of file diff --git a/core/targets/qemu/libvirt-users b/core/targets/qemu/libvirt-users new file mode 120000 index 00000000..6f799d72 --- /dev/null +++ b/core/targets/qemu/libvirt-users @@ -0,0 +1 @@ +../../modules/libvirt-users
\ No newline at end of file diff --git a/core/targets/qemu/openjdk-8-jre-headless b/core/targets/qemu/openjdk-8-jre-headless new file mode 120000 index 00000000..b899a6a4 --- /dev/null +++ b/core/targets/qemu/openjdk-8-jre-headless @@ -0,0 +1 @@ +../../modules/openjdk-8-jre-headless/
\ No newline at end of file diff --git a/core/targets/qemu/qemu b/core/targets/qemu/qemu new file mode 120000 index 00000000..bb8ffc08 --- /dev/null +++ b/core/targets/qemu/qemu @@ -0,0 +1 @@ +../../modules/qemu
\ No newline at end of file diff --git a/core/targets/qemukvm/qemukvm b/core/targets/qemukvm/qemukvm deleted file mode 120000 index e8e7d1b3..00000000 --- a/core/targets/qemukvm/qemukvm +++ /dev/null @@ -1 +0,0 @@ -../../modules/qemukvm
\ No newline at end of file |