| /* |
| * 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); |
| } |