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 { 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 option; private LinkedHashMap 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 osList, File config) { super(osList); this.config = new QemuConfig(config); init(); } public QemuMetaData(List 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 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")); } }