blob: c0f54f7914db3cb232e87a1da8a6f9e804655d3d [file] [log] [blame]
/*
* 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.
*/
var jsonGlobal;
var widthProgressbar = 120;
// For coloring the dependency graph
var colors = [ "#37485D", "#D9AADC", "#4F7C61", "#8F9C6A", "#BC8E88" ];
var timestamp = 0;
var recentjobs = new Array();
/*
* Initializes global job table
*/
function init() {
$.ajax({ url : "jobsInfo", type : "GET", cache: false, success : function(json) {
// If no job running, poll for new jobs
if(json == "")
setTimeout(function() {init()}, 2000);
jsonGlobal = json
// Fill Table
fillTable("#jobs", json)
}, dataType : "json",
});
}
// Init once on page load
$(init());
/*
* Pools for updates on currently running jobs
*/
function poll(jobId) {
$.ajax({ url : "jobsInfo?get=updates&job="+jobId, type : "GET", cache: false, success : function(json) {
// Call init of no more jobs are running
$.each(json.recentjobs, function(j, job) {
if(!$.inArray(job.jobid, recentjobs)) {
init();
}
});
updateTable(json);
}, dataType : "json",
});
};
/*
* Polls the job history on page load
*/
(function pollArchive() {
$.ajax({ url : "jobsInfo?get=archive", cache: false, type : "GET",
success : function(json) {
// Fill Table
fillTableArchive("#jobsArchive", json)
}, dataType : "json",
});
})();
/*
* Polls the number of taskmanagers on page load
*/
(function pollTaskmanagers() {
$.ajax({ url : "jobsInfo?get=taskmanagers", cache: false, type : "GET",
success : function(json) {
$("#stat-taskmanagers").html(json.taskmanagers);
$("#stat-slots").html(json.slots);
}, dataType : "json",
});
})();
/*
* Toggle ExecutionVectors
*/
$(document).on("click", ".opensub", function() {
var id = $(this).data("open");
$("#" + id).toggle();
drawDependencies();
});
/**
* Cancels a job
*/
$(document).on("click", ".cancel", function() {
var id = $(this).attr("job");
$.ajax({ url : "jobsInfo?get=cancel&job=" + id, cache: false, type : "GET",
success : function(json) {
}
});
});
/*
* Draw graph on left side beside table
*/
function drawDependencies() {
$.each(jsonGlobal, function(i, job) {
$("#dependencies" + job.jobid).clearCanvas();
$("#dependencies" + job.jobid).attr("height", 10);
$("#dependencies" + job.jobid).attr("height", ($("#" + job.jobid).height()-$("#sum").height()-14));
edgeCount = -1;
$.each(job.groupvertices, function(j, groupvertex) {
$.each(groupvertex.backwardEdges, function(k, edge) {
var y1 = ($("#" + edge.groupvertexid).offset().top - $("#dependencies"+ job.jobid).offset().top) + 15;
var y2 = ($("#" + groupvertex.groupvertexid).offset().top - $("#dependencies" + job.jobid).offset().top) + 15;
var cy1 = y1 + (y2 - y1) / 2;
var cx1 = 0;
edgeCount++;
var strokeWidth = 2;
if (edge.channelType == "NETWORK")
var strokeWidth = 3;
$("#dependencies" + job.jobid).drawQuadratic({
strokeStyle : colors[edgeCount % 5],
strokeWidth : strokeWidth,
rounded : true,
endArrow : true,
arrowRadius : 10,
arrowAngle : 40,
x1 : 95,
y1 : y1, // Start point
cx1 : cx1,
cy1 : cy1, // Control point
x2 : 95,
y2 : y2
// End point
});
});
});
});
}
/*
* Creates and fills the global running jobs table
*/
function fillTable(table, json) {
$(table).html("");
$.each(json, function(i, job) {
$("#rJpH").hide();
var countGroups = 0;
var countTasks = 0;
var countStarting = 0;
var countRunning = 0;
var countFinished = 0;
var countCanceled = 0;
var countFailed = 0;
recentjobs.push(job.jobid);
poll(job.jobid);
if(parseInt(job.time) > timestamp)
timestamp = parseInt(job.time);
$(table).append(
"<h2 id=\""+job.jobid+"_title\">"+ job.jobname
+ " ("+ formattedTimeFromTimestamp(job.time) + ")"
+"</h2>"
+"<a id=\""+job.jobid+"_cancel\" class=\"cancel btn btn-warning\" href=\"#\" job=\""+job.jobid+"\" style=\"margin-bottom: 0.5cm\">cancel</a><br />");
var jobtable;
jobtable = "<div class=\"table-responsive\">";
jobtable += "<table class=\"table table-bordered table-hover table-striped\" id=\""+job.jobid+"\" jobname=\""+job.jobname+"\">\
<tr>\
<th>Name</th>\
<th>Tasks</th>\
<th>Starting</th>\
<th>Running</th>\
<th>Finished</th>\
<th>Canceled</th>\
<th>Failed</th>\
</tr>";
$.each(job.groupvertices, function(j, groupvertex) {
countGroups++;
countTasks += groupvertex.numberofgroupmembers;
starting = (groupvertex.CREATED + groupvertex.SCHEDULED + groupvertex.DEPLOYING);
countStarting += starting;
countRunning += groupvertex.RUNNING;
countFinished += groupvertex.FINISHED;
countCanceled += groupvertex.CANCELING + groupvertex.CANCELED;
countFailed += groupvertex.FAILED;
jobtable += "<tr>\
<td id=\""+groupvertex.groupvertexid+"\">\
<span class=\"opensub\" data-open=\"_"+groupvertex.groupvertexid+"\">"
+ groupvertex.groupvertexname
+ "</span>\
</td>\
<td class=\"nummembers\">"+ groupvertex.numberofgroupmembers+ "</td>";
jobtable += progressBar(groupvertex.numberofgroupmembers, starting, 'starting');
jobtable += progressBar(groupvertex.numberofgroupmembers, groupvertex.RUNNING, 'running');
jobtable += progressBar(groupvertex.numberofgroupmembers, (groupvertex.FINISHED), 'success finished');
jobtable += progressBar(groupvertex.numberofgroupmembers, (groupvertex.CANCELING + groupvertex.CANCELED), 'warning canceled');
jobtable += progressBar(groupvertex.numberofgroupmembers, groupvertex.FAILED, 'danger failed');
jobtable += "</tr><tr>\
<td colspan=8 id=\"_"+groupvertex.groupvertexid+"\" style=\"display:none\">\
<div class =\"table-responsive\">\
<table class=\"table table-bordered table-hover table-striped tablesorter\">\
<tr>\
<th>Name</th>\
<th>status</th>\
<th>instancename</th>\
<th>instancetype</th>\
</tr>";
$.each(groupvertex.groupmembers, function(k, vertex) {
jobtable += "<tr id="+vertex.vertexid+" lastupdate=\""+job.time+"\">\
<td>"+ vertex.vertexname + "</td>\
<td class=\"status\">"+ vertex.vertexstatus + "</td>\
<td>"+ vertex.vertexinstancename + "</td>\
<td>"+ vertex.vertexinstancetype + "</td>\
</tr>";
});
jobtable += "</table></div>\
</td></tr>";
});
jobtable += "<tr id=\"sum\">\
<td colspan=\"2\" align=\"center\">Sum</td>\
<td class=\"nummebembers\">"+ countTasks + "</td>";
jobtable += progressBar(countTasks, countStarting, 'starting');
jobtable += progressBar(countTasks, countRunning, 'running');
jobtable += progressBar(countTasks, countFinished, 'success finished');
jobtable += progressBar(countTasks, countCanceled, 'warning canceled');
jobtable += progressBar(countTasks, countFailed, 'danger failed');
jobtable += "</tr>";
jobtable += "</table></div>"
$(table).append(jobtable);
$("#" + job.jobid).prepend(
"<tr><td width=\"100\" rowspan=" + (countGroups * 2 + 2)+ " style=\"overflow:hidden\">\
<canvas id=\"dependencies" + job.jobid+ "\" height=\"10\" width=\"100\"></canvas>\
</td></tr>");
});
drawDependencies(json);
}
/*
* Generates the progress bars
*/
function progressBar(maximum, input, classVal) {
if (input != 0) {
return "<td class=\""+classVal+"\" val=\""+input+"\"><div class=\"progress\"><div class=\"progress-bar progress-bar-success\" role=\"progressbar\""
+ "aria-valuemin=\"0\" aria-valuemax=\""+widthProgressbar+"\" style=\"width:"
+ (20 + 80*input/maximum) + "%;\">"
+ input
+ "</div></div></td>";
} else {
return "<td class=\""+classVal+"\" val=\""+input+"\">"+input+"</td>";
}
}
/*
* Generates the progress bar without the class val
*/
function progressBar2(maximum, input) {
if (input != 0) {
return "<div class=\"progress\"><div class=\"progress-bar progress-bar-success\" role=\"progressbar\""
+ "aria-valuemin=\"0\" aria-valuemax=\""+widthProgressbar+"\" style=\"width:"
+ (20 + 80*input/maximum) + "%;\">"
+ input
+ "</div></div>";
}
}
/*
* Returns the new width for a progress bar
*/
function newWidth(maximum, val) {
return (20 + 80*val/maximum);
}
/*
* Updates the global running job table with newest events
*/
function updateTable(json) {
var pollfinished = false;
$.each(json.vertexevents , function(i, event) {
if(parseInt($("#"+event.vertexid).attr("lastupdate")) < event.timestamp)
{
// not very nice
var oldstatus = ""+$("#"+event.vertexid).children(".status").html();
if (oldstatus == "CREATED" || oldstatus == "SCHEDULED" || oldstatus == "DEPLOYING")
oldstatus = "starting";
else if(oldstatus == "CANCELING")
oldstatus = "canceled";
var newstate = event.newstate;
if(newstate == "CREATED" || newstate == "SCHEDULED" || newstate == "DEPLOYING")
newstate = "starting";
else if(newstate == "CANCELING")
newstate = "canceled";
// update detailed state
$("#"+event.vertexid).children(".status").html(event.newstate);
// update timestamp
$("#"+event.vertexid).attr("lastupdate", event.timestamp);
var nummembers = parseInt($("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children(".nummembers").html());
var summembers = parseInt($("#sum").children(".nummebembers").html());
if(oldstatus.toLowerCase() != newstate.toLowerCase()) {
// adjust groupvertex
var oldcount = parseInt($("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+oldstatus.toLowerCase()).attr("val"));
var oldcount2 = parseInt($("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+newstate.toLowerCase()).attr("val"));
$("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+oldstatus.toLowerCase()).attr("val", oldcount-1);
$("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+newstate.toLowerCase()).attr("val", oldcount2+1);
//adjust progressbars nummembers
if (oldcount == 1) {
$("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+oldstatus.toLowerCase()).children().first().remove();
$("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+oldstatus.toLowerCase()).html("0");
} else if (oldcount > 1) {
$("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+oldstatus.toLowerCase()).first().children().first().children().first().css("width", newWidth(nummembers, (oldcount-1))+"%").html(oldcount-1);
}
if (oldcount2 == 0) {
$("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+newstate.toLowerCase()).html(progressBar2(nummembers, 1, newstate.toLowerCase()));
} else if (oldcount2 > 0) {
$("#"+event.vertexid).parent().parent().parent().parent().parent().prev().children("."+newstate.toLowerCase()).first().children().first().children().first().css("width", newWidth(nummembers, (oldcount2+1))+"%").html(oldcount2+1);
}
// adjust sum
oldcount = parseInt($("#sum").children("."+oldstatus.toLowerCase()).attr("val"));
oldcount2 = parseInt($("#sum").children("."+newstate.toLowerCase()).attr("val"));
$("#sum").children("."+oldstatus.toLowerCase()).attr("val", oldcount-1);
$("#sum").children("."+newstate.toLowerCase()).attr("val", oldcount2+1);
//adjust progressbars summembers
if (oldcount == 1) {
$("#sum").children("."+oldstatus.toLowerCase()).children().first().remove();
$("#sum").children("."+oldstatus.toLowerCase()).html("0");
} else if (oldcount > 1) {
$("#sum").children("."+oldstatus.toLowerCase()).first().children().first().children().first().css("width", newWidth(summembers, (oldcount-1))+"%").html(oldcount-1);
}
if (oldcount2 == 0) {
$("#sum").children("."+newstate.toLowerCase()).first().html(progressBar2(summembers, 1, newstate.toLowerCase()));
} else if (oldcount2 > 0) {
$("#sum").children("."+newstate.toLowerCase()).first().children().first().children().first().css("width", newWidth(summembers, (oldcount2+1))+"%").html(oldcount2+1);
}
}
}
});
// handle jobevents
$.each(json.jobevents , function(i, event) {
console.log(event.newstate);
if(event.newstate == "FINISHED" || event.newstate == "FAILED" || event.newstate == "CANCELED") {
// stop polling
pollfinished = true;
// delete table
var jobname = $("#"+json.jobid).attr("jobname");
$("#"+json.jobid).remove();
$("#"+json.jobid+"_title").remove();
$("#"+json.jobid+"_cancel").remove();
// remove from internal list
for(var i in recentjobs){
if(recentjobs[i]==json.jobid){
recentjobs.splice(i,1);
break;
}
}
//display message if all jobs are done
if(recentjobs.length == 0) {
$("#rJpH").show();
}
// add to history
setTimeout(function() {
var jobjson = {};
jobjson.jobid = json.jobid;
jobjson.jobname = jobname;
jobjson.time = event.timestamp;
jobjson.status = event.newstate;
_fillTableArchive("#jobsArchive", jobjson, true);
}, 8000);
}
});
if(!pollfinished)
setTimeout(function() {poll(json.jobid)}, 2000);
else if(recentjobs.length == 0) {
// wait long enough for job to be completely removed on server side before new init
setTimeout(init, 5000);
}
}
var archive_finished = 0;
var archive_failed = 0;
var archive_canceled = 0;
/*
* Creates job history table
*/
function fillTableArchive(table, json) {
$(table).html("");
$("#jobs-finished").html(archive_finished);
$("#jobs-failed").html(archive_failed);
$("#jobs-canceled").html(archive_canceled);
$.each(json, function(i, job) {
_fillTableArchive(table, job, false)
});
}
/*
* Adds one row to job history table
*/
function _fillTableArchive(table, job, prepend) {
// no duplicates
if($("#"+job.jobid+"_archive").length > 0) {
return false;
}
if(prepend)
$(table).prepend(
"<li id=\""+job.jobid+"_archive\"><a href=\"analyze.html?job=" + job.jobid + "\">"
+ job.jobname + " ("
+ formattedTimeFromTimestamp(parseInt(job.time))
+ ")</a></li>");
else
$(table).append(
"<li><a href=\"analyze.html?job=" + job.jobid + "\">"
+ job.jobname + " ("
+ formattedTimeFromTimestamp(parseInt(job.time))
+ ")</a></li>");
if (job.status == "FINISHED")
archive_finished++;
if (job.status == "FAILED")
archive_failed++;
if (job.status == "CANCELED")
archive_canceled++;
$("#jobs-finished").html(archive_finished);
$("#jobs-failed").html(archive_failed);
$("#jobs-canceled").html(archive_canceled);
}