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<LinkedHashMap<String, String>, TreeMap<String, String>> entries = new LinkedHashMap<>();
private String osName = new String();
private final static String QUOTE = "\"";
private ArrayList<VmMetaData.HardDisk> 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<String> 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<String> 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<String> lines) {
int index = -1;
int nbDev = 0;
int nbDrive = 0;
int nbNetDev = 0;
ArrayList<String> listID = new ArrayList<>();
LinkedHashMap<String, String> headers = null;
TreeMap<String, String> 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<String, String> get(String key, String id) {
TreeMap<String, String> value = null;
LinkedHashMap keys = new LinkedHashMap();
keys.put(key, id);
if (entries.containsKey(keys)) {
value = entries.get(keys);
}
return value;
}
public void set(LinkedHashMap<String, String> key, TreeMap value) {
entries.put(key, value);
}
public TreeMap<LinkedHashMap<String, String>, TreeMap<String, String>> get() {
return (TreeMap<LinkedHashMap<String, String>, TreeMap<String, String>>) 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<String, String> keys;
TreeMap<String, String> options;
for (Map.Entry<LinkedHashMap<String, String>, TreeMap<String, String>> 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<String, String> 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;
case VIRTIO:
controllerDevice = "virtio-9p-device";
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<VmMetaData.HardDisk> getHdds() {
return hddsArray;
}
public String toString(boolean filtered) {
StringBuilder sb = new StringBuilder(300);
LinkedHashMap<LinkedHashMap<String, String>, TreeMap<String, String>> sortedArray = null;
if (filtered) {
sortedArray = new LinkedHashMap<>();
for (Map.Entry<LinkedHashMap<String, String>, TreeMap<String, String>> 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<LinkedHashMap<String, String>, TreeMap<String, String>> 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<String, String> 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();
}
}