package org.openslx.util.vm; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; import org.apache.log4j.Logger; import static org.openslx.util.vm.QemuConfig.Header.*; import org.openslx.util.vm.VmMetaData.DriveBusType; public final class QemuConfig { private static final Logger LOGGER = Logger.getLogger(QemuConfig.class); private Map, TreeMap> entries = new LinkedHashMap<>(); private String osName = new String(); private final static String QUOTE = "\""; private ArrayList hddsArray = new ArrayList<>(); public static enum Header { BOOT("boot-opts", null), DEVICE("device", null), DRIVE("drive", ""), MACHINE("machine", null), MEMORY("memory", null), NAME("name", null), NETDEV("netdev", ""), SMP("smp-opts", null); private final String header; private String id; private Header(String header, String id) { this.header = header; this.id = id; } public String getHeader() { return this.header; } public String getID() { return id; } public void setID(String id) { this.id = id; } @Override public String toString() { String result; if (id == null) { result = "[" + header + "]"; } else { result = "[" + header + " " + QUOTE + id + QUOTE + "]"; } return result; } } public QemuConfig(byte[] vmContent, int length) { ArrayList result; BufferedReader stream = null; try { stream = new BufferedReader( new InputStreamReader( new ByteArrayInputStream(vmContent, 0, length), StandardCharsets.ISO_8859_1)); result = new ArrayList<>(); String line; while ((line = stream.readLine()) != null) { if (line.startsWith("#") == false) { if (line.isEmpty() == false) { result.add(line.trim()); } } } stream.close(); stream = null; } catch (IOException e) { result = null; } finally { if (stream != null) { try { stream.close(); stream = null; } catch (IOException ioe2) { } } } init(result); } public QemuConfig(File configFile) { ArrayList result; BufferedReader stream = null; try { stream = new BufferedReader( new InputStreamReader( new FileInputStream(configFile), StandardCharsets.ISO_8859_1)); result = new ArrayList<>(); String line; while ((line = stream.readLine()) != null) { if (line.startsWith("#") == false) { if (line.isEmpty() == false) { result.add(line.trim()); } } } stream.close(); stream = null; } catch (IOException e) { result = null; } finally { if (stream != null) { try { stream.close(); stream = null; } catch (IOException ioe2) { } } } init(result); } public void init(ArrayList lines) { int index = -1; int nbDev = 0; int nbDrive = 0; int nbNetDev = 0; ArrayList listID = new ArrayList<>(); LinkedHashMap headers = null; TreeMap options = null; boolean exist = false; boolean save = true; if (lines != null) { for (String option : lines) { if (option.startsWith("[") && option.endsWith("]")) { option = option.replaceAll("\\[|\\]", ""); headers = new LinkedHashMap<>(); if (option.contains(DRIVE.getHeader()) && option.contains(QUOTE)) { //Check drive with id String[] drive = option.split(QUOTE); for (String id : listID) { if (drive[1].equals(id)) { DRIVE.setID("id-disk-" + nbDrive); listID.add(DRIVE.getID()); exist = true; } } if (!exist) { DRIVE.setID(drive[1]); listID.add(DRIVE.getID()); } headers.put(DRIVE.getHeader(), DRIVE.getID()); nbDrive++; exist = false; } else if (option.equals(DRIVE.getHeader())) {//Check drive without id DRIVE.setID("id-disk-" + nbDrive); listID.add(DRIVE.getID()); headers.put(option, DRIVE.getID()); nbDrive++; } else if (option.contains(NETDEV.getHeader()) && option.contains(QUOTE)) {//Check netdev with id String[] netdev = option.split(QUOTE); for (String id : listID) { if (netdev[1].equals(id)) { NETDEV.setID("id-netdev-" + nbNetDev); listID.add(NETDEV.getID()); exist = true; } } if (!exist) { NETDEV.setID(netdev[1]); listID.add(NETDEV.getID()); } headers.put(NETDEV.getHeader(), NETDEV.getID()); nbNetDev++; exist = false; } else if (option.equals(NETDEV.toString())) {//Check netdev without id NETDEV.setID("id-netdev-" + nbNetDev); listID.add(NETDEV.getID()); headers.put(option, NETDEV.getID()); } else if (option.equals(DEVICE.getHeader())) {//This will always come as [device] DEVICE.setID("dev" + nbDev); headers.put(option, DEVICE.getID()); nbDev++; } else { if (option.equals(MEMORY.getHeader()) || option.equals(SMP.getHeader()) || option.equals(MACHINE.getHeader())) { save = false; continue; } else { headers.put(option, null); } } options = new TreeMap<>(); index++; } else if (index == -1) { //In case file doesn't begin with a header LOGGER.error("This config file is invalid. Chech syntax. Must begin with [..]"); } else { if (save) { String[] parameter = option.split("="); options.put(parameter[0].trim(), parameter[1].trim().replace(QUOTE, "")); entries.put(headers, options); }else{ save = true; } } } } } public TreeMap get(String key, String id) { TreeMap value = null; LinkedHashMap keys = new LinkedHashMap(); keys.put(key, id); if (entries.containsKey(keys)) { value = entries.get(keys); } return value; } public void set(LinkedHashMap key, TreeMap value) { entries.put(key, value); } public TreeMap, TreeMap> get() { return (TreeMap, TreeMap>) entries; } public String getDisplayName() { String result = ""; LinkedHashMap key = new LinkedHashMap(); key.put(NAME.getHeader(), null); if (entries.containsKey(key)) { result = entries.get(key).get("guest"); } return result; } public boolean isMachineSnapshot() { boolean isSnapshot = false; LinkedHashMap key = new LinkedHashMap(); key.put(DRIVE.getHeader(), DRIVE.getID()); String active = entries.get(key).get("snapshot"); if (active != null && active.equals("on")) { isSnapshot = true; } return isSnapshot; } public void setOsName() { //It is not defined in config file. Using dummy name. Will be set in combo box later osName = "QemuOS"; } public String getOsName() { return osName; } public void setHdds() { int dev = 0; String filename = null; DriveBusType bus = null; String controllerDevice = null; LinkedHashMap keys; TreeMap options; for (Map.Entry, TreeMap> entry : entries.entrySet()) { String busType; if (entry.getKey().containsKey(DRIVE.getHeader())) { if (entry.getValue().containsKey("index")) { if (entry.getValue().get("index").equals("0")) { DRIVE.setID(entry.getKey().get(DRIVE.getHeader())); filename = entry.getValue().get("file"); if (filename == null) { LOGGER.error("Please make sure your harddrive has a path"); } busType = entry.getValue().get("if"); if (busType != null) { switch (busType) { case "ide": bus = DriveBusType.IDE; break; case "scsi": bus = DriveBusType.SCSI; break; // case "virtio": // bus = DriveBusType.VIRTIO; case "sata": //not available for Qemu. Others : sd, mtd, floppy, pflash break; default: bus = DriveBusType.SCSI; break; } } else { bus = DriveBusType.SCSI; busType = "scsi"; keys = new LinkedHashMap<>(); keys.put(DRIVE.getHeader(), DRIVE.getID()); options = entries.get(entry.getKey()); options.put("if", busType); entries.put(keys, options); } } } } if (entry.getKey().containsKey(DEVICE.getHeader())) { String drive = entry.getValue().get("drive"); if (drive != null && drive.equals(DRIVE.getID())) { controllerDevice = entry.getValue().get("driver"); dev++; } } else { for (LinkedHashMap key : entries.keySet()) { if (key.containsKey(DEVICE.getHeader())) { dev++; } } } if (dev == 0) { if (bus != null) { switch (bus) { case IDE: controllerDevice = "ide-hd"; break; case SCSI: controllerDevice = "scsi-generic"; break; default: controllerDevice = "scsi-generic"; break; } } } if ((bus != null) && (filename != null) && (controllerDevice != null)) { hddsArray.add(new VmMetaData.HardDisk(controllerDevice, bus, filename));; if (dev == 0) { keys = new LinkedHashMap<>(); options = new TreeMap<>(); DEVICE.setID(DRIVE.getID()); keys.put(DEVICE.getHeader(), DEVICE.getID()); options.put("drive", DRIVE.getID()); options.put("driver", controllerDevice); entries.put(keys, options); } break; } } } public ArrayList getHdds() { return hddsArray; } public String toString(boolean filtered) { StringBuilder sb = new StringBuilder(300); LinkedHashMap, TreeMap> sortedArray = null; if (filtered) { sortedArray = new LinkedHashMap<>(); for (Map.Entry, TreeMap> entry : entries.entrySet()) { if (entry.getKey().containsKey(DEVICE.getHeader())) { DEVICE.setID(null); entry.getKey().replace(DEVICE.getHeader(), null); sortedArray.put(entry.getKey(), entry.getValue()); } if (entry.getKey().containsKey(NAME.getHeader()) || entry.getKey().containsKey(DRIVE.getHeader())) { sortedArray.put(entry.getKey(), entry.getValue()); } } } for (Map.Entry, TreeMap> entry : sortedArray == null ? entries.entrySet() : sortedArray.entrySet()) { String header = entry.getKey().keySet().toString(); if (entry.getKey().containsKey(DRIVE.getHeader())) { if ((entry.getValue().get("index") != null)) { if ((entry.getValue().get("index").equals("0"))) { header = DRIVE.toString(); } else { continue; } } else if (entry.getValue().get("media") != null) { DRIVE.setID(entry.getKey().get(DRIVE.getHeader())); header = DRIVE.toString(); } else if (entry.getValue().get("if") != null) { if (entry.getValue().get("if").equals("floppy")) { DRIVE.setID(entry.getKey().get(DRIVE.getHeader())); header = DRIVE.toString(); } } else { continue; } } if (entry.getKey().containsKey(DEVICE.getHeader())) { DEVICE.setID(null); entry.getKey().remove(DEVICE.getHeader()); entry.getKey().put(DEVICE.getHeader(), null); header = DEVICE.toString(); } if (entry.getKey().containsKey(NETDEV.getHeader())) { header = NETDEV.toString(); } sb.append(header + "\n"); TreeMap values = entry.getValue(); for (String key : values.keySet()) { String value = values.get(key); if (value == null) { sb.append(" " + key + " = " + value + "\n"); } else { if (key.equals("driver")) { if (value.isEmpty()) { continue; } } sb.append(" " + key + " = " + QUOTE + value + QUOTE + "\n"); } } } return sb.toString(); } }