package org.openslx.util.vm; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; 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; } } public final class QemuMetaData extends VmMetaData { private final Map arguments = new HashMap<>(); //to remove at the end. easier // the above map's elements will take the place of in the config string private final QemuConfig config; private String setup; private final static String QUOTE = "\""; private TreeMap option; private LinkedHashMap header; private static final Logger LOGGER = Logger.getLogger(QemuMetaData.class); private static final Virtualizer virtualizer = new Virtualizer(TConst.VIRT_QEMU, "QEMU-KVM"); private int cdromCounter = 0; private int floppyCounter = 0; public static enum EthernetType { NAT("qnet1"), BRIDGED("qnet0"), HOST_ONLY("qnet2"); public final String vmnet; private EthernetType(String vnet) { this.vmnet = vnet; } public String getVMnet() { return this.vmnet; } } 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()); // Drive Filename && Bus if (option != null) { option.replace("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"); } //Setting settings header = new LinkedHashMap<>(); header.put(DRIVE.getHeader(), DRIVE.getID()); config.set(header, option); // Device 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) { //Not possible will be set as comment in config file } @Override public VmMetaData.SoundCardType getSoundCard() { //not possible to set just write comment return null; } @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) { QemuEthernetDevTypeMeta dev = networkCards.get(type); option = new TreeMap<>(); option.put("driver", "\"" + dev.value + "\""); option.put("netdev", "\"net" + cardIndex + "\""); // config.set("[device]", option); } @Override public VmMetaData.EthernetDevType getEthernetDevType(int cardIndex) { // QemuEthernetDevTypeMeta ethernetDevTypeMeta = null; // for (String key : config.get().keySet()) { // if (key.equals("[dev" + cardIndex + "]")) {//wont work dev0 // if (config.get(key).get("netdev").equals("\"net" + cardIndex + "\"")) { // String devs = config.get(key).get("driver"); // // for (EthernetDevType type : VmMetaData.EthernetDevType.values()) { // ethernetDevTypeMeta = networkCards.get(type); // if (ethernetDevTypeMeta == null) { // continue; // } // if (devs.equals(ethernetDevTypeMeta.value)) { // return type; // } // } // } // } // } return EthernetDevType.AUTO; } @Override public boolean addEthernet(VmMetaData.EtherType type) { boolean ret = false; int index = 0; for (;; ++index) { DEVICE.setID("dev"+index); TreeMap dev = config.get(DEVICE.getHeader(), DEVICE.getID()); if (dev == null) { break; } else { if (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<>(); option.put("ifname", type.vmnet); option.put("type", "tap"); config.set(header, option); //device header = new LinkedHashMap<>(); header.put(DEVICE.getHeader(), ""); option = new TreeMap<>(); option.put("driver", "e1000"); option.put("netdev", NETDEV.getID()); config.set(header, option); 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 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(DDAcceleration.OFF, new QemuDDAccelMeta(false)); ddacc.put(DDAcceleration.ON, new QemuDDAccelMeta(true)); hwversion.put(HWVersion.DEFAULT, new QemuHWVersionMeta(0)); networkCards.put(EthernetDevType.VIRTIO, new QemuEthernetDevTypeMeta("virtio-net-pci")); networkCards.put(EthernetDevType.E1000, new QemuEthernetDevTypeMeta("e1000")); networkCards.put(EthernetDevType.PCNET32, new QemuEthernetDevTypeMeta("pcnet")); networkCards.put(EthernetDevType.RTL8139, new QemuEthernetDevTypeMeta("rtl8139")); } }