/*
* Copyright (c) Fabien Hermenier
*
* This file is part of Entropy.
*
* Entropy is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Entropy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Entropy. If not, see <http://www.gnu.org/licenses/>.
*/
package entropy.configuration.parser;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import entropy.configuration.Configuration;
import entropy.configuration.ManagedElementSet;
import entropy.configuration.Node;
import entropy.configuration.SimpleConfiguration;
import entropy.configuration.SimpleManagedElementSet;
import entropy.configuration.SimpleNode;
import entropy.configuration.SimpleVirtualMachine;
import entropy.configuration.VirtualMachine;
/**
* Serialize and un-serialize a configuration from/to a plain text format, human readable.
* Mostly the original format of a configuration.
*
* @author Fabien Hermenier
*/
public final class PlainTextConfigurationSerializer extends FileConfigurationSerializer {
private static final PlainTextConfigurationSerializer INSTANCE = new PlainTextConfigurationSerializer();
/**
* Default separator that appears before the nodes declaration.
*/
public static final String LIST_NODES = "#list of nodes";
/**
* Default separator that appears before the virtual machines' declaration.
*/
public static final String LIST_VMS = "#list of VMs";
/**
* Default separator that appears before the configuration declaration.
*/
public static final String CONFIG = "#initial configuration";
public static final String END_CONFIG = "#end of configuration";
//Hack for plan without end mark for the configuration.
public static final String START_PLAN = "#Reconfiguration";
/**
* Default field separator.
*/
public static final String FIELD_SEP = " ";
/**
* Indicate the farm of the Virtual machines in waiting state.
*/
public static final String FARM = "FARM";
//VirtualMachine Parsing part
/**
* Index of the name of the virtual machine.
*/
public static final int VM_NAME_IDX = 0;
/**
* Index of the nb of CPUs value.
*/
public static final int VM_NB_CPU_IDX = 1;
/**
* Index of the cpu consumption value.
*/
public static final int VM_CONSO_CPU_IDX = 2;
/**
* Index of the memory consumption value.
*/
public static final int VM_CONSO_MEM_IDX = 3;
/**
* Number minimum token in the line.
*/
public static final int VM_NB_MIN_TOKENS = 4;
/**
* Index of the lease identifier in the line (optional information).
*/
public static final int VM_LEASE_IDX = 4;
/**
* Field separator to indicate a resource demand.
*/
public static final String VM_DEMAND_SEP = "->";
//Node part
/**
* Index of the name of the node.
*/
public static final int NODE_NAME_IDX = 0;
/**
* Index of the nb of CPUs value.
*/
public static final int NODE_NB_CPU_IDX = 1;
/**
* Index of the cpu capacity value.
*/
public static final int NODE_CAPA_CPU_IDX = 2;
/**
* Index of the memory capacity value.
*/
public static final int NODE_CAPA_MEM_IDX = 3;
/**
* Number of tokens in a line.
*/
public static final int NODE_NB_TOKENS = 4;
private PlainTextConfigurationSerializer() {
}
public static PlainTextConfigurationSerializer getInstance() {
return INSTANCE;
}
public Configuration unSerialize(BufferedReader reader) throws IOException, ConfigurationSerializerException {
Configuration conf = new SimpleConfiguration();
ManagedElementSet<VirtualMachine> vms = new SimpleManagedElementSet<VirtualMachine>();
ManagedElementSet<Node> nodes = new SimpleManagedElementSet<Node>();
String state = null;
String line = reader.readLine();
while (line != null && !line.equals(END_CONFIG) && !line.startsWith(START_PLAN)) {
if (line.startsWith(LIST_NODES)) {
state = LIST_NODES;
} else if (line.startsWith(LIST_VMS)) {
state = LIST_VMS;
} else if (line.startsWith(CONFIG)) {
state = CONFIG;
} else if (line.length() > 0) {
if (state == null) {
throw new ConfigurationSerializerException("Unrecognized characters: " + line);
} else if (state.equals(LIST_NODES)) {
nodes.add(readNode(line));
} else if (state.equals(LIST_VMS)) {
vms.add(readVirtualMachine(line));
} else if (state.equals(CONFIG)) {
String[] toks = line.split(FIELD_SEP);
if (toks[0].equals(FARM)) {
for (int i = 1; i < toks.length; i++) {
VirtualMachine vm = vms.get(toks[i]);
if (vm != null) {
conf.addWaiting(vm);
} else {
throw new ConfigurationSerializerException("VirtualMachine '" + toks[i] + "' is unknown");
}
}
} else if (toks[0].startsWith("(")) {
String id = toks[0].substring(1, toks[0].indexOf(')'));
Node n = nodes.get(id);
if (n != null) {
conf.addOffline(n);
} else {
throw new ConfigurationSerializerException("Node '" + id + "' is unknown");
}
} else {
Node n = nodes.get(toks[0]);
if (n == null) {
throw new ConfigurationSerializerException("Node '" + toks[0] + "' is unknown");
}
conf.addOnline(n);
for (int i = 1; i < toks.length; i++) {
if (toks[i].startsWith("(")) {
String id = toks[i].substring(1, toks[i].indexOf(')'));
VirtualMachine vm = vms.get(id);
if (vm == null) {
throw new ConfigurationSerializerException("VirtualMachine '" + id + "' is unknow");
} else {
conf.setSleepOn(vm, n);
}
} else {
VirtualMachine vm = vms.get(toks[i]);
if (vm == null) {
throw new ConfigurationSerializerException("VirtualMachine '" + toks[i] + "' is unknow");
} else {
conf.setRunOn(vm, n);
}
}
}
}
} else {
throw new ConfigurationSerializerException("Unrecognized characters: " + line);
}
}
line = reader.readLine();
}
return conf;
}
@Override
public Configuration unSerialize(InputStream in) throws IOException, ConfigurationSerializerException {
return unSerialize(new BufferedReader(new InputStreamReader(in)));
}
@Override
public void serialize(Configuration c, OutputStream o) throws IOException {
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(o));
out.write(LIST_NODES);
out.write("\n");
for (Node n : c.getOnlines()) {
out.write(writeNode(n));
out.write("\n");
}
for (Node n : c.getOfflines()) {
out.write(writeNode(n));
out.write("\n");
}
out.write(LIST_VMS);
out.write("\n");
for (VirtualMachine vm : c.getRunnings()) {
out.write(writeVirtualMachine(vm));
out.write("\n");
}
for (VirtualMachine vm : c.getSleepings()) {
out.write(writeVirtualMachine(vm));
out.write("\n");
}
for (VirtualMachine vm : c.getWaitings()) {
out.write(writeVirtualMachine(vm));
out.write("\n");
}
out.write(CONFIG);
out.write("\n");
for (Node n : c.getOnlines()) {
out.write(n.getName());
for (VirtualMachine vm : c.getRunnings(n)) {
out.write(" " + vm.getName());
}
for (VirtualMachine vm : c.getSleepings(n)) {
out.write(" (" + vm.getName());
out.write(")");
}
out.write("\n");
}
for (Node n : c.getOfflines()) {
out.write("(" + n.getName() + ")");
out.write("\n");
}
out.write(FARM);
for (VirtualMachine vm : c.getWaitings()) {
out.write(" " + vm.getName());
}
out.write("\n");
out.write(END_CONFIG);
out.write("\n");
out.flush();
} finally {
/*if (out != null) {
out.close();
} */
}
}
private String writeVirtualMachine(VirtualMachine vm) {
StringBuilder buffer = new StringBuilder(50);
buffer.append(vm.getName());
buffer.append(" ");
buffer.append(vm.getNbOfCPUs());
buffer.append(" ");
buffer.append(vm.getCPUConsumption());
if (vm.getCPUDemand() != vm.getCPUConsumption()) {
buffer.append(VM_DEMAND_SEP);
buffer.append(vm.getCPUDemand());
}
buffer.append(" ");
buffer.append(vm.getMemoryConsumption());
if (vm.getMemoryConsumption() != vm.getMemoryDemand()) {
buffer.append(VM_DEMAND_SEP);
buffer.append(vm.getMemoryDemand());
}
if (vm.getVJobId() != null) {
buffer.append(" ");
buffer.append(vm.getVJobId());
}
return buffer.toString();
}
private VirtualMachine readVirtualMachine(String line) throws ConfigurationSerializerException {
String[] toks = line.split(FIELD_SEP);
if (toks.length < VM_NB_MIN_TOKENS) {
throw new ConfigurationSerializerException("'" + line + "' should have at lease " + VM_NB_MIN_TOKENS + " tokens");
}
String name = toks[VM_NAME_IDX];
int consoCPU;
int consoMem;
int needCPU = -1;
int needMem = -1;
int nbCPU = Integer.parseInt(toks[VM_NB_CPU_IDX]);
if (toks[VM_CONSO_CPU_IDX].contains(VM_DEMAND_SEP)) {
//String[] ss = toks[VM_CONSO_CPU_IDX].split(VM_DEMAND_SEP);
//System.err.println(toks[VM_CONSO_CPU_IDX]);
consoCPU = Integer.parseInt(toks[VM_CONSO_CPU_IDX].substring(0, toks[VM_CONSO_CPU_IDX].indexOf(VM_DEMAND_SEP)));
needCPU = Integer.parseInt(toks[VM_CONSO_CPU_IDX].substring(toks[VM_CONSO_CPU_IDX].indexOf(VM_DEMAND_SEP) + 2, toks[VM_CONSO_CPU_IDX].length()));
//System.err.println(toks[VM_CONSO_CPU_IDX] + " " + "|" + consoCPU + "| |" + needCPU+"|");
/*if ((ss.length != 2) && (ss[0].length() == 0 || ss[1].length() == 0)) {
throw new ConfigurationSerializerException("Bad syntax for '" + toks[VM_CONSO_CPU_IDX] + "'. '" + VM_DEMAND_SEP + "' must be between 2 integers, whitout spaces");
} */
//consoCPU = Integer.parseInt(ss[0]);
//needCPU = Integer.parseInt(ss[1]);
} else {
consoCPU = Integer.parseInt(toks[VM_CONSO_CPU_IDX]);
}
if (toks[VM_CONSO_MEM_IDX].contains(VM_DEMAND_SEP)) {
String[] ss = toks[VM_CONSO_MEM_IDX].split(VM_DEMAND_SEP);
if ((ss.length != 2) && (ss[0].length() == 0 || ss[1].length() == 0)) {
throw new ConfigurationSerializerException("Bad syntax for '" + toks[VM_CONSO_MEM_IDX] + "'. '" + VM_DEMAND_SEP + "' must be between 2 integers, whitout spaces");
}
consoMem = Integer.parseInt(ss[0]);
needMem = Integer.parseInt(ss[1]);
} else {
consoMem = Integer.parseInt(toks[VM_CONSO_MEM_IDX]);
}
if (nbCPU <= 0 || consoCPU < 0 || consoMem < 0) {
throw new ConfigurationSerializerException("Incorrect value for '" + line + "'. all numbers must be positive");
}
VirtualMachine vm = new SimpleVirtualMachine(name, nbCPU, consoCPU, consoMem);
if (needCPU >= 0) {
vm.setCPUDemand(needCPU);
} else {
vm.setCPUDemand(consoCPU);
}
if (needMem >= 0) {
vm.setMemoryDemand(needMem);
} else {
vm.setMemoryDemand(consoMem);
}
/*if (toks.length == 5) {
vm.updateValue(DefaultVirtualMachine.VJOB_ID, toks[VM_LEASE_IDX]);
} */
return vm;
}
private Node readNode(String line) throws ConfigurationSerializerException {
String[] toks = line.split(FIELD_SEP);
if (toks.length != NODE_NB_TOKENS) {
throw new ConfigurationSerializerException("'" + line + "' should have 4 tokens");
}
String name = toks[NODE_NAME_IDX];
try {
int nbCPU = Integer.parseInt(toks[NODE_NB_CPU_IDX]);
int capaCPU = Integer.parseInt(toks[NODE_CAPA_CPU_IDX]);
int capaMem = Integer.parseInt(toks[NODE_CAPA_MEM_IDX]);
if (nbCPU <= 0 || capaCPU < 0 || capaMem < 0) {
throw new ConfigurationSerializerException("Incorrect value for '" + line + "'. all numbers must be positive");
}
return new SimpleNode(name, nbCPU, capaCPU, capaMem);
} catch (NumberFormatException e) {
throw new ConfigurationSerializerException("Unable to create a Node from '" + line + "':" + e.getMessage());
}
}
private String writeNode(Node node) {
StringBuilder buffer = new StringBuilder(100);
buffer.append(node.getName());
buffer.append(" ");
buffer.append(node.getNbOfCPUs());
buffer.append(" ");
buffer.append(node.getCPUCapacity());
buffer.append(" ");
buffer.append(node.getMemoryCapacity());
return buffer.toString();
}
}