package org.openslx.util.vm;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.TreeMap;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.OperatingSystem;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.thrifthelper.TConst;
import static org.openslx.util.vm.QemuConfig.Header.*;
class QemuDDAccelMeta {
public final boolean isPresent;
public QemuDDAccelMeta(boolean present) {
isPresent = present;
}
}
class QemuHWVersionMeta {
public final int version;
public QemuHWVersionMeta(int vers) {
version = vers;
}
}
class QemuEthernetDevTypeMeta {
public final String value;
public QemuEthernetDevTypeMeta(String val) {
value = val;
}
}
class QemuSoundCardMeta {
public final boolean isPresent;
public final String value;
public QemuSoundCardMeta(boolean present, String val) {
isPresent = present;
value = val;
}
}
class QemuUSBMeta {
public final String value;
public final int speed;
public QemuUSBMeta(String value, int speed) {
this.value = value;
this.speed = speed;
}
}
public final class QemuMetaData extends VmMetaData<QemuSoundCardMeta, QemuDDAccelMeta, QemuHWVersionMeta, QemuEthernetDevTypeMeta, QemuUSBMeta> {
private static final Logger LOGGER = Logger.getLogger(QemuMetaData.class);
private static final Virtualizer virtualizer = new Virtualizer(TConst.VIRT_QEMU, "QEMU-KVM");
private final QemuConfig config;
private TreeMap<String, String> option;
private LinkedHashMap<String, String> header;
private int cdromCounter = 0;
private int floppyCounter = 0;
private int netdevCounter = 0;
public static enum EthernetType {
NAT("qnet1"), BRIDGED("qnet0"), HOST_ONLY("qnet2");
public final String vmnet;
private EthernetType(String vnet) {
this.vmnet = vnet;
}
}
public QemuMetaData(List<OperatingSystem> osList, File config) {
super(osList);
this.config = new QemuConfig(config);
init();
}
public QemuMetaData(List<OperatingSystem> osList, byte[] vmContent, int length) throws IOException, UnsupportedVirtualizerFormatException {
super(osList);
this.config = new QemuConfig(vmContent, length);
init();
}
public void init() {
registerVirtualHW();
config.setHdds();
displayName = config.getDisplayName();
setOs(config.getOsName());
isMachineSnapshot = config.isMachineSnapshot();
for (HardDisk hardDisk : config.getHdds()) {
hdds.add(hardDisk);
}
}
@Override
public byte[] getFilteredDefinitionArray() {
//TODO: Filter is done fix server side
return config.toString(false).getBytes(StandardCharsets.UTF_8);
}
@Override
public byte[] getDefinitionArray() {
return config.toString(true).getBytes(StandardCharsets.UTF_8);
}
@Override
public void applySettingsForLocalEdit() {
//nothing ! for VMware
}
@Override
public boolean addHddTemplate(File diskImage, String hddMode, String redoDir) {
return addHddTemplate(diskImage.getName(), hddMode, redoDir);
}
@Override
public boolean addHddTemplate(String diskImagePath, String hddMode, String redoDir) {
DriveBusType bus = null;
option = new TreeMap<>();
option = config.get(DRIVE.getHeader(), DRIVE.getID());
if (option != null) {
option.remove("file");
option.put("file", diskImagePath);
if (option.get("if") == null) {
} else {
switch (option.get("if")) {
case "ide":
bus = DriveBusType.IDE;
break;
case "scsi":
bus = DriveBusType.SCSI;
break;
case "sata":
//not available for Qemu. Others : sd, mtd, floppy, pflash, virtio
break;
default:
break;
}
}
} else {
LOGGER.error("Missing disk");
}
header = new LinkedHashMap<>();
header.put(DRIVE.getHeader(), DRIVE.getID());
config.set(header, option);
option = new TreeMap<>();
option = config.get(DEVICE.getHeader(), DEVICE.getID());
if (option == null) {
option = new TreeMap<>();
header = new LinkedHashMap<>();
header.put(DEVICE.getHeader(), null);
option.put("drive", DRIVE.getID());
if (bus != null) {
if (bus.equals(DriveBusType.IDE)) {
option.put("driver", "ide-hd");
} else if (bus.equals(DriveBusType.SCSI)) {
option.put("driver", "scsi-hd");
}
} else {
//Shouldn't come to this but to check TODO
}
config.set(header, option);
}
return true;
}
@Override
public boolean addDefaultNat() {
//No arguments needed, default on nat. But cannot receive
return true;
}
@Override
public void setOs(String vendorOsId) {
setOs(TConst.VIRT_QEMU, vendorOsId);
}
@Override
public boolean addDisplayName(String name) {
option = new TreeMap<>();
option.put("guest", name);
header = new LinkedHashMap<>();
header.put(NAME.getHeader(), null);
config.set(header, option);
return true;
}
@Override
public boolean addRam(int mem) {
option = new TreeMap<>();
option.put("size", "" + mem);
header = new LinkedHashMap<>();
header.put(MEMORY.getHeader(), null);
config.set(header, option);
return true;
}
@Override
public void addFloppy(int index, String image, boolean readOnly) {
option = new TreeMap<>();
if (readOnly) {
option.put("readonly", "on");
} else {
option.put("readonly", "off");
}
option.put("if", "floppy");
option.put("file", image);
header = new LinkedHashMap<>();
header.put(DRIVE.getHeader(), "floppy" + floppyCounter);
config.set(header, option);
floppyCounter++;
}
@Override
public boolean addCdrom(String image) {
option = new TreeMap<>();
option.put("media", "cdrom");
option.put("file", image);
header = new LinkedHashMap<>();
header.put(DRIVE.getHeader(), "cdrom" + cdromCounter);
config.set(header, option);
cdromCounter++;
return true;
}
@Override
public boolean addCpuCoreCount(int nrOfCores) {
option = new TreeMap<>();
option.put("cpus", nrOfCores + "");
header = new LinkedHashMap<>();
header.put(SMP.getHeader(), null);
config.set(header, option);
return true;
}
@Override
public void setSoundCard(VmMetaData.SoundCardType type) {
//TODO Not possible will be set as comment in config file
}
@Override
public VmMetaData.SoundCardType getSoundCard() {
//TODO set to default, just write comment
return VmMetaData.SoundCardType.DEFAULT;
}
@Override
public void setDDAcceleration(VmMetaData.DDAcceleration type) {
//TODO: Not really used by qemu.
}
@Override
public VmMetaData.DDAcceleration getDDAcceleration() {
return DDAcceleration.OFF;
}
@Override
public void setHWVersion(VmMetaData.HWVersion type) {
//nothing...
}
@Override
public VmMetaData.HWVersion getHWVersion() {
return VmMetaData.HWVersion.DEFAULT;
}
@Override
public void setEthernetDevType(int cardIndex, VmMetaData.EthernetDevType type) {
/*
//TODO
QemuEthernetDevTypeMeta dev = networkCards.get(type);
header = new LinkedHashMap<>();
header.put(DEVICE.getHeader(), "net" + netdevCounter);
option = new TreeMap<>();
option.put("driver", dev.value);
//option.put("netdev", "net" + cardIndex);
config.set(header, option);
netdevCounter++;
*/
}
@Override
public VmMetaData.EthernetDevType getEthernetDevType(int cardIndex) {
//TO DO
return EthernetDevType.AUTO;
}
@Override
public boolean addEthernet(VmMetaData.EtherType type) {
boolean ret = false;
int index = 0;
//TODO use existing interface if given or create a new one
for (;; ++index) {
DEVICE.setID("netType" + index);
TreeMap<String, String> dev = config.get(DEVICE.getHeader(), DEVICE.getID());
if (dev == null || dev.get("netdev") == null) {
break;
}
}
switch (type) {
case NAT:
ret = addEthernet(index, EthernetType.NAT);
break;
case BRIDGED:
ret = addEthernet(index, EthernetType.BRIDGED);
break;
case HOST_ONLY:
ret = addEthernet(index, EthernetType.HOST_ONLY);
break;
default:
// Should not come to this...
break;
}
return ret;
}
public boolean addEthernet(int index, EthernetType type) {
NETDEV.setID("net" + index);
header = new LinkedHashMap<>();
header.put(NETDEV.getHeader(), NETDEV.getID());
//netdev
option = new TreeMap<>();
//device tap,ifname=nat1
option.put("ifname", type.vmnet);
option.put("type", "tap");
config.set(header, option);
//device
header = new LinkedHashMap<>();
header.put(DEVICE.getHeader(), "netType" + netdevCounter);
option = new TreeMap<>();
option.put("driver", "e1000");
option.put("netdev", NETDEV.getID());
config.set(header, option);
netdevCounter++;
return true;
}
@Override
public Virtualizer getVirtualizer() {
return virtualizer;
}
// @Override
// public void enableUsb(boolean enabled) {
// option = new TreeMap<>();
// if (enabled) {
// option.put("usb", "on");
// } else {
// option.put("usb", "off");
// }
//
// header = new LinkedHashMap<>();
// header.put(MACHINE.getHeader(), MACHINE.getID());
// config.set(header, option);
// }
// @Override
// public boolean disableSuspend() {
// return false;
// }
@Override
public boolean tweakForNonPersistent() {
return false;
}
public void setMaxUsbSpeed( VmMetaData.UsbSpeed speed )
{
// TODO: Actual speed setting?
if ( speed == null || speed == VmMetaData.UsbSpeed.NONE ) {
option.remove( "usb" );
} else {
option.put( "usb", "" );
}
}
@Override
public VmMetaData.UsbSpeed getMaxUsbSpeed()
{
if (option.containsKey( "usb" ) )
return VmMetaData.UsbSpeed.USB2_0; // TODO
return VmMetaData.UsbSpeed.NONE;
}
@Override
public void registerVirtualHW() {
soundCards.put(VmMetaData.SoundCardType.NONE, new QemuSoundCardMeta(false, null));
soundCards.put(VmMetaData.SoundCardType.DEFAULT, new QemuSoundCardMeta(true, "ich6"));
soundCards.put(VmMetaData.SoundCardType.AC, new QemuSoundCardMeta(true, "ac97"));
soundCards.put(VmMetaData.SoundCardType.ES, new QemuSoundCardMeta(true, "es1370"));
soundCards.put(VmMetaData.SoundCardType.SOUND_BLASTER, new QemuSoundCardMeta(true, "sb16"));
ddacc.put(VmMetaData.DDAcceleration.OFF, new QemuDDAccelMeta(false));
ddacc.put(VmMetaData.DDAcceleration.ON, new QemuDDAccelMeta(true));
hwversion.put(VmMetaData.HWVersion.DEFAULT, new QemuHWVersionMeta(0));
usbSpeeds.put(VmMetaData.UsbSpeed.NONE, new QemuUSBMeta(null, 0));
usbSpeeds.put(VmMetaData.UsbSpeed.USB1_1, new QemuUSBMeta("usb", 1));
usbSpeeds.put(VmMetaData.UsbSpeed.USB2_0, new QemuUSBMeta("ehci", 2));
usbSpeeds.put(VmMetaData.UsbSpeed.USB3_0, new QemuUSBMeta("usb_xhci", 3));
//networkCards.put(VmMetaData.EthernetDevType.VIRTIO, new QemuEthernetDevTypeMeta("virtio-net-pci"));
networkCards.put(VmMetaData.EthernetDevType.E1000, new QemuEthernetDevTypeMeta("e1000"));
networkCards.put(VmMetaData.EthernetDevType.PCNET32, new QemuEthernetDevTypeMeta("pcnet"));
//networkCards.put(VmMetaData.EthernetDevType.RTL8139, new QemuEthernetDevTypeMeta("rtl8139"));
}
}