/*
* 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.plan.visualization;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import entropy.plan.Plan;
import entropy.plan.TimedReconfigurationPlan;
import entropy.plan.action.Action;
import entropy.plan.action.Migration;
import entropy.plan.action.Pause;
import entropy.plan.action.Resume;
import entropy.plan.action.Run;
import entropy.plan.action.Shutdown;
import entropy.plan.action.Startup;
import entropy.plan.action.Stop;
import entropy.plan.action.Suspend;
import entropy.plan.action.UnPause;
/**
* Visualize a reconfiguration plan as a dot multigraph, indicating
* the movement of the VMs.
*
* @author Fabien Hermenier
*/
public class DotVisualizer implements PlanVisualizer {
/**
* The output file.
*/
private String out;
/**
* The current buffer.
*/
private StringBuilder buffer;
/**
* Make a new visualizer.
*
* @param path the path name to the output file
*/
public DotVisualizer(String path) {
this.out = path;
}
/**
* Get the path of the output file.
*
* @return a path
*/
public String getOutputFile() {
return out;
}
/**
* Set the output file.
*
* @param path the path to the output file
*/
public void setOutputFile(String path) {
this.out = path;
}
/**
* Build the dot graph.
*
* @param plan the plan to visualize
* @return {@code true} if the generation succeeds
*/
@Override
public boolean buildVisualization(TimedReconfigurationPlan plan) {
buffer = new StringBuilder("digraph TimedExecutionGraph{\n");
File parent = new File(getOutputFile()).getParentFile();
if (parent != null && !parent.exists() && !parent.mkdirs()) {
Plan.logger.error("Unable to create '" + parent.getName() + "'");
return false;
}
buffer.append("rankdir=LR;\n");
for (Action a : plan) {
a.injectToVisualizer(this);
}
buffer.append("}");
FileWriter out = null;
try {
out = new FileWriter(getOutputFile());
out.write(buffer.toString());
} catch (IOException e) {
Plan.logger.error(e.getMessage(), e);
return false;
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
Plan.logger.error(e.getMessage(), e);
}
}
}
return true;
}
private void setTimesLabel(Action a, StringBuilder b) {
if (a.getStartMoment() >= 0) {
b.append(", headlabel=\"").append(a.getStartMoment()).append("\",");
b.append("taillabel=\" ").append(a.getFinishMoment()).append("\"");
}
}
@Override
public void inject(Migration a) {
buffer.append(a.getHost().getName());
buffer.append(" -> ");
buffer.append(a.getDestination().getName());
buffer.append(" [label=\"migrate(");
buffer.append(a.getVirtualMachine().getName());
buffer.append(")\"");
setTimesLabel(a, buffer);
buffer.append(",color=\"green\"]\n");
}
@Override
public void inject(Run a) {
buffer.append("r");
buffer.append(a.getVirtualMachine().getName());
buffer.append(" -> ");
buffer.append(a.getHost().getName());
buffer.append(" [label=\"run(");
buffer.append(a.getVirtualMachine().getName());
buffer.append(")\"");
setTimesLabel(a, buffer);
buffer.append(", color=\"red\"]\n");
buffer.append("r");
buffer.append(a.getVirtualMachine().getName());
buffer.append(" [label=\"\", shape=none]\n");
}
@Override
public void inject(Stop a) {
buffer.append(a.getHost().getName());
buffer.append(" -> ");
buffer.append("s");
buffer.append(a.getVirtualMachine().getName());
buffer.append(" [label=\"stop(");
buffer.append(a.getVirtualMachine().getName());
buffer.append(")\"");
setTimesLabel(a, buffer);
buffer.append(", color=\"blue\"]\n");
buffer.append("s");
buffer.append(a.getVirtualMachine().getName());
buffer.append(" [label=\"\", shape=none]\n");
}
@Override
public void inject(Startup a) {
buffer.append("b");
buffer.append(a.getNode().getName());
buffer.append(" -> ");
buffer.append(a.getNode().getName());
buffer.append(" [label=\"boot\"");
setTimesLabel(a, buffer);
buffer.append(",color=\"blue\"]\n");
buffer.append("b");
buffer.append(a.getNode().getName());
buffer.append(" [label=\"\", shape=none]\n");
}
@Override
public void inject(Shutdown a) {
buffer.append(a.getNode().getName());
buffer.append(" -> ");
buffer.append(a.getNode().getName());
buffer.append(" [label=\"halt\"");
setTimesLabel(a, buffer);
buffer.append(", color=\"red\"]\n");
}
@Override
public void inject(Resume a) {
buffer.append(a.getHost().getName());
buffer.append(" -> ");
buffer.append(a.getDestination().getName());
buffer.append(" [label=\"resume(");
buffer.append(a.getVirtualMachine().getName()).append(")\"");
setTimesLabel(a, buffer);
buffer.append(", color=\"red\"]\n");
}
@Override
public void inject(Suspend a) {
buffer.append(a.getHost().getName());
buffer.append(" -> ");
buffer.append(a.getHost().getName());
buffer.append(" [label=\"suspend(");
buffer.append(a.getVirtualMachine().getName());
buffer.append(")\"");
setTimesLabel(a, buffer);
buffer.append(", color=\"blue\"]\n");
}
@Override
public void inject(Pause a) {
buffer.append("[label=\"pause(").append(a.getVirtualMachine().getName()).append(")\", color=\"blue\"]\n");
}
@Override
public void inject(UnPause a) {
buffer.append("[label=\"unpause(").append(a.getVirtualMachine().getName()).append(")\", color=\"red\"]\n");
}
}