azkaban-aplcache
Changes
src/less/flow.less 6(+5 -1)
src/less/tables.less 44(+34 -10)
src/web/js/azkaban/view/exflow.js 12(+6 -6)
src/web/js/azkaban/view/flow-execution-list.js 130(+86 -44)
src/web/js/azkaban/view/flow-job.js 20(+9 -11)
Details
diff --git a/src/java/azkaban/execapp/FlowRunner.java b/src/java/azkaban/execapp/FlowRunner.java
index 2159f4f..993d1b4 100644
--- a/src/java/azkaban/execapp/FlowRunner.java
+++ b/src/java/azkaban/execapp/FlowRunner.java
@@ -358,7 +358,6 @@ public class FlowRunner extends EventHandler implements Runnable {
return true;
}
- long currentTime = System.currentTimeMillis();
for (ExecutableNode node: jobsReadyToRun) {
Status nextStatus = getImpliedStatus(node);
@@ -368,12 +367,12 @@ public class FlowRunner extends EventHandler implements Runnable {
}
else if (nextStatus == Status.KILLED || isCancelled()) {
logger.info("Killing " + node.getId() + " due to prior errors.");
- node.killNode(currentTime);
+ node.killNode(System.currentTimeMillis());
fireEventListeners(Event.create(this, Type.JOB_FINISHED, node));
}
else if (nextStatus == Status.DISABLED) {
logger.info("Skipping disabled job " + node.getId() + ".");
- node.skipNode(currentTime);
+ node.skipNode(System.currentTimeMillis());
fireEventListeners(Event.create(this, Type.JOB_FINISHED, node));
}
else {
@@ -401,7 +400,7 @@ public class FlowRunner extends EventHandler implements Runnable {
for(String end: flow.getEndNodes()) {
ExecutableNode node = flow.getExecutableNode(end);
- if (node.getStatus() == Status.KILLED) {
+ if (node.getStatus() == Status.KILLED || node.getStatus() == Status.FAILED) {
succeeded = false;
}
diff --git a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
index 7dcee5e..d944f4c 100644
--- a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
@@ -28,7 +28,6 @@
<script type="text/javascript" src="${context}/js/flowstats.js"></script>
<script type="text/javascript" src="${context}/js/flowstats-no-data.js"></script>
- <script type="text/javascript" src="${context}/js/azkaban/view/flow-execution-list.js"></script>
<script type="text/javascript" src="${context}/js/azkaban/view/exflow.js"></script>
<script type="text/javascript">
var contextURL = "${context}";
@@ -119,7 +118,7 @@
<div class="container-full" id="jobListView">
<div class="row">
<div class="col-xs-12">
- <table class="table table-striped table-bordered table-condensed table-hover executions-table">
+ <table class="table table-bordered table-condensed table-hover executions-table">
<thead>
<tr>
<th>Name</th>
diff --git a/src/java/azkaban/webapp/servlet/velocity/svgflowincludes.vm b/src/java/azkaban/webapp/servlet/velocity/svgflowincludes.vm
index 364a968..773416e 100644
--- a/src/java/azkaban/webapp/servlet/velocity/svgflowincludes.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/svgflowincludes.vm
@@ -31,7 +31,8 @@
<script type="text/javascript" src="${context}/js/azkaban/util/date.js"></script>
<script type="text/javascript" src="${context}/js/azkaban/view/flow-stats.js"></script>
<script type="text/javascript" src="${context}/js/azkaban/view/flow-job.js"></script>
+ <script type="text/javascript" src="${context}/js/azkaban/view/flow-execution-list.js"></script>
<script type="text/javascript" src="${context}/js/azkaban/view/svg-graph.js"></script>
<script type="text/javascript" src="${context}/js/azkaban/util/svg-navigate.js"></script>
- <link rel="stylesheet" type="text/css" href="${context}/css/azkaban-graph.css" />
\ No newline at end of file
+ <link rel="stylesheet" type="text/css" href="${context}/css/azkaban-graph.css" />
\ No newline at end of file
src/less/flow.less 6(+5 -1)
diff --git a/src/less/flow.less b/src/less/flow.less
index 5b1a957..99f6be5 100644
--- a/src/less/flow.less
+++ b/src/less/flow.less
@@ -60,6 +60,10 @@
&.QUEUED {
background-color: #009fc9;
}
+
+ &.KILLED {
+ background-color: #ff9999;
+ }
}
td {
@@ -111,7 +115,7 @@ td {
}
&.KILLED {
- background-color: #d9534f;
+ background-color: #ff9999;
}
}
}
src/less/tables.less 44(+34 -10)
diff --git a/src/less/tables.less b/src/less/tables.less
index d67061b..d96815f 100644
--- a/src/less/tables.less
+++ b/src/less/tables.less
@@ -53,7 +53,26 @@ table.table-properties {
// Table of executions.
.executions-table {
- th {
+ tr {
+ &.expanded {
+ opacity: 0.6;
+ }
+ }
+
+ td {
+ &.subflowrow {
+ padding: 0px 0px;
+
+ table {
+ margin: 0px;
+ background-color: rgba(230, 230, 230, 0.75);
+
+ td {
+ background-color: none;
+ }
+ }
+ }
+
&.date {
width: 160px;
}
@@ -74,7 +93,7 @@ table.table-properties {
width: 90px;
}
- &.status {
+ &.statustd {
width: 100px;
}
@@ -89,19 +108,24 @@ table.table-properties {
&.logs {
width: 30px;
}
- }
-
- td {
- &.timeline {
+
+ &.timeline {
width: 280px;
padding: 0px;
height: 100%;
vertical-align: bottom;
margin: 0px;
}
-
- &.execId {
- font-weight: bold;
- }
+
+ &.startTime {
+ width: 160px;
+ }
+
+ &.endTime {
+ width: 160px;
+ }
+ &.elapsedTime {
+ width: 90px;
+ }
}
}
src/web/js/azkaban/view/exflow.js 12(+6 -6)
diff --git a/src/web/js/azkaban/view/exflow.js b/src/web/js/azkaban/view/exflow.js
index 1452014..eee5a1a 100644
--- a/src/web/js/azkaban/view/exflow.js
+++ b/src/web/js/azkaban/view/exflow.js
@@ -382,25 +382,24 @@ azkaban.GraphModel = Backbone.Model.extend({});
var logModel;
azkaban.LogModel = Backbone.Model.extend({});
-var updateStatus = function() {
+var updateStatus = function(updateTime) {
var requestURL = contextURL + "/executor";
var oldData = graphModel.get("data");
var nodeMap = graphModel.get("nodeMap");
- var updateTime = oldData.updateTime ? oldData.updateTime : 0;
+ if (!updateTime) {
+ updateTime = oldData.updateTime ? oldData.updateTime : 0;
+ }
+
var requestData = {
"execid": execId,
"ajax": "fetchexecflowupdate",
"lastUpdateTime": updateTime
};
-
- graphModel.set({"lastUpdateTime":updateTime})
var successHandler = function(data) {
console.log("data updated");
if (data.updateTime) {
- updateTime = data.updateTime;
-
updateGraph(oldData, data);
graphModel.set({"update": data});
@@ -451,6 +450,7 @@ var updaterFunction = function() {
}
else {
console.log("Flow finished, so no more updates");
+ setTimeout(function() {updateStatus(0);}, 500);
}
}
else {
src/web/js/azkaban/view/flow-execution-list.js 130(+86 -44)
diff --git a/src/web/js/azkaban/view/flow-execution-list.js b/src/web/js/azkaban/view/flow-execution-list.js
index 23210a5..69621a3 100644
--- a/src/web/js/azkaban/view/flow-execution-list.js
+++ b/src/web/js/azkaban/view/flow-execution-list.js
@@ -1,56 +1,67 @@
var executionListView;
azkaban.ExecutionListView = Backbone.View.extend({
events: {
- //"click .flow-progress-bar": "handleProgressBoxClick"
+ //"contextmenu .flow-progress-bar": "handleProgressBoxClick"
},
initialize: function(settings) {
this.model.bind('change:graph', this.renderJobs, this);
this.model.bind('change:update', this.updateJobs, this);
+
+ // This is for tabbing. Blah, hacky
+ var executingBody = $("#executableBody")[0];
+ executingBody.level = 0;
},
renderJobs: function(evt) {
var data = this.model.get("data");
var lastTime = data.endTime == -1 ? (new Date()).getTime() : data.endTime;
- this.updateJobRow(data.nodes);
- this.updateProgressBar(data);
- },
-
- /*handleProgressBoxClick: function(evt) {
- var target = evt.currentTarget;
- var job = target.job;
- var attempt = target.attempt;
-
- var data = this.model.get("data");
- var node = data.nodes[job];
+ var executingBody = $("#executableBody");
+ this.updateJobRow(data.nodes, executingBody);
- var jobId = event.currentTarget.jobid;
- var requestURL = contextURL + "/manager?project=" + projectName + "&execid=" + execId + "&job=" + job + "&attempt=" + attempt;
-
- var menu = [
- {title: "Open Job...", callback: function() {window.location.href=requestURL;}},
- {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
- ];
-
- contextMenuView.show(evt, menu);
- },*/
+ var flowLastTime = data.endTime == -1 ? (new Date()).getTime() : data.endTime;
+ var flowStartTime = data.startTime;
+ this.updateProgressBar(data, flowStartTime, flowLastTime);
+ },
+//
+// handleProgressBoxClick: function(evt) {
+// var target = evt.currentTarget;
+// var job = target.job;
+// var attempt = target.attempt;
+//
+// var data = this.model.get("data");
+// var node = data.nodes[job];
+//
+// var jobId = event.currentTarget.jobid;
+// var requestURL = contextURL + "/manager?project=" + projectName + "&execid=" + execId + "&job=" + job + "&attempt=" + attempt;
+//
+// var menu = [
+// {title: "Open Job...", callback: function() {window.location.href=requestURL;}},
+// {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
+// ];
+//
+// contextMenuView.show(evt, menu);
+// },
updateJobs: function(evt) {
var update = this.model.get("update");
var lastTime = update.endTime == -1 ? (new Date()).getTime() : update.endTime;
+ var executingBody = $("#executableBody");
if (update.nodes) {
- this.updateJobRow(update.nodes);
+ this.updateJobRow(update.nodes, executingBody);
}
- this.updateProgressBar(this.model.get("data"));
+
+ var data = this.model.get("data");
+ var flowLastTime = data.endTime == -1 ? (new Date()).getTime() : data.endTime;
+ var flowStartTime = data.startTime;
+ this.updateProgressBar(data, flowStartTime, flowLastTime);
},
-
- updateJobRow: function(nodes) {
+ updateJobRow: function(nodes, body) {
if (!nodes) {
return;
}
- var executingBody = $("#executableBody");
nodes.sort(function(a,b) { return a.startTime - b.startTime; });
for (var i = 0; i < nodes.length; ++i) {
@@ -62,7 +73,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
//var nodeId = node.id.replace(".", "\\\\.");
var row = node.joblistrow;
if (!row) {
- this.addNodeRow(node);
+ this.addNodeRow(node, body);
}
row = node.joblistrow;
@@ -117,21 +128,24 @@ azkaban.ExecutionListView = Backbone.View.extend({
var elapsedTime = $(row).find("> td.elapsedTime");
if (node.endTime == -1) {
- $(elapsedTime).text(getDuration(node.startTime, (new Date()).getTime()));
+ $(elapsedTime).text(getDuration(node.startTime, (new Date()).getTime()));
}
else {
$(elapsedTime).text(getDuration(node.startTime, node.endTime));
}
+
+ if (node.nodes) {
+ var subtableBody = $(row.subflowrow).find("> td > table");
+ subtableBody[0].level = $(body)[0].level + 1;
+ this.updateJobRow(node.nodes, subtableBody);
+ }
}
},
- updateProgressBar: function(data) {
+ updateProgressBar: function(data, flowStartTime, flowLastTime) {
if (data.startTime == -1) {
return;
}
-
- var flowLastTime = data.endTime == -1 ? (new Date()).getTime() : data.endTime;
- var flowStartTime = data.startTime;
var outerWidth = $(".flow-progress").css("width");
if (outerWidth) {
@@ -190,21 +204,31 @@ azkaban.ExecutionListView = Backbone.View.extend({
progressBar.css("margin-left", left)
progressBar.css("width", width);
progressBar.attr("title", "attempt:" + progressBar.attempt + " start:" + getHourMinSec(new Date(node.startTime)) + " end:" + getHourMinSec(new Date(node.endTime)));
+
+ if (node.nodes) {
+ this.updateProgressBar(node, flowStartTime, flowLastTime);
+ }
}
},
toggleExpandFlow: function(flow) {
console.log("Toggle Expand");
- var tr = flow.progressbar;
+ var tr = flow.joblistrow;
+ var subFlowRow = tr.subflowrow;
var expandIcon = $(tr).find("> td > .listExpand");
if (tr.expanded) {
tr.expanded = false;
$(expandIcon).removeClass("glyphicon-chevron-up");
$(expandIcon).addClass("glyphicon-chevron-down");
+
+ $(tr).removeClass("expanded");
+ $(subFlowRow).hide();
}
else {
tr.expanded = true;
$(expandIcon).addClass("glyphicon-chevron-up");
$(expandIcon).removeClass("glyphicon-chevron-down");
+ $(tr).addClass("expanded");
+ $(subFlowRow).show();
}
},
expandFlow: function(flow) {
@@ -213,9 +237,8 @@ azkaban.ExecutionListView = Backbone.View.extend({
///@TODO Expand.
}
},
- addNodeRow: function(node) {
+ addNodeRow: function(node, body) {
var self = this;
- var executingBody = $("#executableBody");
var tr = document.createElement("tr");
var tdName = document.createElement("td");
var tdTimeline = document.createElement("td");
@@ -226,6 +249,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
var tdDetails = document.createElement("td");
node.joblistrow = tr;
tr.node = node;
+ var padding = 15*$(body)[0].level;
$(tr).append(tdName);
$(tr).append(tdTimeline);
@@ -235,6 +259,11 @@ azkaban.ExecutionListView = Backbone.View.extend({
$(tr).append(tdStatus);
$(tr).append(tdDetails);
$(tr).addClass("jobListRow");
+
+ $(tdName).addClass("jobname");
+ if (padding) {
+ $(tdName).css("padding-left", padding);
+ }
$(tdTimeline).addClass("timeline");
$(tdStart).addClass("startTime");
$(tdEnd).addClass("endTime");
@@ -242,13 +271,6 @@ azkaban.ExecutionListView = Backbone.View.extend({
$(tdStatus).addClass("statustd");
$(tdDetails).addClass("details");
-// $(tr).attr("id", node.id + "-row");
-// $(tdTimeline).attr("id", node.id + "-timeline");
-// $(tdStart).attr("id", node.id + "-start");
-// $(tdEnd).attr("id", node.id + "-end");
-// $(tdElapse).attr("id", node.id + "-elapse");
-// $(tdStatus).attr("id", node.id + "-status");
-
var outerProgressBar = document.createElement("div");
//$(outerProgressBar).attr("id", node.id + "-outerprogressbar");
$(outerProgressBar).addClass("flow-progress");
@@ -294,7 +316,27 @@ azkaban.ExecutionListView = Backbone.View.extend({
$(a).text("Details");
$(tdDetails).append(a);
}
- executingBody.append(tr);
+
+ $(body).append(tr);
+ if (node.type=="flow") {
+ var subFlowRow = document.createElement("tr");
+ var subFlowCell = document.createElement("td");
+ $(subFlowCell).addClass("subflowrow");
+
+ var numColumn = $(tr).children("td").length;
+ $(subFlowCell).attr("colspan", numColumn);
+ tr.subflowrow = subFlowRow;
+
+ $(subFlowRow).append(subFlowCell);
+ $(body).append(subFlowRow);
+ $(subFlowRow).hide();
+ var subtable = document.createElement("table");
+ var parentClasses = $(body).closest("table").attr("class");
+
+ $(subtable).attr("class", parentClasses);
+ $(subtable).addClass("subtable");
+ $(subFlowCell).append(subtable);
+ }
}
});
src/web/js/azkaban/view/flow-job.js 20(+9 -11)
diff --git a/src/web/js/azkaban/view/flow-job.js b/src/web/js/azkaban/view/flow-job.js
index 05ed20a..e7f0875 100644
--- a/src/web/js/azkaban/view/flow-job.js
+++ b/src/web/js/azkaban/view/flow-job.js
@@ -102,25 +102,23 @@ azkaban.JobListView = Backbone.View.extend({
handleStatusUpdate: function(evt) {
var data = this.model.get("data");
- var lastUpdateTime = this.model.get("lastUpdateTime");
- if (data.nodes) {
- this.changeStatuses(data, lastUpdateTime);
- }
+ this.changeStatuses(data);
},
- changeStatuses: function(data, lastUpdateTime) {
+ changeStatuses: function(data) {
for (var i = 0; i < data.nodes.length; ++i) {
var node = data.nodes[i];
- if (lastUpdateTime <= 0 || lastUpdateTime < node.updateTime) {
- var liElement = node.listElement;
- var child = $(liElement).children("a");
+
+ // Confused? In updates, a node reference is given to the update node.
+ var liElement = node.listElement;
+ var child = $(liElement).children("a");
+ if (!$(child).hasClass(node.status)) {
$(child).removeClass(statusList.join(' '));
$(child).addClass(node.status);
$(child).attr("title", node.status + " (" + node.type + ")");
}
-
if (node.nodes) {
- this.changeStatuses(node, lastUpdateTime);
+ this.changeStatuses(node);
}
}
},
@@ -133,7 +131,7 @@ azkaban.JobListView = Backbone.View.extend({
//this.assignInitialStatus(self);
this.handleDisabledChange(self);
- this.changeStatuses(data, 0);
+ this.changeStatuses(data);
},
renderTree: function(el, data, prefix) {