/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.chukwa.analysis.salsa.visualization;
import prefuse.data.io.sql.*;
import prefuse.data.Table;
import prefuse.data.expression.parser.*;
import prefuse.data.expression.*;
import prefuse.data.column.*;
import prefuse.data.query.*;
import prefuse.data.*;
import prefuse.action.*;
import prefuse.action.layout.*;
import prefuse.action.assignment.*;
import prefuse.visual.expression.*;
import prefuse.visual.*;
import prefuse.render.*;
import prefuse.util.*;
import prefuse.*;
import org.apache.hadoop.chukwa.hicc.OfflineTimeHandler;
import org.apache.hadoop.chukwa.hicc.TimeHandler;
import org.apache.hadoop.chukwa.util.DatabaseWriter;
import org.apache.hadoop.chukwa.database.Macro;
import org.apache.hadoop.chukwa.util.XssFilter;
import javax.servlet.http.*;
import javax.swing.BorderFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.sql.*;
import java.util.*;
import java.text.NumberFormat;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.awt.Font;
import java.awt.geom.Rectangle2D;
import java.awt.Color;
/**
* Static image rendering for heatmap visualization of spatial HDFS
* activity patterns for scalable rendering on front-end (web-browser)
* Handles database data retrieval, transforming data to form for
* visualization elements, and initializing and calling visualization
* elements
*/
public class Heatmap {
/**
* Internal representation of all data needed to render heatmap;
* data-handling code populates this data structure
*/
protected static class HeatmapData {
public Table agg_tab;
public long [][] stats;
public long min;
public long max;
public int num_hosts;
public String [] hostnames;
public HeatmapData() {
}
}
private static Log log = LogFactory.getLog(Heatmap.class);
static final String START_FIELD_NAME = "start_time_num";
static final String END_FIELD_NAME = "finish_time_num";
int BOXWIDTH = 250;
int SIZE_X = 1600, SIZE_Y=1600;
final int [] BORDER = {200,150,150,150};
final int LEGEND_X_OFFSET = 10;
final int LEGEND_Y_OFFSET = 0;
final int LEGEND_TEXT_OFFSET = 10;
final int LEGEND_FONT_SIZE = 24;
final int AXIS_NAME_FONT_SIZE = 24;
protected boolean offline_use = true;
protected HttpServletRequest request;
// for offline use only
// keys that need to be filled:
// period (last1/2/3/6/12/24hr,last7d,last30d), time_type (range/last), start, end
protected HashMap<String, String> param_map;
protected String cluster;
protected String timezone;
protected String query_state;
protected String query_stat_type;
protected final String table = new String("filesystem_fsm");
protected boolean plot_legend = false; // controls whether to plot hostnames
protected boolean sort_nodes = true;
protected boolean plot_additional_info = true;
protected String add_info_extra = null;
protected Display dis;
protected Visualization viz;
protected Rectangle2D dataBound = new Rectangle2D.Double();
protected Rectangle2D xlabBound = new Rectangle2D.Double();
protected Rectangle2D ylabBound = new Rectangle2D.Double();
protected Rectangle2D labelBottomBound = new Rectangle2D.Double();
protected HashMap<String, String> prettyStateNames;
/* Different group names allow control of what Renderers to use */
final String maingroup = "Data";
final String othergroup = "Misc";
final String labelgroup = "Label";
final String legendgroup = "Legend";
final String legendshapegroup = "LegendShape";
final String addinfogroup = "AddInfo";
final String addinfoshapegroup = "AddInfoShape";
public Heatmap() {
this.cluster = new String("");
this.timezone = new String("");
this.query_state = new String("");
this.query_stat_type = new String("");
param_map = new HashMap<String, String>();
}
/**
* @brief Constructor for Swimlanes visualization object
* @param timezone Timezone string from environment
* @param cluster Cluster name from environment
* @param event_type Whether to display shuffles or not
* @param valmap HashMap of key/value pairs simulating parameters from a HttpRequest
*/
public Heatmap
(String timezone, String cluster, String event_type,
String query_stat_type,
HashMap<String, String> valmap)
{
this.cluster = new String(cluster);
if (timezone != null) {
this.timezone = new String(timezone);
} else {
this.timezone = null;
}
this.query_state = new String(event_type);
this.query_stat_type = new String(query_stat_type);
/* This should "simulate" an HttpServletRequest
* Need to have "start" and "end" in seconds since Epoch
*/
this.param_map = valmap;
}
public Heatmap
(String timezone, String cluster, String query_state,
String query_stat_type,
HashMap<String, String> valmap, String shuffles)
{
this.cluster = new String(cluster);
if (timezone != null) {
this.timezone = new String(timezone);
} else {
this.timezone = null;
}
this.query_state = new String(query_state);
this.query_stat_type = new String(query_stat_type);
/* This should "simulate" an HttpServletRequest
* Need to have "start" and "end" in seconds since Epoch
*/
this.param_map = valmap;
}
public Heatmap
(String timezone, String cluster, String query_state,
String query_stat_type,
HashMap<String, String> valmap,
int w, int h)
{
this.cluster = new String(cluster);
if (timezone != null) {
this.timezone = new String(timezone);
} else {
this.timezone = null;
}
this.query_state = new String(query_state);
this.query_stat_type = new String(query_stat_type);
/* This should "simulate" an HttpServletRequest
* Need to have "start" and "end" in seconds since Epoch
*/
this.param_map = valmap;
this.SIZE_X = w;
this.SIZE_Y = h;
}
public Heatmap(HttpServletRequest request) {
XssFilter xf = new XssFilter(request);
this.offline_use = false;
this.request = request;
HttpSession session = request.getSession();
this.cluster = session.getAttribute("cluster").toString();
String query_state = xf.getParameter("query_state");
if (query_state != null) {
this.query_state = new String(query_state);
} else {
this.query_state = new String("read");
}
String query_stat_type = xf.getParameter("query_stat_type");
if (query_stat_type != null) {
this.query_stat_type = new String(query_stat_type);
} else {
this.query_stat_type = new String("transaction_count");
}
this.timezone = session.getAttribute("time_zone").toString();
}
/**
* Set dimensions of image to be generated
* Call before calling @see #run
*/
public void setDimensions(int width, int height) {
this.SIZE_X=width;
this.SIZE_Y=height;
}
/**
* Specify whether to print labels of hosts along axes
* Call before calling @see #run
*/
public void setLegend(boolean legendopt) {
if (legendopt) {
this.plot_legend = true;
} else {
this.plot_legend = false;
}
}
/**
* Generates image in specified format, and writes image as binary
* output to supplied output stream
*/
public boolean getImage(java.io.OutputStream output, String img_fmt, double scale) {
dis = new Display(this.viz);
dis.setSize(SIZE_X,SIZE_Y);
dis.setHighQuality(true);
dis.setFont(new Font(Font.SANS_SERIF,Font.PLAIN,24));
return dis.saveImage(output, img_fmt, scale);
}
protected void setupRenderer() {
this.viz.setRendererFactory(new RendererFactory(){
AbstractShapeRenderer sr = new ShapeRenderer();
ShapeRenderer sr_big = new ShapeRenderer(BOXWIDTH);
Renderer arY = new AxisRenderer(Constants.LEFT, Constants.TOP);
Renderer arX = new AxisRenderer(Constants.CENTER, Constants.BOTTOM);
PolygonRenderer pr = new PolygonRenderer(Constants.POLY_TYPE_LINE);
LabelRenderer lr = new LabelRenderer("label");
LabelRenderer lr_legend = new LabelRenderer("label");
public Renderer getRenderer(VisualItem item) {
lr_legend.setHorizontalAlignment(Constants.LEFT);
lr_legend.setVerticalAlignment(Constants.CENTER);
lr.setHorizontalAlignment(Constants.CENTER);
lr.setVerticalAlignment(Constants.CENTER);
if (item.isInGroup(maingroup)) {
return sr_big;
} else if (item.isInGroup(legendgroup)) {
return lr_legend;
} else if (item.isInGroup(addinfogroup)) {
return lr;
}
return sr;
}
});
}
// setup columns: add additional time fields
protected HeatmapData setupDataTable() {
HeatmapData hd = this.getData();
return hd;
}
protected void setupHeatmap(VisualTable vtab, HeatmapData hd)
{
long [][] stats = hd.stats;
int i, j, curr_idx;
long curr_val;
int num_hosts = hd.num_hosts;
ColorMap cm = new ColorMap(
ColorLib.getInterpolatedPalette(
ColorLib.color(ColorLib.getColor(32,0,0)),
ColorLib.color(Color.WHITE)
),
(double)hd.min,(double)hd.max
);
for (i = 0; i < num_hosts; i++) {
for (j = 0; j < num_hosts; j++) {
curr_idx = j+(i*num_hosts);
curr_val = stats[i][j];
if (curr_val >= hd.min) {
vtab.setFillColor(curr_idx, cm.getColor((double)curr_val));
} else if (curr_val == 0) {
vtab.setFillColor(curr_idx, ColorLib.color(Color.BLACK));
}
}
}
// gridlayout puts tiles on row-wise (row1, followed by row2, etc.)
GridLayout gl = new GridLayout(maingroup, num_hosts, num_hosts);
gl.setLayoutBounds(this.dataBound);
ActionList gl_list = new ActionList();
gl_list.add(gl);
this.viz.putAction("gridlayout",gl_list);
this.viz.run("gridlayout");
}
protected void addHostLabels(HeatmapData hd) {
Table legend_labels_table = new Table();
legend_labels_table.addColumn("label",String.class);
legend_labels_table.addRows(hd.hostnames.length);
for (int i = 0; i < hd.hostnames.length; i++) {
legend_labels_table.setString(i,"label",hd.hostnames[i]);
}
float start_x = LEGEND_X_OFFSET;
float start_y = LEGEND_Y_OFFSET + BORDER[1] + (BOXWIDTH/2);
float incr = this.BOXWIDTH;
VisualTable legend_labels_table_viz = this.viz.addTable(legendgroup, legend_labels_table);
for (int i = 0; i < hd.hostnames.length; i++) {
legend_labels_table_viz.setFloat(i, VisualItem.X, start_x + LEGEND_TEXT_OFFSET);
legend_labels_table_viz.setFloat(i, VisualItem.Y, start_y + (i * incr));
legend_labels_table_viz.setTextColor(i,ColorLib.color(java.awt.Color.BLACK));
legend_labels_table_viz.setFont(i,new Font(Font.SANS_SERIF,Font.PLAIN,LEGEND_FONT_SIZE));
}
}
protected void addAddlInfo(HeatmapData hd) {
Table legend_labels_table = new Table();
legend_labels_table.addColumn("label",String.class);
legend_labels_table.addRows(3);
String hostnumstring = "Number of hosts: " + hd.num_hosts;
if (sort_nodes) {
hostnumstring += " (nodes sorted)";
} else {
hostnumstring += " (nodes not sorted)";
}
if (add_info_extra != null) hostnumstring += add_info_extra;
legend_labels_table.setString(0,"label",hostnumstring);
legend_labels_table.setString(1,"label","Src. Hosts");
legend_labels_table.setString(2,"label","Dest. Hosts");
float start_x = LEGEND_X_OFFSET;
float start_y = LEGEND_Y_OFFSET + BORDER[1] + (BOXWIDTH/2);
float incr = this.BOXWIDTH;
VisualTable legend_labels_table_viz = this.viz.addTable(addinfogroup, legend_labels_table);
legend_labels_table_viz.setFloat(0, VisualItem.X, this.SIZE_X/2);
legend_labels_table_viz.setFloat(0, VisualItem.Y, BORDER[1]/2);
legend_labels_table_viz.setTextColor(0,ColorLib.color(java.awt.Color.BLACK));
legend_labels_table_viz.setFont(0,new Font(Font.SANS_SERIF,Font.PLAIN,LEGEND_FONT_SIZE));
legend_labels_table_viz.setFloat(1, VisualItem.X, this.SIZE_X/2);
legend_labels_table_viz.setFloat(1, VisualItem.Y, BORDER[1] + (BOXWIDTH*hd.num_hosts) + BORDER[3]/2);
legend_labels_table_viz.setTextColor(1,ColorLib.color(java.awt.Color.BLACK));
legend_labels_table_viz.setFont(1,new Font(Font.SANS_SERIF,Font.PLAIN,LEGEND_FONT_SIZE));
legend_labels_table_viz.setFloat(2, VisualItem.X, BORDER[0] + (BOXWIDTH*hd.num_hosts) + BORDER[2]/2);
legend_labels_table_viz.setFloat(2, VisualItem.Y, this.SIZE_Y/2);
legend_labels_table_viz.setTextColor(2,ColorLib.color(java.awt.Color.BLACK));
legend_labels_table_viz.setFont(2,new Font(Font.SANS_SERIF,Font.PLAIN,LEGEND_FONT_SIZE));
}
protected void initPrettyNames() {
this.prettyStateNames = new HashMap<String, String>();
prettyStateNames.put("read","Block Reads");
prettyStateNames.put("write","Block Writes");
prettyStateNames.put("read_local", "Local Block Reads");
prettyStateNames.put("write_local", "Local Block Writes");
prettyStateNames.put("read_remote", "Remote Block Reads");
prettyStateNames.put("write_remote", "Remote Block Writes");
prettyStateNames.put("write_replicated", "Replicated Block Writes");
}
/**
* Actual code that calls data, generates heatmap, and saves it
*/
public void run() {
initPrettyNames();
// setup visualization
this.viz = new Visualization();
// add table to visualization
HeatmapData hd = this.setupDataTable();
// setup bounds
int width, realwidth;
if (SIZE_X-BORDER[0]-BORDER[2] < SIZE_Y-BORDER[1]-BORDER[3]) {
BOXWIDTH = (SIZE_X-BORDER[0]-BORDER[2]) / hd.num_hosts;
} else {
BOXWIDTH = (SIZE_Y-BORDER[1]-BORDER[3]) / hd.num_hosts;
}
width = hd.num_hosts * BOXWIDTH;
this.dataBound.setRect(
BORDER[0]+BOXWIDTH/2,
BORDER[1]+BOXWIDTH/2,
width-BOXWIDTH,width-BOXWIDTH
);
this.SIZE_X = BORDER[0] + BORDER[2] + (hd.num_hosts * BOXWIDTH);
this.SIZE_Y = BORDER[1] + BORDER[3] + (hd.num_hosts * BOXWIDTH);
log.debug("width total: " + width + " width per state: " + BOXWIDTH + " xstart: "
+ (BORDER[0]+BOXWIDTH/2)
+ " ystart: " + (BORDER[1]+BOXWIDTH/2) + " (num hosts: "+hd.num_hosts+")");
log.debug("X size: " + this.SIZE_X + " Y size: " + this.SIZE_Y);
this.setupRenderer();
VisualTable data_tab_viz = viz.addTable(maingroup, hd.agg_tab);
setupHeatmap(data_tab_viz, hd);
ShapeAction legend_sa1 = null, legend_sa2 = null;
SpecifiedLayout legendlabels_sl1 = null, legendlabels_sl2 = null;
if (plot_legend) {
addHostLabels(hd);
legend_sa1 = new ShapeAction(legendshapegroup);
legendlabels_sl1 = new SpecifiedLayout(legendgroup, VisualItem.X, VisualItem.Y);
ActionList legenddraw = new ActionList();
legenddraw.add(legend_sa1);
this.viz.putAction(legendshapegroup, legenddraw);
ActionList legendlabelsdraw = new ActionList();
legendlabelsdraw.add(legendlabels_sl1);
this.viz.putAction(legendgroup,legendlabelsdraw);
}
if (plot_additional_info) {
addAddlInfo(hd);
legend_sa2 = new ShapeAction(addinfoshapegroup);
legendlabels_sl2 = new SpecifiedLayout(addinfogroup, VisualItem.X, VisualItem.Y);
ActionList legenddraw = new ActionList();
legenddraw.add(legend_sa2);
this.viz.putAction(addinfoshapegroup, legenddraw);
ActionList legendlabelsdraw = new ActionList();
legendlabelsdraw.add(legendlabels_sl2);
this.viz.putAction(addinfogroup,legendlabelsdraw);
}
}
protected boolean checkDone(int [] clustId) {
for (int i = 1; i < clustId.length; i++) {
if (clustId[i] != clustId[0]) return false;
}
return true;
}
/**
* Sort data for better visualization of patterns
*/
protected int [] hClust (long [][] stat)
{
int statlen = stat.length;
long [] rowSums = new long[statlen];
int [] permute = new int[statlen];
int i,j,k;
// initialize permutation
for (i = 0; i < statlen; i++) {
permute[i] = i;
}
for (i = 0; i < statlen; i++) {
rowSums[i] = 0;
for (j = 0; j < statlen; j++) {
rowSums[i] += stat[i][j];
}
}
// insertion sort
for (i = 0; i < statlen-1; i++) {
long val = rowSums[i];
int thispos = permute[i];
j = i-1;
while (j >= 0 && rowSums[j] > val) {
rowSums[j+1] = rowSums[j];
permute[j+1] = permute[j];
j--;
}
rowSums[j+1] = val;
permute[j+1] = thispos;
}
return permute;
}
/**
* Reorder rows (and columns) according to a given ordering
* Maintains same ordering along rows and columns
*/
protected long [][] doPermute (long [][] stat, int [] permute) {
int statlen = stat.length;
int i, j, curr_pos;
long [][] stat2 = new long[statlen][statlen];
assert(stat.length == permute.length);
for (i = 0; i < statlen; i++) {
curr_pos = permute[i];
for (j = 0; j < statlen; j++) {
stat2[i][j] = stat[curr_pos][permute[j]];
}
}
return stat2;
}
/**
* Interfaces with database to get data and
* populate data structures for rendering
*/
public HeatmapData getData() {
// preliminary setup
OfflineTimeHandler time_offline;
TimeHandler time_online;
long start, end, min, max;
if (offline_use) {
time_offline = new OfflineTimeHandler(param_map, this.timezone);
start = time_offline.getStartTime();
end = time_offline.getEndTime();
} else {
time_online = new TimeHandler(this.request, this.timezone);
start = time_online.getStartTime();
end = time_online.getEndTime();
}
DatabaseWriter dbw = new DatabaseWriter(this.cluster);
// setup query
String query;
if (this.query_state != null && this.query_state.equals("read")) {
query = "select block_id,start_time,finish_time,start_time_millis,finish_time_millis,status,state_name,hostname,other_host,bytes from ["+table+"] where finish_time between '[start]' and '[end]' and (state_name like 'read_local' or state_name like 'read_remote')";
} else if (this.query_state != null && this.query_state.equals("write")) {
query = "select block_id,start_time,finish_time,start_time_millis,finish_time_millis,status,state_name,hostname,other_host,bytes from ["+table+"] where finish_time between '[start]' and '[end]' and (state_name like 'write_local' or state_name like 'write_remote' or state_name like 'write_replicated')";
} else {
query = "select block_id,start_time,finish_time,start_time_millis,finish_time_millis,status,state_name,hostname,other_host,bytes from ["+table+"] where finish_time between '[start]' and '[end]' and state_name like '" + query_state + "'";
}
Macro mp = new Macro(start,end,query);
query = mp.toString() + " order by start_time";
ArrayList<HashMap<String, Object>> events = new ArrayList<HashMap<String, Object>>();
ResultSet rs = null;
log.debug("Query: " + query);
// run query, extract results
try {
rs = dbw.query(query);
ResultSetMetaData rmeta = rs.getMetaData();
int col = rmeta.getColumnCount();
while (rs.next()) {
HashMap<String, Object> event = new HashMap<String, Object>();
long event_time=0;
for(int i=1;i<=col;i++) {
if(rmeta.getColumnType(i)==java.sql.Types.TIMESTAMP) {
event.put(rmeta.getColumnName(i),rs.getTimestamp(i).getTime());
} else {
event.put(rmeta.getColumnName(i),rs.getString(i));
}
}
events.add(event);
}
} catch (SQLException ex) {
// handle any errors
log.error("SQLException: " + ex.getMessage());
log.error("SQLState: " + ex.getSQLState());
log.error("VendorError: " + ex.getErrorCode());
} finally {
dbw.close();
}
SimpleDateFormat format = new SimpleDateFormat("MMM dd yyyy HH:mm:ss");
log.info(events.size() + " results returned.");
HashSet<String> host_set = new HashSet<String>();
HashMap<String, Integer> host_indices = new HashMap<String, Integer>();
HashMap<Integer, String> host_rev_indices = new HashMap<Integer, String>();
// collect hosts, name unique hosts
for(int i = 0; i < events.size(); i++) {
HashMap<String, Object> event = events.get(i);
String curr_host = (String) event.get("hostname");
String other_host = (String) event.get("other_host");
host_set.add(curr_host);
host_set.add(other_host);
}
int num_hosts = host_set.size();
Iterator<String> host_iter = host_set.iterator();
for (int i = 0; i < num_hosts && host_iter.hasNext(); i++) {
String curr_host = host_iter.next();
host_indices.put(curr_host, new Integer(i));
host_rev_indices.put(new Integer(i),curr_host);
}
System.out.println("Number of hosts: " + num_hosts);
long stats[][] = new long[num_hosts][num_hosts];
long count[][] = new long[num_hosts][num_hosts]; // used for averaging
int start_millis = 0, end_millis = 0;
// deliberate design choice to duplicate code PER possible operation
// otherwise we have to do the mode check N times, for N states returned
//
// compute aggregate statistics
log.info("Query statistic type: "+this.query_stat_type);
if (this.query_stat_type.equals("transaction_count")) {
for(int i=0;i<events.size();i++) {
HashMap<String, Object> event = events.get(i);
start=(Long)event.get("start_time");
end=(Long)event.get("finish_time");
start_millis = Integer.parseInt(((String)event.get("start_time_millis")));
end_millis = Integer.parseInt(((String)event.get("finish_time_millis")));
String cell = (String) event.get("state_name");
String this_host = (String) event.get("hostname");
String other_host = (String) event.get("other_host");
int this_host_idx = host_indices.get(this_host).intValue();
int other_host_idx = host_indices.get(other_host).intValue();
// to, from
stats[other_host_idx][this_host_idx] += 1;
}
} else if (this.query_stat_type.equals("avg_duration")) {
for(int i=0;i<events.size();i++) {
HashMap<String, Object> event = events.get(i);
start=(Long)event.get("start_time");
end=(Long)event.get("finish_time");
start_millis = Integer.parseInt(((String)event.get("start_time_millis")));
end_millis = Integer.parseInt(((String)event.get("finish_time_millis")));
String cell = (String) event.get("state_name");
String this_host = (String) event.get("hostname");
String other_host = (String) event.get("other_host");
int this_host_idx = host_indices.get(this_host).intValue();
int other_host_idx = host_indices.get(other_host).intValue();
long curr_val = end_millis - start_millis + ((end - start)*1000);
// to, from
stats[other_host_idx][this_host_idx] += curr_val;
count[other_host_idx][this_host_idx] += 1;
}
for (int i = 0; i < num_hosts; i++) {
for (int j = 0; j < num_hosts; j++) {
if (count[i][j] > 0) stats[i][j] = stats[i][j] / count[i][j];
}
}
} else if (this.query_stat_type.equals("avg_volume")) {
for(int i=0;i<events.size();i++) {
HashMap<String, Object> event = events.get(i);
start=(Long)event.get("start_time");
end=(Long)event.get("finish_time");
start_millis = Integer.parseInt(((String)event.get("start_time_millis")));
end_millis = Integer.parseInt(((String)event.get("finish_time_millis")));
String cell = (String) event.get("state_name");
String this_host = (String) event.get("hostname");
String other_host = (String) event.get("other_host");
int this_host_idx = host_indices.get(this_host).intValue();
int other_host_idx = host_indices.get(other_host).intValue();
long curr_val = Long.parseLong((String)event.get("bytes"));
// to, from
stats[other_host_idx][this_host_idx] += curr_val;
count[other_host_idx][this_host_idx] += 1;
}
for (int i = 0; i < num_hosts; i++) {
for (int j = 0; j < num_hosts; j++) {
if (count[i][j] > 0) stats[i][j] = stats[i][j] / count[i][j];
}
}
} else if (this.query_stat_type.equals("total_duration")) {
for(int i=0;i<events.size();i++) {
HashMap<String, Object> event = events.get(i);
start=(Long)event.get("start_time");
end=(Long)event.get("finish_time");
start_millis = Integer.parseInt(((String)event.get("start_time_millis")));
end_millis = Integer.parseInt(((String)event.get("finish_time_millis")));
String cell = (String) event.get("state_name");
String this_host = (String) event.get("hostname");
String other_host = (String) event.get("other_host");
int this_host_idx = host_indices.get(this_host).intValue();
int other_host_idx = host_indices.get(other_host).intValue();
double curr_val = end_millis - start_millis + ((end - start)*1000);
// to, from
stats[other_host_idx][this_host_idx] += curr_val;
}
} else if (this.query_stat_type.equals("total_volume")) {
for(int i=0;i<events.size();i++) {
HashMap<String, Object> event = events.get(i);
start=(Long)event.get("start_time");
end=(Long)event.get("finish_time");
start_millis = Integer.parseInt(((String)event.get("start_time_millis")));
end_millis = Integer.parseInt(((String)event.get("finish_time_millis")));
String cell = (String) event.get("state_name");
String this_host = (String) event.get("hostname");
String other_host = (String) event.get("other_host");
int this_host_idx = host_indices.get(this_host).intValue();
int other_host_idx = host_indices.get(other_host).intValue();
long curr_val = Long.parseLong((String)event.get("bytes"));
// to, from
stats[other_host_idx][this_host_idx] += curr_val;
}
}
int [] permute = null;
if (sort_nodes) {
permute = hClust(stats);
stats = doPermute(stats,permute);
}
Table agg_tab = new Table();
agg_tab.addColumn("stat", long.class);
min = Long.MAX_VALUE;
max = Long.MIN_VALUE;
agg_tab.addRows(num_hosts*num_hosts);
// row-wise placement (row1, followed by row2, etc.)
for (int i = 0; i < num_hosts; i++) {
for (int j = 0; j < num_hosts; j++) {
agg_tab.setLong((i*num_hosts)+j,"stat",stats[i][j]);
if (stats[i][j] > max) max = stats[i][j];
if (stats[i][j] > 0 && stats[i][j] < min) min = stats[i][j];
}
}
if (min == Long.MAX_VALUE) min = 0;
log.info(agg_tab);
// collate data
HeatmapData hd = new HeatmapData();
hd.stats = new long[num_hosts][num_hosts];
hd.stats = stats;
hd.min = min;
hd.max = max;
hd.num_hosts = num_hosts;
hd.agg_tab = agg_tab;
this.add_info_extra = new String("\nState: "+this.prettyStateNames.get(this.query_state)+
" ("+events.size()+" "+this.query_state+"'s ["+this.query_stat_type+"])\n" +
"Plotted value range: ["+hd.min+","+hd.max+"] (Zeros in black)");
hd.hostnames = new String [num_hosts];
for (int i = 0; i < num_hosts; i++) {
String curr_host = host_rev_indices.get(new Integer(permute[i]));
if (sort_nodes) {
hd.hostnames[i] = new String(curr_host);
} else {
hd.hostnames[i] = new String(curr_host);
}
}
return hd;
}
}