azkaban-developers

Added flow view page.

1/16/2014 3:53:56 AM

Details

diff --git a/src/java/azkaban/execapp/FlowRunner.java b/src/java/azkaban/execapp/FlowRunner.java
index 7021c01..5f38882 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 c8bd33f..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}";
diff --git a/src/java/azkaban/webapp/servlet/velocity/svgflowincludes.vm b/src/java/azkaban/webapp/servlet/velocity/svgflowincludes.vm
index 5a26041..773416e 100644
--- a/src/java/azkaban/webapp/servlet/velocity/svgflowincludes.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/svgflowincludes.vm
@@ -35,4 +35,4 @@
 	<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 10(+5 -5)

diff --git a/src/less/flow.less b/src/less/flow.less
index c970d66..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 {
@@ -71,10 +75,6 @@ td {
 		font-size: 8pt;
 	}
 
-	&.subflowrow {
-		height: 20px;
-	}
-
   .status {
     -moz-border-radius: 2px;
     border-radius: 2px;
@@ -115,7 +115,7 @@ td {
     }
 
     &.KILLED {
-      background-color: #d9534f;
+      background-color: #ff9999;
     }
   }
 }

src/less/tables.less 65(+33 -32)

diff --git a/src/less/tables.less b/src/less/tables.less
index 75c3186..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,42 +108,24 @@ table.table-properties {
     &.logs {
       width: 30px;
     }
-  }
-
-  colgroup {
-  	&.name {
-  	}
-  	
-  	&.details {
-  		width: 10px;
-  	}
-  	
-  	&.status {
-  		width: 100px;
-  	}
-  	
-  	&.startTime {
+    
+     &.timeline {
+      width: 280px;
+      padding: 0px;
+      height: 100%;
+      vertical-align: bottom;
+      margin: 0px;
+    }
+    
+    &.startTime {
   		width: 160px;
   	}
   	
   	&.endTime {
   		width: 160px;
   	}
-  	
-  	&.elapsedTime {
-  		width: 160px;
+    &.elapsedTime {
+  		width: 90px;
   	}
-  	
-    &.timeline {
-      width: 280px;
-      padding: 0px;
-      height: 100%;
-      vertical-align: bottom;
-      margin: 0px;
-    }
-
-    &.execId {
-      font-weight: bold;
-    }
   }
 }
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 {
diff --git a/src/web/js/azkaban/view/flow-execution-list.js b/src/web/js/azkaban/view/flow-execution-list.js
index bf4428e..69621a3 100644
--- a/src/web/js/azkaban/view/flow-execution-list.js
+++ b/src/web/js/azkaban/view/flow-execution-list.js
@@ -7,6 +7,10 @@ azkaban.ExecutionListView = Backbone.View.extend({
 	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) {
@@ -14,7 +18,10 @@ azkaban.ExecutionListView = Backbone.View.extend({
 		var lastTime = data.endTime == -1 ? (new Date()).getTime() : data.endTime;
 		var executingBody = $("#executableBody");
 		this.updateJobRow(data.nodes, executingBody);
-		this.updateProgressBar(data);
+		
+		var flowLastTime = data.endTime == -1 ? (new Date()).getTime() : data.endTime;
+		var flowStartTime = data.startTime;
+		this.updateProgressBar(data, flowStartTime, flowLastTime);
 	},
 //
 //	handleProgressBoxClick: function(evt) {
@@ -44,7 +51,11 @@ azkaban.ExecutionListView = Backbone.View.extend({
 		if (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, body) {
 		if (!nodes) {
@@ -125,18 +136,16 @@ azkaban.ExecutionListView = Backbone.View.extend({
 			
 			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) {
@@ -197,7 +206,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
 			progressBar.attr("title", "attempt:" + progressBar.attempt + "	start:" + getHourMinSec(new Date(node.startTime)) + "	end:" + getHourMinSec(new Date(node.endTime)));
 		
 			if (node.nodes) {
-				this.updateProgressBar(node);
+				this.updateProgressBar(node, flowStartTime, flowLastTime);
 			}
 		}
 	},
@@ -211,13 +220,14 @@ azkaban.ExecutionListView = Backbone.View.extend({
 			$(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();
 		}
 	},
@@ -239,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);
@@ -248,7 +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");
@@ -256,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");
@@ -325,15 +333,6 @@ azkaban.ExecutionListView = Backbone.View.extend({
 			var subtable = document.createElement("table");
 			var parentClasses = $(body).closest("table").attr("class");
 			
-			// Seriously stupid... but okay
-			createColGroupElement(subtable, "name");
-			createColGroupElement(subtable, "timeline");
-			createColGroupElement(subtable, "startTime");
-			createColGroupElement(subtable, "endTime");
-			createColGroupElement(subtable, "elapseTime");
-			createColGroupElement(subtable, "status");
-			createColGroupElement(subtable, "details");
-			
 			$(subtable).attr("class", parentClasses);
 			$(subtable).addClass("subtable");
 			$(subFlowCell).append(subtable);
@@ -341,12 +340,6 @@ azkaban.ExecutionListView = Backbone.View.extend({
 	}
 });
 
-var createColGroupElement = function(body, className) {
-	var group = document.createElement("colgroup");
-	$(group).addClass(className);
-	$(body).append(group);
-}
-
 var attemptRightClick = function(event) {
 	var target = event.currentTarget;
 	var job = target.job;
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) {