| /* |
| * 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.*; |
| import prefuse.action.*; |
| import prefuse.action.layout.*; |
| import prefuse.action.assignment.*; |
| 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 org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import java.sql.*; |
| import java.util.*; |
| |
| 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 = "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 = ""; |
| this.timezone = ""; |
| this.query_state = ""; |
| this.query_stat_type = ""; |
| param_map = new HashMap<String, String>(); |
| } |
| |
| /** |
| * 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 query_stat_type Query state type |
| * @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 = cluster; |
| if (timezone != null) { |
| this.timezone = timezone; |
| } else { |
| this.timezone = null; |
| } |
| this.query_state = event_type; |
| this.query_stat_type = 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 = cluster; |
| if (timezone != null) { |
| this.timezone = timezone; |
| } else { |
| this.timezone = null; |
| } |
| this.query_state = query_state; |
| this.query_stat_type = 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 = cluster; |
| if (timezone != null) { |
| this.timezone = timezone; |
| } else { |
| this.timezone = null; |
| } |
| this.query_state = query_state; |
| this.query_stat_type = 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 = query_state; |
| } else { |
| this.query_state = "read"; |
| } |
| String query_stat_type = xf.getParameter("query_stat_type"); |
| if (query_stat_type != null) { |
| this.query_stat_type = query_stat_type; |
| } else { |
| this.query_stat_type = "transaction_count"; |
| } |
| this.timezone = session.getAttribute("time_zone").toString(); |
| } |
| |
| /** |
| * Set dimensions of image to be generated |
| * Call before calling @see #run |
| * @param width Image width in pixels |
| * @param height Image height in pixels |
| */ |
| 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 |
| * @param legendopt Flag to control plot legends |
| */ |
| 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 |
| * @param output Image output stream |
| * @param img_fmt Image format |
| * @param scale Image scale |
| * @return true if image is saved |
| */ |
| 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); |
| 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"); |
| |
| VisualTable legend_labels_table_viz = this.viz.addTable(addinfogroup, legend_labels_table); |
| |
| legend_labels_table_viz.setFloat(0, VisualItem.X, this.SIZE_X/2f); |
| legend_labels_table_viz.setFloat(0, VisualItem.Y, BORDER[1]/2f); |
| 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/2f); |
| legend_labels_table_viz.setFloat(1, VisualItem.Y, BORDER[1] + (BOXWIDTH*hd.num_hosts) + BORDER[3]/2f); |
| 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]/2f); |
| legend_labels_table_viz.setFloat(2, VisualItem.Y, this.SIZE_Y/2f); |
| 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; |
| 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; |
| |
| // 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 |
| * @return heat map data JSON |
| */ |
| @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = |
| "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE", |
| justification = "Dynamic based upon tables in the database") |
| 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 sqlTemplate = "select block_id,start_time,finish_time,start_time_millis,finish_time_millis,status,state_name,hostname,other_host,bytes from [%s] where finish_time between '[start]' and '[end]' and (%s) order by start_time"; |
| String query; |
| if (this.query_state != null && this.query_state.equals("read")) { |
| query = String.format(sqlTemplate, table,"state_name like 'read_local' or state_name like 'read_remote'"); |
| } else if (this.query_state != null && this.query_state.equals("write")) { |
| query = String.format(sqlTemplate, table, "state_name like 'write_local' or state_name like 'write_remote' or state_name like 'write_replicated'"); |
| } else { |
| query = String.format(sqlTemplate, table, "state_name like '" + query_state + "'"); |
| } |
| Macro mp = new Macro(start,end,query); |
| String q = mp.toString(); |
| |
| ArrayList<HashMap<String, Object>> events = new ArrayList<HashMap<String, Object>>(); |
| |
| ResultSet rs = null; |
| |
| log.debug("Query: " + q); |
| // run query, extract results |
| try { |
| rs = dbw.query(q); |
| ResultSetMetaData rmeta = rs.getMetaData(); |
| int col = rmeta.getColumnCount(); |
| while (rs.next()) { |
| HashMap<String, Object> event = new HashMap<String, Object>(); |
| 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(); |
| } |
| |
| 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, i); |
| host_rev_indices.put(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 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 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 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 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 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 = stats; |
| hd.min = min; |
| hd.max = max; |
| hd.num_hosts = num_hosts; |
| hd.agg_tab = agg_tab; |
| |
| this.add_info_extra = new StringBuilder().append("\nState: ").append(this.prettyStateNames.get(this.query_state)). |
| append(" (").append(events.size()).append(" ").append(this.query_state). |
| append("'s [").append(this.query_stat_type).append("])\n"). |
| append("Plotted value range: [").append(hd.min).append(",").append(hd.max). |
| append("] (Zeros in black)").toString(); |
| |
| hd.hostnames = new String [num_hosts]; |
| for (int i = 0; i < num_hosts; i++) { |
| String curr_host = host_rev_indices.get(permute[i]); |
| hd.hostnames[i] = curr_host; |
| } |
| |
| return hd; |
| } |
| |
| } |