package org.openslx.dozmod.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.nio.charset.StandardCharsets;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.OperatingSystem;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.helper.MessageType;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
import org.openslx.thrifthelper.TConst;
import org.openslx.util.vm.DiskImage;
import org.openslx.util.vm.QemuMetaData;
import org.openslx.util.vm.VboxMetaData;
import org.openslx.util.vm.VmMetaData;
import org.openslx.util.vm.VmwareMetaData;
public class VmWrapper {
private static final Logger LOGGER = Logger.getLogger(VmWrapper.class);
public static void wrapVm(File diskFile, String imageName, byte[] machineDescription,
String virtualizerId, int osId, DiskImage diskImageInfo) throws MetaDataMissingException,
IOException {
if (!diskFile.exists())
throw new FileNotFoundException("Disk file " + diskFile.getAbsolutePath() + " does not exist");
if (machineDescription == null || machineDescription.length < 10) {
if (!TConst.VIRT_VMWARE.equals(virtualizerId)) {
throw new MetaDataMissingException();
}
machineDescription = getFallbackVmx(diskImageInfo);
}
// Handle machine description to generate configuration file
VmMetaData<?, ?, ?, ?> vmMeta = VmMetaData.getInstance(MetaDataCache.getOperatingSystems(),
machineDescription, machineDescription.length);
// Append disk & NAT
if (!vmMeta.addDefaultNat() || !vmMeta.addHddTemplate(diskFile, null, null)) {
throw new MetaDataMissingException();
}
// The guestOS should be in the vmx, but the user could have changed it
// by editing
// the image via the GUI. Those changes are not written back to the
// stored vmx
int osMaxMemMb = 0;
if (virtualizerId != null && osId != 0) {
OperatingSystem os = MetaDataCache.getOsById(osId);
if (os != null && os.virtualizerOsId != null) {
String virtOsId = os.virtualizerOsId.get(virtualizerId);
if (virtOsId != null) {
vmMeta.setOs(virtOsId);
}
if (os.maxMemMb > 0) {
osMaxMemMb = os.maxMemMb;
}
}
}
vmMeta.addDisplayName(imageName);
int mem = getMainMemoryMb() / 2 - 512;
if (mem < 1024) {
mem = 1024;
}
if (osMaxMemMb > 0 && mem > osMaxMemMb) {
mem = osMaxMemMb;
}
vmMeta.addCpuCoreCount(1);
vmMeta.addRam((mem / 4) * 4);
vmMeta.addFloppy(0, null, true);
vmMeta.addFloppy(1, null, true);
vmMeta.addCdrom(""); // ISO-Based with no disk in drive
vmMeta.addCdrom(null); // Autodetect real drive
vmMeta.enableUsb(true);
vmMeta.applySettingsForLocalEdit();
// Output vm
String base = diskFile.getAbsolutePath();
int dot = base.lastIndexOf('.');
if (dot > 0) {
base = base.substring(0, dot);
}
String fileType = null;
if (vmMeta instanceof VmwareMetaData) {
fileType = ".vmx";
// Copy BIOS with both floppies enabled
try (FileOutputStream output = new FileOutputStream(new File(diskFile.getAbsoluteFile()
.getParentFile(), "nvram")); InputStream input = ResourceLoader.getStream("/data/nvram")) {
IOUtils.copy(input, output);
} catch (Exception e) {
Gui.asyncMessageBox("Konnte das BIOS für die VM nicht kopieren", MessageType.ERROR, LOGGER, e);
}
} else if (vmMeta instanceof VboxMetaData) {
fileType = ".vbox";
} else if (vmMeta instanceof QemuMetaData) {
fileType = "_startCommand.txt";
}
if (fileType == null) {
LOGGER.warn("file type somehow unclear; application could stop working here!");
}
File vmFile = new File(base + fileType);
vmFile.delete();
// actually write vmx to disk
FileUtils.writeByteArrayToFile(vmFile, vmMeta.getFilteredDefinitionArray());
}
private static byte[] getFallbackVmx(DiskImage diskImageInfo) throws IOException {
String vmx = ResourceLoader.getTextFile("/txt/vmx_template");
return vmx.replace("%VM_HW_VERSION%", Integer.toString(diskImageInfo.hwVersion)).getBytes(
StandardCharsets.UTF_8);
}
/**
* Generates a filename based on the given imageName and with the proper
* extension. The given imageName is sanitized and shortened.
*
* @param imageName desired basename of image file
* @param extension desired extension
* @return the generated name
*/
public static String generateFilename(String imageName, String extension) {
String fileName = imageName.replaceAll("[^a-zA-Z0-9_\\.\\-]+", "_");
if (fileName.length() > 50) {
fileName = fileName.substring(0, 50);
}
if (extension == null || extension.isEmpty()) {
fileName += ".img";
} else {
fileName += "." + extension;
}
return fileName;
}
public static class MetaDataMissingException extends Exception {
private static final long serialVersionUID = -7842986428831219758L;
}
private static int getMainMemoryMb() {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
Object attribute;
try {
attribute = mBeanServer.getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"),
"TotalPhysicalMemorySize");
} catch (AttributeNotFoundException | InstanceNotFoundException | MalformedObjectNameException
| MBeanException | ReflectionException e) {
return -1;
}
try {
return (int) (Long.parseLong(attribute.toString()) / (1024 * 1024));
} catch (Exception e) {
return -1;
}
}
}