diff options
Diffstat (limited to 'src/main/java/org/openslx')
4 files changed, 343 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/firmware/FirmwareException.java b/src/main/java/org/openslx/firmware/FirmwareException.java new file mode 100644 index 0000000..494d653 --- /dev/null +++ b/src/main/java/org/openslx/firmware/FirmwareException.java @@ -0,0 +1,25 @@ +package org.openslx.firmware; + +/** + * An exception of a firmware-related error. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class FirmwareException extends Exception +{ + /** + * Version for serialization. + */ + private static final long serialVersionUID = -5932122856822258867L; + + /** + * Creates a firmware exception including an error message. + * + * @param errorMsg message to describe the exception. + */ + public FirmwareException( String errorMsg ) + { + super( errorMsg ); + } +} diff --git a/src/main/java/org/openslx/firmware/QemuFirmware.java b/src/main/java/org/openslx/firmware/QemuFirmware.java new file mode 100644 index 0000000..7a81217 --- /dev/null +++ b/src/main/java/org/openslx/firmware/QemuFirmware.java @@ -0,0 +1,169 @@ +package org.openslx.firmware; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.Reader; +import java.util.ArrayList; + +import com.google.gson.Gson; +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.annotations.SerializedName; + +/** + * Data representation of QEMU firmware specification files (*.json). + * + * @author Manuel Bentele + * @version 1.0 + */ +public class QemuFirmware +{ + /** + * Default QEMU firmware specification directory under Linux-based systems. + */ + public final static String DEFAULT_SPEC_DIR = "/usr/share/qemu/firmware"; + + @SerializedName( "description" ) + private String description; + @SerializedName( "interface-types" ) + private ArrayList<String> interfaceTypes; + @SerializedName( "mapping" ) + private FirmwareMapping mapping; + @SerializedName( "targets" ) + private ArrayList<FirmwareTarget> targets; + @SerializedName( "features" ) + private ArrayList<String> features; + @SerializedName( "tags" ) + private ArrayList<String> tags; + + public String getDescription() + { + return description; + } + + public ArrayList<String> getInterfaceTypes() + { + return interfaceTypes; + } + + public FirmwareMapping getMapping() + { + return mapping; + } + + public ArrayList<FirmwareTarget> getTargets() + { + return targets; + } + + public ArrayList<String> getFeatures() + { + return features; + } + + public ArrayList<String> getTags() + { + return tags; + } + + /** + * Parse QEMU firmware specification from firmware specification Json file. + * + * @param fwSpecFile firmware specification Json file. + * @return QEMU firmware specification. + */ + public static QemuFirmware fromFwSpec( File fwSpecFile ) + { + final Gson gson = new Gson(); + QemuFirmware firmware = null; + + try { + final Reader jsonContent = new FileReader( fwSpecFile ); + firmware = gson.fromJson( jsonContent, QemuFirmware.class ); + } catch ( FileNotFoundException | NullPointerException | JsonSyntaxException | JsonIOException e ) { + firmware = null; + } + + return firmware; + } +} + +class FirmwareMapping +{ + @SerializedName( "device" ) + private String device; + @SerializedName( "executable" ) + private FirmwareMappingExecutable executable; + @SerializedName( "nvram-template" ) + private FirmwareMappingNvramTemplate nvramTemplate; + + public String getDevice() + { + return device; + } + + public FirmwareMappingExecutable getExecutable() + { + return executable; + } + + public FirmwareMappingNvramTemplate getNvramTemplate() + { + return nvramTemplate; + } +} + +class FirmwareMappingExecutable +{ + @SerializedName( "filename" ) + private String fileName; + @SerializedName( "format" ) + private String format; + + public String getFileName() + { + return fileName; + } + + public String getFormat() + { + return format; + } +} + +class FirmwareMappingNvramTemplate +{ + @SerializedName( "filename" ) + private String fileName; + @SerializedName( "format" ) + private String format; + + public String getFileName() + { + return fileName; + } + + public String getFormat() + { + return format; + } +} + +class FirmwareTarget +{ + @SerializedName( "architecture" ) + private String architecture; + @SerializedName( "machines" ) + private ArrayList<String> machines; + + public String getArchitecture() + { + return architecture; + } + + public ArrayList<String> getMachines() + { + return machines; + } +} diff --git a/src/main/java/org/openslx/firmware/QemuFirmwareUtil.java b/src/main/java/org/openslx/firmware/QemuFirmwareUtil.java new file mode 100644 index 0000000..987a0c0 --- /dev/null +++ b/src/main/java/org/openslx/firmware/QemuFirmwareUtil.java @@ -0,0 +1,121 @@ +package org.openslx.firmware; + +import java.io.File; +import java.io.FileFilter; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.function.Predicate; + +import org.openslx.util.LevenshteinDistance; +import org.openslx.virtualization.configuration.transformation.TransformationException; + +/** + * Utilities to process QEMU firmware specification files. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class QemuFirmwareUtil +{ + /** + * Lookup a virtual machine's target OS loader based on QEMU firmware specification files. + * + * @param fwSpecDir QEMU firmware specification directory of the target host. + * @param sourceOsLoader OS loader of the virtual machine. + * @param sourceOsArch OS architecture of the virtual machine. + * @param sourceOsMachine OS machine type of the virtual machine. + * @return Path to the new target OS loader file for the virtual machine. + * + * @throws TransformationException Failed to lookup target OS loader for virtual machine. + */ + public static String lookupTargetOsLoader( String fwSpecDir, String sourceOsLoader, String sourceOsArch, + String sourceOsMachine ) + throws FirmwareException + { + String lookupOsLoader = null; + + // parse and check firmware specification directory + final File fwSpecDirFile = new File( fwSpecDir ); + if ( !fwSpecDirFile.exists() || !fwSpecDirFile.isDirectory() ) { + throw new FirmwareException( "Path to QEMU firmware specifications directory is invalid!" ); + } + + // get all firmware specification files + final FileFilter fwSpecFilesFilter = file -> !file.isDirectory() && file.getName().endsWith( ".json" ); + final File[] fwSpecFiles = fwSpecDirFile.listFiles( fwSpecFilesFilter ); + + // get paths to firmware files from firmware specification files + if ( fwSpecFiles != null ) { + final ArrayList<QemuFirmware> uefiFirmwares = new ArrayList<QemuFirmware>(); + + for ( final File fwSpecFile : fwSpecFiles ) { + // parse the firmware file + final QemuFirmware firmware = QemuFirmware.fromFwSpec( fwSpecFile ); + if ( firmware == null ) { + throw new FirmwareException( "Firmware '" + fwSpecFile.toString() + "' can not be parsed correctly!" ); + } else { + final Predicate<String> byInterfaceType = s -> s.toLowerCase().equals( "uefi" ); + if ( firmware.getInterfaceTypes().stream().filter( byInterfaceType ).findAny().isPresent() ) { + // found valid UEFI firmware + // check if architecture and machine type of the VM is supported by the firmware + final Predicate<FirmwareTarget> byArchitecture = t -> sourceOsArch.equals( t.getArchitecture() ); + final Predicate<String> byMachineType = s -> sourceOsMachine.startsWith( s.replace( "*", "" ) ); + final Predicate<FirmwareTarget> byMachines = t -> t.getMachines().stream().filter( byMachineType ) + .findAny().isPresent(); + + if ( firmware.getTargets().stream().filter( byArchitecture ).filter( byMachines ).findAny() + .isPresent() ) { + // found UEFI firmware supporting suitable architecture and machine type from VM + uefiFirmwares.add( firmware ); + } + } + } + } + + if ( uefiFirmwares.isEmpty() ) { + throw new FirmwareException( "There aren't any suitable UEFI firmwares locally available!" ); + } else { + final LevenshteinDistance distance = new LevenshteinDistance( 1, 1, 1 ); + int minFileNameDistance = Integer.MAX_VALUE; + Path suitablestUefiFirmwarePath = null; + + for ( final QemuFirmware uefiFirmware : uefiFirmwares ) { + final Path uefiFirmwarePath = Paths.get( uefiFirmware.getMapping().getExecutable().getFileName() ); + final Path sourceOsLoaderPath = Paths.get( sourceOsLoader ); + final String uefiFirmwareFileName = uefiFirmwarePath.getFileName().toString().toLowerCase(); + final String sourceOsLoaderFileName = sourceOsLoaderPath.getFileName().toString().toLowerCase(); + + final int fileNameDistance = distance.calculateDistance( uefiFirmwareFileName, sourceOsLoaderFileName ); + if ( fileNameDistance < minFileNameDistance ) { + minFileNameDistance = fileNameDistance; + suitablestUefiFirmwarePath = uefiFirmwarePath; + } + } + + lookupOsLoader = suitablestUefiFirmwarePath.toString(); + } + } + + return lookupOsLoader; + } + + /** + * Lookup a virtual machine's target OS loader based on QEMU firmware specification files under + * the default path. + * + * @param sourceOsLoader OS loader of the virtual machine. + * @param sourceOsArch OS architecture of the virtual machine. + * @param sourceOsMachine OS machine type of the virtual machine. + * @return Path to the new target OS loader file for the virtual machine. + * + * @throws TransformationException Failed to lookup target OS loader for virtual machine. + */ + public static String lookupTargetOsLoaderDefaultFwSpecDir( String sourceOsLoader, String sourceOsArch, + String sourceOsMachine ) + throws FirmwareException + { + return QemuFirmwareUtil.lookupTargetOsLoader( QemuFirmware.DEFAULT_SPEC_DIR, sourceOsLoader, sourceOsArch, + sourceOsMachine ); + } +} diff --git a/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java index 706a083..ced9160 100644 --- a/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java +++ b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemu.java @@ -7,6 +7,8 @@ import java.util.ArrayList; import java.util.List; import org.openslx.bwlp.thrift.iface.OperatingSystem; +import org.openslx.firmware.FirmwareException; +import org.openslx.firmware.QemuFirmwareUtil; import org.openslx.libvirt.domain.Domain; import org.openslx.libvirt.domain.DomainUtils; import org.openslx.libvirt.domain.device.ControllerUsb; @@ -208,6 +210,32 @@ public class VirtualizationConfigurationQemu extends VirtualizationConfiguration { } + public void transformOsLoader() throws VirtualizationConfigurationException + { + final String sourceOsLoader = this.vmConfig.getOsLoader(); + final String sourceOsArch = this.vmConfig.getOsArch(); + final String sourceOsMachine = this.vmConfig.getOsMachine(); + + // transform OS loader for local editing + // check if OS loader is specified + if ( sourceOsLoader != null && !sourceOsLoader.isEmpty() ) { + // OS loader is specified so transform path to specified firmware path + // First, lookup QEMU firmware loader for target + String targetOsLoader = null; + try { + targetOsLoader = QemuFirmwareUtil.lookupTargetOsLoaderDefaultFwSpecDir( sourceOsLoader, sourceOsArch, + sourceOsMachine ); + } catch ( FirmwareException e ) { + throw new VirtualizationConfigurationException( e.getLocalizedMessage() ); + } + + // Second, set target QEMU firmware loader if specified + if ( targetOsLoader != null && !targetOsLoader.isEmpty() ) { + this.vmConfig.setOsLoader( targetOsLoader ); + } + } + } + @Override public boolean addEmptyHddTemplate() { |