You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2013/03/08 01:30:37 UTC
svn commit: r1454191 - in /incubator/ambari/trunk: ./ ambari-web/app/
ambari-web/app/mappers/ ambari-web/app/styles/
ambari-web/app/templates/main/apps/item/ ambari-web/app/utils/
ambari-web/app/views/main/apps/item/ ambari-web/vendor/scripts/ ambari-w...
Author: yusaku
Date: Fri Mar 8 00:30:36 2013
New Revision: 1454191
URL: http://svn.apache.org/r1454191
Log:
AMBARI-1491. Add task plots to job swimlane diagram. (yusaku)
Modified:
incubator/ambari/trunk/CHANGES.txt
incubator/ambari/trunk/ambari-web/app/mappers/jobs_mapper.js
incubator/ambari/trunk/ambari-web/app/messages.js
incubator/ambari/trunk/ambari-web/app/styles/apps.less
incubator/ambari/trunk/ambari-web/app/templates/main/apps/item/bar.hbs
incubator/ambari/trunk/ambari-web/app/utils/graph.js
incubator/ambari/trunk/ambari-web/app/views/main/apps/item/bar_view.js
incubator/ambari/trunk/ambari-web/app/views/main/apps/item/dag_view.js
incubator/ambari/trunk/ambari-web/vendor/scripts/workflow_visualization.js
incubator/ambari/trunk/ambari-web/vendor/styles/cubism.css
Modified: incubator/ambari/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/CHANGES.txt?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/CHANGES.txt (original)
+++ incubator/ambari/trunk/CHANGES.txt Fri Mar 8 00:30:36 2013
@@ -105,6 +105,8 @@ Trunk (unreleased changes):
IMPROVEMENTS
+ AMBARI-1491. Add task plots to job swimlane diagram. (billie via yusaku)
+
AMBARI-1584. Stack Upgrade Wizard - integrate host progress popup.
(yusaku)
Modified: incubator/ambari/trunk/ambari-web/app/mappers/jobs_mapper.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/mappers/jobs_mapper.js?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/mappers/jobs_mapper.js (original)
+++ incubator/ambari/trunk/ambari-web/app/mappers/jobs_mapper.js Fri Mar 8 00:30:36 2013
@@ -71,6 +71,23 @@ App.jobTimeLineMapper = App.QuickDataMap
}
});
+App.taskTimeLineMapper = App.QuickDataMapper.create({
+ model: null, //model will be set outside of mapper
+ config:{
+ allmap:'map',
+ allshuffle:'shuffle',
+ allreduce:'reduce'
+ },
+ map:function (json) {
+ var job = this.get('model'); // @model App.MainAppsItemBarView
+ var parseResult = this.parseIt(json, this.config);
+
+ $.each(parseResult, function (field, value) {
+ job.set(field, value);
+ });
+ }
+});
+
App.jobTasksMapper = App.QuickDataMapper.create({
model: null, //model will be set outside of mapper
config:{
@@ -88,4 +105,4 @@ App.jobTasksMapper = App.QuickDataMapper
job.set(field, value);
});
}
-});
\ No newline at end of file
+});
Modified: incubator/ambari/trunk/ambari-web/app/messages.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/messages.js?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/messages.js (original)
+++ incubator/ambari/trunk/ambari-web/app/messages.js Fri Mar 8 00:30:36 2013
@@ -926,10 +926,8 @@ Em.I18n.translations = {
'apps.filters.nothingToShow': 'No jobs to display',
'apps.dagCharts.popup':'Job Charts',
'apps.dagCharts.popup.job': 'Job',
- 'apps.dagCharts.popup.dag':'DAG',
- 'apps.dagCharts.popup.tasks':'Timeline & Tasks',
- 'apps.dagCharts.popup.tasks.timeline':'Job Timeline',
- 'apps.dagCharts.popup.tasks.tasks':'Job Tasks',
+ 'apps.dagCharts.popup.dag':'Job Timeline',
+ 'apps.dagCharts.popup.tasks':'Job Tasks',
'apps.isRunning.popup.title':'Is running',
'apps.isRunning.popup.content':'Job is running now',
@@ -940,4 +938,4 @@ Em.I18n.translations = {
'menu.item.jobs':'Jobs',
'menu.item.admin':'Admin'
-};
\ No newline at end of file
+};
Modified: incubator/ambari/trunk/ambari-web/app/styles/apps.less
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/styles/apps.less?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/styles/apps.less (original)
+++ incubator/ambari/trunk/ambari-web/app/styles/apps.less Fri Mar 8 00:30:36 2013
@@ -389,6 +389,17 @@
}
}
+ #job_tasks .axis line,
+ #job_tasks .axis path {
+ fill: none;
+ stroke: #000;
+ shape-rendering: crispEdges;
+ }
+ #job_tasks text.axislabel {
+ pointer-events: none;
+ text-anchor: middle;
+ }
+
ul.nav-tabs{
margin-bottom: 0;
}
@@ -470,4 +481,4 @@
}
}
-/*Big modal window end*/
\ No newline at end of file
+/*Big modal window end*/
Modified: incubator/ambari/trunk/ambari-web/app/templates/main/apps/item/bar.hbs
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/templates/main/apps/item/bar.hbs?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/templates/main/apps/item/bar.hbs (original)
+++ incubator/ambari/trunk/ambari-web/app/templates/main/apps/item/bar.hbs Fri Mar 8 00:30:36 2013
@@ -16,42 +16,5 @@
* limitations under the License.
}}
-<div class="btn-group">
- <button class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
- {{t apps.dagCharts.popup.job}} {{view.activeJob.id}} - {{view.activeJob.run.id}}
- <span class="caret"></span>
- </button>
- <ul class="dropdown-menu">
- {{#each job in view.content}}
- <li><a {{action "selectJob" job target="view"}} href="javascript:void(null)">{{t apps.dagCharts.popup.job}} {{job.id}} - {{job.run.id}}</a></li>
- {{/each}}
- </ul>
-</div>
-<div></div>
-<div id="graph1" class="pull-left">
- <div class="pull-left">
- <div id="graph1_desc" class="graph_desc">
- <h4>{{t apps.dagCharts.popup.tasks.timeline}}</h4>
- </div>
- <div id="y-axis"></div>
- <div id="chart"></div>
- <div id="timeline1"></div>
- </div>
- <div id="legend_container" class="pull-left">
- <div id="legend"></div>
- </div>
-</div>
-<div id="graph2" class="pull-right">
- <div class="pull-left">
- <div id="graph2_desc" class="graph_desc">
- <h4>{{t apps.dagCharts.popup.tasks.tasks}}</h4>
- </div>
- <div id="y-axis2"></div>
- <div id="job_tasks"></div>
- <div id="timeline2"></div>
- </div>
- <div id="tasks_legend_container" class="pull-left">
- <div id="tasks_legend"></div>
- </div>
-</div>
-<div class="clearfix"></div>
\ No newline at end of file
+<div id="job_tasks"></div>
+<div class="clearfix"></div>
Modified: incubator/ambari/trunk/ambari-web/app/utils/graph.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/utils/graph.js?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/utils/graph.js (original)
+++ incubator/ambari/trunk/ambari-web/app/utils/graph.js Fri Mar 8 00:30:36 2013
@@ -18,195 +18,63 @@
module.exports = {
- uniformSeries: function () {
- var series_min_length = 100000000;
- for (i=0; i<arguments.length; i++) {
- if (arguments[i].length < series_min_length) {
- series_min_length = arguments[i].length;
- }
- }
- for (i=0; i<arguments.length; i++) {
- if (arguments[i].length > series_min_length) {
- arguments[i].length = series_min_length;
- }
- }
+ durationFormatter:function(d) {
+ if (d==0) { return "0" }
+ var seconds = Math.floor(parseInt(d) / 1000);
+ if ( seconds < 60 )
+ return seconds + "s";
+ var minutes = Math.floor(seconds / 60);
+ if ( minutes < 60 ) {
+ var x = seconds - 60*minutes;
+ return minutes + "m" + (x==0 ? "" : " " + x + "s");
+ }
+ var hours = Math.floor(minutes / 60);
+ if ( hours < 24 ) {
+ var x = minutes - 60*hours;
+ return hours + "h" + (x==0 ? "" : " " + x + "m");
+ }
+ var days = Math.floor(hours / 24);
+ if ( days < 7 ) {
+ var x = hours - 24*days;
+ return days + "d " + (x==0 ? "" : " " + x + "h");
+ }
+ var weeks = Math.floor(days / 7);
+ var x = days - 7*weeks;
+ return weeks + "w " + (x==0 ? "" : " " + x + "d");
},
- /**
- * Get min, max for X and Y for provided series
- * @param series
- * @return {Object}
- */
- getExtInSeries: function(series) {
- var maxY = 0;
- var maxX = 0;
- var minX = 2147465647; // max timestamp value
- var minY = 2147465647;
- if (series.length > 0) {
- series.forEach(function(item){
- if (item.y > maxY) {
- maxY = item.y;
- }
- if (item.y < minY) {
- minY = item.y;
- }
- if (item.x > maxX) {
- maxX = item.x;
- }
- if (item.x < minX) {
- minX = item.x;
- }
- });
- }
- return {maxX: maxX, minX: minX, maxY: maxY, minY: minY};
+ bytesFormatter:function(y) {
+ if (y >= 1125899906842624) { return Math.floor(10 * y / 1125899906842624)/10 + " PB" }
+ else if (y >= 1099511627776){ return Math.floor(10 * y / 1099511627776)/10 + " TB" }
+ else if (y >= 1073741824) { return Math.floor(10 * y / 1073741824)/10 + " GB" }
+ else if (y >= 1048576) { return Math.floor(10 * y / 1048576)/10 + " MB" }
+ else if (y >= 1024) { return Math.floor(10 * y / 1024)/10 + " KB" }
+ else { return y + " B"}
},
- /**
- * Get min, max for x and Y for all provided series
- * @param args array of series
- * @return {Object}
- */
- getExtInAllSeries: function(args) {
- var maxx = [];
- var minx = [];
- var maxy = [];
- var miny = [];
- for (var i = 0; i < args.length; i++) {
- var localExt = this.getExtInSeries(args[i]);
- maxx.push(localExt.maxX);
- minx.push(localExt.minX);
- maxy.push(localExt.maxY);
- miny.push(localExt.minY);
- }
- return {
- maxX: Math.max.apply(null, maxx),
- minX: Math.min.apply(null, minx),
- maxY: Math.max.apply(null, maxy),
- minY: Math.min.apply(null, miny)
- };
- },
- /**
- * Get coordinates for new circle in the graph
- * New circle needed to prevent cut on the borders of the graph
- * List of arguments - series arrays
- * @return {Object}
- */
- getNewCircle: function() {
- var ext = this.getExtInAllSeries(arguments);
- var newX;
- if (ext.minX != 2147465647) {
- newX = ext.maxX + Math.round((ext.maxX - ext.minX) * 0.2);
- }
- else {
- newX = (new Date()).getTime();
- }
- var newY = ext.maxY * 1.2;
- return {
- x: newX,
- y: newY,
- r: 0,
- io: 0
- };
- },
- /**
- *
- * @param map
- * @param shuffle
- * @param reduce
- * @param w
- * @param h
- * @param element
- * @param legend_id
- * @param timeline_id
- */
- drawJobTimeLine:function (map, shuffle, reduce, w, h, element, legend_id, timeline_id) {
- map = $.parseJSON(map);
- shuffle = $.parseJSON(shuffle);
- reduce = $.parseJSON(reduce);
- if (!map || !shuffle || !reduce) {
- console.warn('drawJobTimeLine');
- return;
- }
- this.uniformSeries(map, reduce, shuffle);
- var ext = this.getExtInAllSeries([map, reduce, shuffle]);
- var submitTime = ext.minX;
- var maxX = ext.maxX; // Used on X-axis time stamps
-
- var graph = new Rickshaw.Graph({
- width:w,
- height:h,
- element:document.querySelector(element),
- renderer:'area',
- interpolation: 'step-after',
- strokeWidth: 2,
- stroke:true,
- series:[
- {
- data:map,
- color:'green',
- name:'maps'
- },
- {
- data:shuffle,
- color:'lightblue',
- name:'shuffles'
- },
- {
- data:reduce,
- color:'steelblue',
- name:'reduces'
- }
- ]
- }
- );
- graph.render();
-
- var legend = new Rickshaw.Graph.Legend({
- graph:graph,
- element:document.getElementById(legend_id)
- });
-
- var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
- graph:graph,
- legend:legend
- });
-
- var order = new Rickshaw.Graph.Behavior.Series.Order({
- graph:graph,
- legend:legend
- });
-
- var highlight = new Rickshaw.Graph.Behavior.Series.Highlight({
- graph:graph,
- legend:legend
- });
-
- var xAxis = new Rickshaw.Graph.Axis.Time({
- graph:graph,
- timeUnit: {
- name: 'Custom',
- seconds: Math.round((maxX - submitTime) / 2),
- formatter: function(d) { return (new Date(d)).getTime() / 1000 - submitTime + 's'; }
- }
- });
- xAxis.render();
-
- var yAxis = new Rickshaw.Graph.Axis.Y({
- orientation: 'left',
- element: document.querySelector('#y-axis'),
- graph:graph
- });
- yAxis.render();
-
- var hoverDetail = new Rickshaw.Graph.HoverDetail({
- graph:graph,
- yFormatter:function (y) {
- return Math.floor(y) + " tasks"
- }
- });
-
- /*var annotator = new Rickshaw.Graph.Annotate({
- graph:graph,
- //element:document.getElementById(timeline_id)
- });*/
+ addSeries:function(svgg,series,color,xscale,yscale,margin,startTime,dotInfo) {
+ if (series.length==0) return;
+ var self = this;
+ var g = svgg.append("svg:g").selectAll("g")
+ .data(series)
+ .enter().append("svg:g")
+ .attr("transform", "translate(0,"+margin+")");
+ g.append("svg:circle")
+ .attr("r",function(d) {return d.r;})
+ .attr("cx",function(d) {return xscale(d.x);})
+ .attr("cy",function(d) {return yscale(d.y);})
+ .style("fill",color)
+ .style("fill-opacity",0.8)
+ .style("stroke",d3.interpolateRgb(color, 'black')(0.125))
+ .append("title")
+ .text(function(d) { return dotInfo[Math.round(xscale(d.x))][Math.round(yscale(d.y))]; });
+ g.append("svg:line")
+ .attr("x1", function(d) { return xscale(d.x)+d.r; } )
+ .attr("x2", function(d) { return xscale(d.x+d.y); } )
+ .attr("y1", function(d) { return yscale(d.y); } )
+ .attr("y2", function(d) { return yscale(d.y); } )
+ .style("stroke",d3.interpolateRgb(color, 'black')(0.125))
+ .style("stroke-width",2)
+ .append("title")
+ .text(function(d) { return dotInfo[Math.round(xscale(d.x))][Math.round(yscale(d.y))]; });
},
/**
*
@@ -214,125 +82,103 @@ module.exports = {
* @param mapRackLocal
* @param mapOffSwitch
* @param reduceOffSwitch
- * @param submitTime
+ * @param startTime
+ * @param endTime
* @param w
* @param h
* @param element
- * @param legend_id
- * @param timeline_id
*/
- drawJobTasks:function (mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch, submitTime, w, h, element, legend_id, timeline_id) {
- mapNodeLocal = $.parseJSON(mapNodeLocal);
- mapRackLocal = $.parseJSON(mapRackLocal);
- mapOffSwitch = $.parseJSON(mapOffSwitch);
- reduceOffSwitch = $.parseJSON(reduceOffSwitch);
- if (!mapNodeLocal || !mapRackLocal || !mapOffSwitch || !reduceOffSwitch) {
- console.warn('drawJobTasks');
- return;
- }
- this.uniformSeries(mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch);
- var newC = this.getNewCircle(mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch);
- var ext = this.getExtInAllSeries([mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch]);
- var maxX = ext.maxX; // Used on X-axis time stamps
- mapNodeLocal.push(newC);
- mapRackLocal.push(newC);
- mapOffSwitch.push(newC);
- reduceOffSwitch.push(newC);
- var graph = new Rickshaw.Graph({
- width:w,
- height:h,
- element:document.querySelector(element),
- renderer:'scatterplot',
- stroke:true,
- series:[
- {
- data:mapNodeLocal,
- color:'green',
- name:'node_local_map'
- },
- {
- data:mapRackLocal,
- color:'#66B366',
- name:'rack_local_map'
- },
- {
- data:mapOffSwitch,
- color:'brown',
- name:'off_switch_map'
- },
- {
- data:reduceOffSwitch,
- color:'steelblue',
- name:'reduce'
- }
- ]
- });
- graph.render();
- var legend = new Rickshaw.Graph.Legend({
- graph:graph,
- element:document.getElementById(legend_id)
- });
-
- var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
- graph:graph,
- legend:legend
- });
-
- var order = new Rickshaw.Graph.Behavior.Series.Order({
- graph:graph,
- legend:legend
- });
-
- var highlight = new Rickshaw.Graph.Behavior.Series.Highlight({
- graph:graph,
- legend:legend
- });
-
- var ticksTreatment = 'glow';
-
- var xAxis = new Rickshaw.Graph.Axis.Time({
- graph:graph,
- timeUnit: {
- name: 'Custom',
- seconds: Math.round((maxX - submitTime) / 2),
- formatter: function(d) { return (new Date(d)).getTime() / 1000 - submitTime + 's'; }
- },
- ticksTreatment:ticksTreatment
- });
- xAxis.render();
-
- var yAxis = new Rickshaw.Graph.Axis.Y({
- graph:graph,
- ticksTreatment:ticksTreatment,
- orientation: 'left',
- element: document.querySelector('#y-axis2'),
- tickFormat: function(y) { return y / 1000 + 's' }
- });
- yAxis.render();
-
- var hoverDetail = new Rickshaw.Graph.HoverDetail({
- graph:graph,
- xFormatter:function (x) {
- return (x - submitTime) + 's'
- },
- yFormatter:function (y) {
- return y / 1000 + 's'
- },
- formatter:function (series, x, y, formattedX, formattedY, d) {
- var bytesFormatter = function(y) {
- if (y >= 1125899906842624) { return Math.floor(10 * y / 1125899906842624)/10 + " PB" }
- else if (y >= 1099511627776){ return Math.floor(10 * y / 1099511627776)/10 + " TB" }
- else if (y >= 1073741824) { return Math.floor(10 * y / 1073741824)/10 + " GB" }
- else if (y >= 1048576) { return Math.floor(10 * y / 1048576)/10 + " MB" }
- else if (y >= 1024) { return Math.floor(10 * y / 1024)/10 + " KB" }
- else { return y + " B"}
- };
- var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';
- return swatch + d.value.label +
- '<br>Run-time: ' + formattedY + '<br>Wait-time: ' + formattedX +
- '<br>I/O: ' + bytesFormatter(d.value.io) + '<br>Status: ' + d.value.status;
- }
+ drawJobTasks:function (mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch, startTime, endTime, svgw, svgh, element) {
+ var rmax = 24; // default value
+ var axisHeight = 24;
+ var margin = {"vertical":10, "horizontal":50};
+ var w = svgw - 2*margin.horizontal;
+ var h = svgh - 2*margin.vertical;
+ var x = d3.time.scale.utc()
+ .domain([startTime, endTime])
+ .range([0, w]);
+ var xrel = d3.time.scale()
+ .domain([0, endTime-startTime])
+ .range([0, w]);
+ // create axes
+ var topAxis = d3.svg.axis()
+ .scale(x)
+ .orient("bottom");
+ var self = this;
+ var bottomAxis = d3.svg.axis()
+ .scale(xrel)
+ .orient("bottom")
+ .tickFormat(function(d) {return self.durationFormatter(d.getTime())});
+
+ var svg = d3.select("div#" + element).append("svg:svg")
+ .attr("width", svgw+"px")
+ .attr("height", svgh+"px");
+ var svgg = svg.append("g")
+ .attr("transform", "translate("+margin.horizontal+","+margin.vertical+")");
+
+ svgg.append("g")
+ .attr("class", "x axis top")
+ .call(topAxis);
+ svgg.append("g")
+ .attr("class", "x axis bottom")
+ .call(bottomAxis)
+ .attr("transform", "translate(0,"+(h-axisHeight)+")");
+
+ var ymax = 0;
+ if (mapNodeLocal.length > 0)
+ ymax = Math.max(ymax, d3.max(mapNodeLocal, function(d) { return d.y; } ));
+ if (mapRackLocal.length > 0)
+ ymax = Math.max(ymax, d3.max(mapRackLocal, function(d) { return d.y; } ));
+ if (mapOffSwitch.length > 0)
+ ymax = Math.max(ymax, d3.max(mapOffSwitch, function(d) { return d.y; } ));
+ if (reduceOffSwitch.length > 0)
+ ymax = Math.max(ymax, d3.max(reduceOffSwitch, function(d) { return d.y; } ));
+
+ var y = d3.scale.linear()
+ .domain([0, ymax])
+ .range([h-2*axisHeight-rmax, 0]);
+
+ var yAxis = d3.svg.axis()
+ .scale(y)
+ .orient("left")
+ .tickFormat(self.durationFormatter);
+
+ svgg.append("svg:g")
+ .attr("class", "y axis")
+ .call(yAxis)
+ .attr("transform", "translate(0,"+(axisHeight+rmax)+")")
+ .append("text")
+ .attr("transform", "rotate(-90)")
+ .attr("x", -(h-2*axisHeight-rmax)/2)
+ .attr("y", -margin.horizontal + 11)
+ .attr("class", "axislabel")
+ .text("Task Attempt Duration");
+
+
+ var dotInfo = new Array();
+ var mapDotInfo = function(d) {
+ var thisx = Math.round(x(d.x));
+ var thisy = Math.round(y(d.y));
+ if (!(thisx in dotInfo))
+ dotInfo[thisx] = new Array();
+ var existing = dotInfo[thisx][thisy];
+ var newInfo = d.label + " \n" +
+ 'Run-time: ' + self.durationFormatter(d.y) + ' \nWait-time: ' + self.durationFormatter(d.x-startTime) +
+ ' \nI/O: ' + self.bytesFormatter(d.io) + ' \nStatus: ' + d.status;
+ if (existing)
+ dotInfo[thisx][thisy] = existing + " \n" + newInfo;
+ else
+ dotInfo[thisx][thisy] = newInfo;
+ };
- });
+ mapNodeLocal.forEach(mapDotInfo);
+ mapRackLocal.forEach(mapDotInfo);
+ mapOffSwitch.forEach(mapDotInfo);
+ reduceOffSwitch.forEach(mapDotInfo);
+
+ this.addSeries(svgg, mapNodeLocal, "green", x, y, axisHeight+rmax, startTime, dotInfo);
+ this.addSeries(svgg, mapRackLocal,'#66B366', x, y, axisHeight+rmax, startTime, dotInfo);
+ this.addSeries(svgg, mapOffSwitch, 'brown', x, y, axisHeight+rmax, startTime, dotInfo);
+ this.addSeries(svgg, reduceOffSwitch, 'steelblue', x, y, axisHeight+rmax, startTime, dotInfo);
}
}
Modified: incubator/ambari/trunk/ambari-web/app/views/main/apps/item/bar_view.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/views/main/apps/item/bar_view.js?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/views/main/apps/item/bar_view.js (original)
+++ incubator/ambari/trunk/ambari-web/app/views/main/apps/item/bar_view.js Fri Mar 8 00:30:36 2013
@@ -22,59 +22,24 @@ var graph = require('utils/graph');
App.MainAppsItemBarView = Em.View.extend({
elementId:'bars',
templateName:require('templates/main/apps/item/bar'),
- width:300,
- height:210,
- /**
- * Jobs list. Sorted by job id
- */
content:function () {
- return this.get('controller.content.jobs').sort(function(a, b) {
- var jobIdA = a.get('id').toLowerCase(), jobIdB = b.get('id').toLowerCase();
- if (jobIdA < jobIdB)
- return -1;
- if (jobIdA > jobIdB)
- return 1;
- return 0;
- });
+ return this.get('controller.content.jobs');
}.property('controller.content.jobs'),
- firstJob:function () {
- return this.get('content').get('firstObject');
- }.property('content'),
- activeJob:null,
- selectJob:function (event) {
- this.set('activeJob', event.context);
-
- },
onLoad:function () {
if (!this.get('controller.content.loadAllJobs') || this.get('activeJob')) {
return;
}
- this.set('activeJob', this.get('firstJob'));
+ var self = this;
+ Ember.run.next(function(){
+ self.updateTasksView();
+ });
}.observes('controller.content.loadAllJobs'),
didInsertElement:function () {
this.onLoad();
},
draw:function () {
- var self = this;
- if (!this.get('activeJob')) {
- return;//when job is not defined
- }
-
- var desc1 = $('#graph1_desc');
- var desc2 = $('#graph2_desc');
- $('.rickshaw_graph, .rickshaw_legend, .rickshaw_annotation_timeline').html('');
- if (null == desc1.html() || null == desc2.html()) return;
- desc1.css('display', 'block');
- desc2.css('display', 'block');
-
- this.propertyDidChange('getChartData');
-
- }.observes('activeJob'),
-
- map:false,
- shuffle:false,
- reduce:false,
+ },
mapNodeLocal:false,
mapRackLocal:false,
@@ -83,46 +48,21 @@ App.MainAppsItemBarView = Em.View.extend
submit:false,
finish:false,
- updateTimeLine:function () {
- var url = App.testMode ? '/data/apps/jobs/timeline.json' : App.apiPrefix + "/jobhistory/task?jobId=" + this.get('activeJob').get('id') +
- "&width=" + this.get('width');
- var mapper = App.jobTimeLineMapper;
- mapper.set('model', this);
- App.HttpClient.get(url, mapper,{
- complete:function(jqXHR, textStatus) {
- console.log("updateTimeLine");
- }
- });
- }.observes('getChartData'),
-
updateTasksView:function () {
- var url = App.testMode ? '/data/apps/jobs/taskview.json' : App.apiPrefix + "/jobhistory/tasklocality?jobId=" + this.get('activeJob').get('id');
+ var url = App.testMode ? '/data/apps/jobs/taskview.json' : App.apiPrefix + "/jobhistory/tasklocality?workflowId=" + this.get('controller.content.id');
var mapper = App.jobTasksMapper;
mapper.set('model', this);
+ var self = this;
App.HttpClient.get(url, mapper,{
complete:function(jqXHR, textStatus) {
- console.log("updateTasksView");
+ self.set('loadJobTasks', true);
}
});
- }.observes('getChartData'),
-
- drawJobTimeline:function () {
- var map = JSON.stringify(this.get('map'));
- var shuffle = JSON.stringify(this.get('shuffle'));
- var reduce = JSON.stringify(this.get('reduce'));
- if (!this.get('map') || !this.get('shuffle') || !this.get('reduce')) {return;}
- $('#chart, #legend, #timeline1, #y-axis').html('');
- graph.drawJobTimeLine(map, shuffle, reduce, this.get('width'), this.get('height'), '#chart', 'legend', 'timeline1');
- }.observes('map', 'shuffle', 'reduce'),
+ },
drawJobTasks:function () {
- var mapNodeLocal = JSON.stringify(this.get('mapNodeLocal'));
- var mapRackLocal = JSON.stringify(this.get('mapRackLocal'));
- var mapOffSwitch = JSON.stringify(this.get('mapOffSwitch'));
- var reduceOffSwitch = JSON.stringify(this.get('reduceOffSwitch'));
if (!this.get('mapNodeLocal') || !this.get('mapRackLocal') || !this.get('mapOffSwitch') || !this.get('reduceOffSwitch')) {return;}
- $('#job_tasks, #tasks_legend, #timeline2, #y-axis2').html('');
- graph.drawJobTasks(mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch, this.get('submit'), this.get('width'), this.get('height'), '#job_tasks', 'tasks_legend', 'timeline2');
- }.observes('mapNodeLocal', 'mapRackLocal', 'mapOffSwitch', 'reduceOffSwitch', 'submit')
+ graph.drawJobTasks(this.get('mapNodeLocal'), this.get('mapRackLocal'), this.get('mapOffSwitch'), this.get('reduceOffSwitch'), this.get('controller.content.startTime'), this.get('controller.content.startTime')+this.get('controller.content.elapsedTime'), this.$().width(), 300, 'job_tasks');
+ }.observes('loadJobTasks')
});
Modified: incubator/ambari/trunk/ambari-web/app/views/main/apps/item/dag_view.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/views/main/apps/item/dag_view.js?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/views/main/apps/item/dag_view.js (original)
+++ incubator/ambari/trunk/ambari-web/app/views/main/apps/item/dag_view.js Fri Mar 8 00:30:36 2013
@@ -23,12 +23,7 @@ App.MainAppsItemDagView = Em.View.extend
templateName: require('templates/main/apps/item/dag'),
elementId : 'jobs',
content:function(){
- //if(this.get("controller.jobsLoaded") == true)
- // {
- return this.get('controller.content.jobs');
- // }
- return this.get('controller.content.jobs');
- // }
+ return this.get('controller.content.jobs');
}.property('controller.content.jobs'),
classNames:['table','dataTable'],
@@ -42,8 +37,7 @@ App.MainAppsItemDagView = Em.View.extend
result[index] = new Object({
'name' : item.get('id'),
'entityName' : item.get('workflow_entity_name'),
- 'status' : item.get('status') == 'SUCCESS',
- 'info' : [],
+ 'status' : item.get('status'),
'input' : item.get('input'),
'output' : item.get('output'),
'submitTime' : item.get('submit_time'),
@@ -70,6 +64,7 @@ App.MainAppsItemDagView = Em.View.extend
Ember.run.next(function(){
self.draw();
+ self.updateTimeline();
});
}.observes('controller.content.loadAllJobs'),
@@ -101,11 +96,55 @@ App.MainAppsItemDagView = Em.View.extend
this.onLoad();
},
+ loadTaskTimeline:false,
+ loadJobTimeline:false,
+ map:false,
+ shuffle:false,
+ reduce:false,
+ allmap:false,
+ allshuffle:false,
+ allreduce:false,
+
+ updateTimeline:function () {
+ var url = App.testMode ? '/data/apps/jobs/timeline.json' : App.apiPrefix + "/jobhistory/task?workflowId=" + this.get('controller.content.id') + "&width=" + this.$().width() + "&startTime=" + this.get('controller.content.startTime') + "&endTime=" + (this.get('controller.content.startTime')+this.get('controller.content.elapsedTime'));
+ var mapper = App.jobTimeLineMapper;
+ mapper.set('model', this);
+ var self = this;
+ App.HttpClient.get(url, mapper,{
+ complete:function(jqXHR, textStatus) {
+ self.set('loadJobTimeline', true);
+ }
+ });
+ url = App.testMode ? '/data/apps/jobs/timeline.json' : App.apiPrefix + "/jobhistory/task?width=" + this.$().width() + "&startTime=" + this.get('controller.content.startTime') + "&endTime=" + (this.get('controller.content.startTime')+this.get('controller.content.elapsedTime'));
+ var mapper = App.taskTimeLineMapper;
+ mapper.set('model', this);
+ var self = this;
+ App.HttpClient.get(url, mapper,{
+ complete:function(jqXHR, textStatus) {
+ self.set('loadTaskTimeline', true);
+ }
+ });
+ },
+
+ drawJobTimeline:function () {
+ if (this.get('loadJobTimeline') && this.get('loadTaskTimeline')) {
+ this.daggraph.addTimeSeries([{"name":"allmap","color":"green","values":this.get('allmap')},
+ {"name":"map","color":"green","values":this.get('map')}], 0, "Maps");
+ this.daggraph.addTimeSeries([
+ {"name":"allshuffle","color":"lightblue","values":this.get('allshuffle')},
+ {"name":"allreduce","color":"steelblue","values":this.get("allreduce")},
+ {"name":"shuffle","color":"lightblue","values":this.get('shuffle')},
+ {"name":"reduce","color":"steelblue","values":this.get("reduce")}], 1, "Reduces");
+ }
+ }.observes('loadJobTimeline', 'loadTaskTimeline'),
+
+ daggraph:false,
+
draw: function(){
var dagSchema = this.get('controller.content.workflowContext');
var jobs = this.get('jobs');
this.resizeModal();
- var graph = new DagViewer('dag_viewer')
+ this.daggraph = new DagViewer('dag_viewer')
.setData(dagSchema, jobs)
.drawDag(this.$().width(), 300, 20);
},
Modified: incubator/ambari/trunk/ambari-web/vendor/scripts/workflow_visualization.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/vendor/scripts/workflow_visualization.js?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/vendor/scripts/workflow_visualization.js (original)
+++ incubator/ambari/trunk/ambari-web/vendor/scripts/workflow_visualization.js Fri Mar 8 00:30:36 2013
@@ -11,6 +11,7 @@ function DagViewer(domId) {
this._links = new Array();
this._numNodes = 0;
this._id = domId;
+ this._SUCCESS = "SUCCESS";
}
// set workflow schema and job data
@@ -24,6 +25,13 @@ DagViewer.prototype.setData = function (
var maxFinishTime = 0;
// iterate through job data
for (var i = 0; i < jobData.length; i++) {
+ jobData[i].info = "jobId:"+jobData[i].name+" \n"+
+ "nodeName:"+jobData[i].entityName+" \n"+
+ "status:"+jobData[i].status+" \n"+
+ "input:"+jobData[i].input+" \n"+
+ "output:"+jobData[i].output+" \n"+
+ "startTime:"+(new Date(jobData[i].submitTime).toString())+" \n"+
+ "duration:"+DagViewer.formatDuration(jobData[i].elapsedTime);
minStartTime = Math.min(minStartTime, jobData[i].submitTime);
maxFinishTime = Math.max(maxFinishTime, jobData[i].submitTime + jobData[i].elapsedTime);
this._addNode(existingNodes, jobData[i].entityName, jobData[i]);
@@ -71,7 +79,7 @@ DagViewer.prototype._addLink = function
}
// add link between nodes
var status = false;
- if (sourceNode.status && targetNode.status)
+ if (sourceNode.status==this._SUCCESS && targetNode.status==this._SUCCESS)
status = true;
this._links.push({"source":sourceNode, "target":targetNode, "status":status, "value":sourceNode.output});
// add source to map of targets to sources
@@ -85,21 +93,27 @@ DagViewer.prototype._addLink = function
// nodeHeight = 15, labelFontSize = 10, maxLabelWidth = 120
// nodeHeight = 40, labelFontSize = 20, maxLabelWidth = 260
// nodeHeight = 30, labelFontSize = 16
-DagViewer.prototype.drawDag = function (svgw, svgh, nodeHeight, labelFontSize, maxLabelWidth, axisPadding) {
- this._addTimelineGraph(svgw, svgh, nodeHeight || 20, labelFontSize || 14, maxLabelWidth || 180, axisPadding || 30);
+DagViewer.prototype.drawDag = function (svgw, svgh, nodeHeight, labelFontSize, maxLabelWidth, axisPadding, numExtraSeries, extraSeriesSize) {
+ this._addTimelineGraph(svgw, svgh, nodeHeight || 20, labelFontSize || 14, maxLabelWidth || 180, axisPadding || 30, numExtraSeries || 2, extraSeriesSize || 50);
return this;
}
// draw timeline graph
-DagViewer.prototype._addTimelineGraph = function (svgw, svgh, nodeHeight, labelFontSize, maxLabelWidth, axisPadding) {
+DagViewer.prototype._addTimelineGraph = function (svgw, svgh, nodeHeight, labelFontSize, maxLabelWidth, axisPadding, numExtraSeries, extraSeriesSize) {
svgw = svgw;
-
- var margin = {"top":10, "bottom":10, "left":30, "right":30};
- var w = svgw - margin.left - margin.right;
+ this._extraSeriesSize = extraSeriesSize;
+
+ var margin = {"vertical":10, "horizontal":50};
+ this._margin = margin;
+ var w = svgw - 2*margin.horizontal;
var startTime = this._minStartTime;
var elapsedTime = this._maxFinishTime - this._minStartTime;
- var x = d3.time.scale()
+ var x = d3.time.scale.utc()
+ .domain([startTime, startTime+elapsedTime])
+ .range([0, w]);
+ this._x = x;
+ var xrel = d3.time.scale()
.domain([0, elapsedTime])
.range([0, w]);
@@ -110,8 +124,8 @@ DagViewer.prototype._addTimelineGraph =
this._nodes = this._nodes.sort(function(a,b){return a.name.localeCompare(b.name);});
for (var i = 0; i < this._numNodes; i++) {
var d = this._nodes[i];
- d.x = x(d.submitTime-startTime);
- d.w = x(d.elapsedTime+d.submitTime-startTime) - x(d.submitTime-startTime);
+ d.x = x(d.submitTime);
+ d.w = x(d.elapsedTime+d.submitTime) - x(d.submitTime);
if (d.w < nodeHeight/2) {
d.w = nodeHeight/2;
if (d.x + d.w > w)
@@ -156,17 +170,19 @@ DagViewer.prototype._addTimelineGraph =
}
var h = 2*axisPadding + 2*nodeHeight*(maxIndex+1);
- var realh = svgh - margin.top - margin.bottom;
+ var realh = svgh - 2*margin.vertical - numExtraSeries*extraSeriesSize;
var scale = 1;
if (h > realh)
scale = realh / h;
- svgh = Math.min(svgh, h + margin.top + margin.bottom);
+ svgh = Math.min(svgh, h + 2*margin.vertical + numExtraSeries*extraSeriesSize);
+ this._extraSeriesOffset = h + margin.vertical;
var svg = d3.select("div#" + this._id).append("svg:svg")
.attr("width", svgw+"px")
.attr("height", svgh+"px");
var svgg = svg.append("g")
- .attr("transform", "translate("+margin.left+","+margin.top+") scale("+scale+")");
+ .attr("transform", "translate("+margin.horizontal+","+margin.vertical+") scale("+scale+")");
+ this._svgg = svgg;
// add an untranslated white rectangle below everything
// so mouse doesn't have to be over nodes for panning/zooming
svgg.append("svg:rect")
@@ -177,41 +193,13 @@ DagViewer.prototype._addTimelineGraph =
.attr("style", "fill:white;stroke:none");
// create axes
- var x = d3.time.scale()
- .domain([0, elapsedTime])
- .range([0, w]);
- var tickFormatter = function(x) {
- d = x.getTime();
- if (d==0) { return "0" }
- var seconds = Math.floor(parseInt(d) / 1000);
- if ( seconds < 60 )
- return seconds + "s";
- var minutes = Math.floor(seconds / 60);
- if ( minutes < 60 ) {
- var x = seconds - 60*minutes;
- return minutes + "m" + (x==0 ? "" : " " + x + "s");
- }
- var hours = Math.floor(minutes / 60);
- if ( hours < 24 ) {
- var x = minutes - 60*hours;
- return hours + "h" + (x==0 ? "" : " " + x + "m");
- }
- var days = Math.floor(hours / 24);
- if ( days < 7 ) {
- var x = hours - 24*days;
- return days + "d " + (x==0 ? "" : " " + x + "h");
- }
- var weeks = Math.floor(days / 7);
- var x = days - 7*weeks;
- return weeks + "w " + (x==0 ? "" : " " + x + "d");
- };
var topAxis = d3.svg.axis()
- .scale(d3.time.scale().domain([startTime, startTime+elapsedTime]).range([0, w]))
+ .scale(x)
.orient("bottom");
var bottomAxis = d3.svg.axis()
- .scale(x)
+ .scale(xrel)
.orient("top")
- .tickFormat(tickFormatter);
+ .tickFormat(function(x) { return DagViewer.formatDuration(x.getTime()); });
svgg.append("g")
.attr("class", "x axis top")
.call(topAxis);
@@ -221,6 +209,7 @@ DagViewer.prototype._addTimelineGraph =
.attr("transform", "translate(0,"+h+")");
// create a rectangle for each node
+ var success = this._SUCCESS;
var boxes = svgg.append("svg:g").selectAll("rect")
.data(this._nodes)
.enter().append("svg:rect")
@@ -229,11 +218,13 @@ DagViewer.prototype._addTimelineGraph =
.attr("width", function(d) { return d.w; } )
.attr("height", function(d) { return d.h; } )
.attr("class", function (d) {
- return "node " + (d.status ? " finished" : "");
+ return "node " + (d.status==success ? " finished" : "");
})
.attr("id", function (d) {
return d.name;
- });
+ })
+ .append("title")
+ .text(function(d) { return d.info; });
// defs for arrowheads marked as to whether they link finished jobs or not
svgg.append("svg:defs").selectAll("arrowmarker")
@@ -350,8 +341,76 @@ DagViewer.prototype._addTimelineGraph =
});
svg.call(d3.behavior.zoom().on("zoom", function() {
- var left = Math.min(Math.max(d3.event.translate[0]+margin.left, margin.left-w*d3.event.scale*scale), margin.left+w);
- var top = Math.min(Math.max(d3.event.translate[1]+margin.top, margin.top-h*d3.event.scale*scale), margin.top+h);
+ var left = Math.min(Math.max(d3.event.translate[0]+margin.horizontal, margin.horizontal-w*d3.event.scale*scale), margin.horizontal+w);
+ var top = Math.min(Math.max(d3.event.translate[1]+margin.vertical, margin.vertical-h*d3.event.scale*scale), margin.vertical+h);
svgg.attr("transform", "translate("+left+","+top+") scale("+(d3.event.scale*scale)+")");
}));
}
+
+DagViewer.prototype.addTimeSeries = function (series, position, name) {
+ var offset = this._extraSeriesOffset + this._extraSeriesSize*position;
+ var x = this._x;
+ var ymax = d3.max(series, function(d) {return d3.max(d.values, function(d) { return d.y;} ) } );
+ var y = d3.scale.linear()
+ .domain([0, ymax])
+ .range([this._extraSeriesSize - this._margin.vertical, 0]);
+
+ var yAxis = d3.svg.axis()
+ .scale(y)
+ .ticks(ymax < 4 ? ymax : 4)
+ .orient("left");
+
+ var line = d3.svg.line()
+ .interpolate("linear")
+ .x(function(d) { return x(d.x*1000); } )
+ .y(function(d) { return y(d.y); } );
+
+ this._svgg.append("svg:g")
+ .attr("class", "y axis")
+ .call(yAxis)
+ .attr("transform", "translate(0,"+offset+")")
+ .append("text")
+ .attr("transform", "rotate(-90)")
+ .attr("x", -(this._extraSeriesSize - this._margin.vertical)/2)
+ .attr("y", -this._margin.horizontal + 11)
+ .attr("class", "axislabel")
+ .text(name);
+
+ var lines = this._svgg.append("svg:g").selectAll("path")
+ .data(series)
+ .enter().append("svg:path")
+ .attr("d", function(d) { return line(d.values);})
+ .attr("class", function(d) { return d.name;})
+ .attr("style", function(d) {
+ if (d.name.substring(0,3)=="all")
+ return "stroke:"+d3.interpolateRgb(d.color, 'black')(0.125)+";fill:white";
+ else
+ return "stroke:"+d3.interpolateRgb(d.color, 'black')(0.125)+";fill:"+d.color;
+ })
+ .attr("transform", "translate(0,"+offset+")");
+}
+
+DagViewer.formatDuration = function(d) {
+ if (d==0) { return "0" }
+ var seconds = Math.floor(parseInt(d) / 1000);
+ if ( seconds < 60 )
+ return seconds + "s";
+ var minutes = Math.floor(seconds / 60);
+ if ( minutes < 60 ) {
+ var x = seconds - 60*minutes;
+ return minutes + "m" + (x==0 ? "" : " " + x + "s");
+ }
+ var hours = Math.floor(minutes / 60);
+ if ( hours < 24 ) {
+ var x = minutes - 60*hours;
+ return hours + "h" + (x==0 ? "" : " " + x + "m");
+ }
+ var days = Math.floor(hours / 24);
+ if ( days < 7 ) {
+ var x = hours - 24*days;
+ return days + "d " + (x==0 ? "" : " " + x + "h");
+ }
+ var weeks = Math.floor(days / 7);
+ var x = days - 7*weeks;
+ return weeks + "w " + (x==0 ? "" : " " + x + "d");
+};
Modified: incubator/ambari/trunk/ambari-web/vendor/styles/cubism.css
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/vendor/styles/cubism.css?rev=1454191&r1=1454190&r2=1454191&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/vendor/styles/cubism.css (original)
+++ incubator/ambari/trunk/ambari-web/vendor/styles/cubism.css Fri Mar 8 00:30:36 2013
@@ -47,7 +47,8 @@
}
#dag_viewer rect.finished {
- fill: #69BE28;
+ fill: green;
+ fill-opacity: 0.8;
}
#dag_viewer text.joblabel {
@@ -55,8 +56,27 @@
text-anchor: middle;
}
+#dag_viewer text.axislabel {
+ pointer-events: none;
+ text-anchor: middle;
+}
+
#dag_viewer text.shadow {
stroke: #fff;
stroke-width: 3px;
stroke-opacity: .8;
}
+
+.map, .shuffle, .reduce {
+ fill-opacity: 0.8;
+ stroke-width: 2px;
+}
+
+.allmap, .allshuffle, .allreduce {
+ fill: none;
+ stroke-width: 2px;
+}
+
+.y.axis {
+ font-size: 10px;
+}