azkaban-uncached

Merge pull request #73 from davidzchen/flow_summary Make

11/21/2013 8:30:49 PM

Changes

src/java/azkaban/webapp/servlet/velocity/joblogpage.vm 93(+0 -93)

src/web/js/azkaban.joblog.view.js 116(+0 -116)

src/web/js/azkaban.jobsummary.view.js 153(+0 -153)

Details

diff --git a/src/java/azkaban/webapp/servlet/ExecutorServlet.java b/src/java/azkaban/webapp/servlet/ExecutorServlet.java
index 7898864..1ba4029 100644
--- a/src/java/azkaban/webapp/servlet/ExecutorServlet.java
+++ b/src/java/azkaban/webapp/servlet/ExecutorServlet.java
@@ -71,11 +71,8 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
 			handleAJAXAction(req, resp, session);
 		}
 		else if (hasParam(req, "execid")) {
-			if (hasParam(req, "summary")) {
-				handleExecutionJobSummaryPage(req, resp, session);
-			}
-			else if (hasParam(req, "job")) {
-				handleExecutionJobLogPage(req, resp, session);
+			if (hasParam(req, "job")) {
+				handleExecutionJobDetailsPage(req, resp, session);
 			}
 			else {
 				handleExecutionFlowPage(req, resp, session);
@@ -86,45 +83,8 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
 		}
 	}
 	
-	private void handleExecutionJobSummaryPage(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
-		Page page = newPage(req, resp, session, "azkaban/webapp/servlet/velocity/jobsummarypage.vm");
-		User user = session.getUser();
-		int execId = getIntParam(req, "execid");
-		String jobId = getParam(req, "job");
-		int attempt = getIntParam(req, "attempt", 0);
-		page.add("execid", execId);
-		page.add("jobid", jobId);
-		page.add("attempt", attempt);
-		
-		ExecutableFlow flow = null;
-		try {
-			flow = executorManager.getExecutableFlow(execId);
-			if (flow == null) {
-				page.add("errorMsg", "Error loading executing flow " + execId + ": not found.");
-				page.render();
-				return;
-			}
-		} catch (ExecutorManagerException e) {
-			page.add("errorMsg", "Error loading executing flow: " + e.getMessage());
-			page.render();
-			return;
-		}
-		
-		int projectId = flow.getProjectId();
-		Project project = getProjectPageByPermission(page, projectId, user, Type.READ);
-		if (project == null) {
-			page.render();
-			return;
-		}
-		
-		page.add("projectName", project.getName());
-		page.add("flowid", flow.getFlowId());
-		
-		page.render();
-	}
-	
-	private void handleExecutionJobLogPage(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
-		Page page = newPage(req, resp, session, "azkaban/webapp/servlet/velocity/joblogpage.vm");
+	private void handleExecutionJobDetailsPage(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
+		Page page = newPage(req, resp, session, "azkaban/webapp/servlet/velocity/jobdetailspage.vm");
 		User user = session.getUser();
 		int execId = getIntParam(req, "execid");
 		String jobId = getParam(req, "job");
diff --git a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
index 33e4c90..3e4af3f 100644
--- a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
@@ -53,7 +53,7 @@
 #set($current_page="all")
 #set($show_schedule="false")
 
-#parse( "azkaban/webapp/servlet/velocity/nav.vm" )
+#parse("azkaban/webapp/servlet/velocity/nav.vm")
 		<div class="messaging"><p id="messageClose">X</p><p id="message"></p></div>  
 		<div class="content">
 #if($errorMsg)
@@ -119,8 +119,7 @@
 									<th class="date">End Time</th>
 									<th class="elapse">Elapsed</th>
 									<th class="status">Status</th>
-									<th class="logs">Logs</th>
-									<th class="summary">Summary</th>
+									<th class="details">Details</th>
 								</tr>
 							</thead>
 							<tbody id="executableBody">
@@ -147,7 +146,7 @@
 					</table>
 				</div>
 
-#parse( "azkaban/webapp/servlet/velocity/flowexecutionpanel.vm" )
+#parse("azkaban/webapp/servlet/velocity/flowexecutionpanel.vm")
 #end
 		</div>
 
diff --git a/src/web/css/azkaban.css b/src/web/css/azkaban.css
index aadf527..41e0af3 100644
--- a/src/web/css/azkaban.css
+++ b/src/web/css/azkaban.css
@@ -2422,7 +2422,7 @@ span.sublabel {
 	width: 100px;
 }
 
-.executionInfo table th.logs {
+.executionInfo table th.details {
 	width: 10px;
 }
 
diff --git a/src/web/js/azkaban.exflow.view.js b/src/web/js/azkaban.exflow.view.js
index 955b648..fce01a8 100644
--- a/src/web/js/azkaban.exflow.view.js
+++ b/src/web/js/azkaban.exflow.view.js
@@ -101,8 +101,8 @@ azkaban.FlowTabView= Backbone.View.extend({
   	$("#resumebtn").hide();
   	$("#retrybtn").hide();
   
- 	this.model.bind('change:graph', this.handleFlowStatusChange, this);
-	this.model.bind('change:update', this.handleFlowStatusChange, this);
+		this.model.bind('change:graph', this.handleFlowStatusChange, this);
+		this.model.bind('change:update', this.handleFlowStatusChange, this);
 	
   	var selectedView = settings.selectedView;
   	if (selectedView == "jobslist") {
@@ -466,8 +466,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
 		var tdEnd = document.createElement("td");
 		var tdElapse = document.createElement("td");
 		var tdStatus = document.createElement("td");
-		var tdLog = document.createElement("td");
-		var tdSummary = document.createElement("td");
+		var tdDetails = document.createElement("td");
 		
 		$(tr).append(tdName);
 		$(tr).append(tdTimeline);
@@ -475,8 +474,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
 		$(tr).append(tdEnd);
 		$(tr).append(tdElapse);
 		$(tr).append(tdStatus);
-		$(tr).append(tdLog);
-		$(tr).append(tdSummary);
+		$(tr).append(tdDetails);
 		$(tr).attr("id", node.id + "-row");
 		$(tdTimeline).attr("id", node.id + "-timeline");
 		$(tdStart).attr("id", node.id + "-start");
@@ -515,19 +513,10 @@ azkaban.ExecutionListView = Backbone.View.extend({
 		var a = document.createElement("a");
 		$(a).attr("href", logURL);
 		$(a).attr("id", node.id + "-log-link");
-		$(a).text("Log");
-		$(tdLog).addClass("logLink");
-		$(tdLog).append(a);
+		$(a).text("Details");
+		$(tdDetails).addClass("details");
+		$(tdDetails).append(a);
 		
-		var summaryURL = contextURL + "/executor?execid=" + execId + 
-			"&job=" + node.id + "&summary";
-		a = document.createElement("a");
-		$(a).attr("href", summaryURL);
-		$(a).attr("id", node.id + "-summary-link");
-		$(a).text("Summary");
-		$(tdSummary).addClass("logSummary");
-		$(tdSummary).append(a);
-
 		executingBody.append(tr);
 	}
 });
diff --git a/src/web/js/azkaban.jobdetails.view.js b/src/web/js/azkaban.jobdetails.view.js
new file mode 100644
index 0000000..aba399c
--- /dev/null
+++ b/src/web/js/azkaban.jobdetails.view.js
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2012 LinkedIn Corp.
+ * 
+ * Licensed 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.
+ */
+
+$.namespace('azkaban');
+
+var logModel;
+azkaban.LogModel = Backbone.Model.extend({});
+
+var jobLogView;
+azkaban.JobLogView = Backbone.View.extend({
+	events: {
+		"click #updateLogBtn" : "handleUpdate"
+	},
+	initialize: function(settings) {
+		this.model.set({"offset": 0});
+		this.handleUpdate();
+	},
+	handleUpdate: function(evt) {
+		var requestURL = contextURL + "/executor"; 
+		var model = this.model;
+		var finished = false;
+
+		var date = new Date();
+		var startTime = date.getTime();
+		
+		while (!finished) {
+			var offset = this.model.get("offset");
+			var requestData = {
+				"execid": execId, 
+				"jobId": jobId, 
+				"ajax":"fetchExecJobLogs", 
+				"offset": offset, 
+				"length": 50000, 
+				"attempt": attempt
+			};
+
+			var successHandler = function(data) {
+				console.log("fetchLogs");
+				if (data.error) {
+					console.log(data.error);
+					finished = true;
+				}
+				else if (data.length == 0) {
+					finished = true;
+				}
+				else {
+					var date = new Date();
+					var endTime = date.getTime();
+					if ((endTime - startTime) > 10000) {
+						finished = true;
+						showDialog("Alert","The log is taking a long time to finish loading. Azkaban has stopped loading them. Please click Refresh to restart the load.");
+					} 
+
+					var re = /(https?:\/\/(([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?))/g;
+					var log = $("#logSection").text();
+					if (!log) {
+						log = data.data;
+					}
+					else {
+						log += data.data;
+					}
+
+					var newOffset = data.offset + data.length;
+					$("#logSection").text(log);
+					log = $("#logSection").html();
+					log = log.replace(re, "<a href=\"$1\" title=\"\">$1</a>");
+					$("#logSection").html(log);
+
+					model.set({"offset": newOffset, "log": log});
+					$(".logViewer").scrollTop(9999);
+				}
+			}
+
+			$.ajax({
+				url: requestURL,
+				type: "get",
+				async: false,
+				data: requestData,
+				dataType: "json",
+				error: function(data) {
+					console.log(data);
+					finished = true;
+				},
+				success: successHandler
+			});
+		}
+	}
+});
+
+var summaryModel;
+azkaban.SummaryModel = Backbone.Model.extend({});
+
+var jobSummaryView;
+azkaban.JobSummaryView = Backbone.View.extend({
+	events: {
+		"click #updateSummaryBtn" : "handleUpdate"
+	},
+	initialize: function(settings) {
+		this.handleUpdate();
+	},
+	handleUpdate: function(evt) {
+		var requestURL = contextURL + "/executor"; 
+		var model = this.model;
+		var self = this;
+
+		var requestData = {
+			"execid": execId, 
+			"jobId": jobId, 
+			"ajax":"fetchExecJobSummary", 
+			"attempt": attempt
+		};
+
+		$.ajax({
+			url: requestURL,
+			dataType: "json",
+			data: requestData,
+			error: function(data) {
+				console.log(data);
+			},
+			success: function(data) {
+				console.log("fetchSummary");
+				if (data.error) {
+					console.log(data.error);
+				}
+				else {
+					self.renderCommandTable(data.command, data.classpath, data.params);
+					self.renderJobTable(data.summaryTableHeaders, data.summaryTableData, "summary");
+					self.renderJobTable(data.statTableHeaders, data.statTableData, "stats");
+				}
+			}
+		});
+	},
+	renderCommandTable: function(command, classpath, params) {
+		if (command) {
+			var commandTable = $("#commandTable");
+			var i;
+			
+			// Add row for command
+			var tr = document.createElement("tr");
+			var td = document.createElement("td");
+			$(td).append("<b>Command</b>");
+			$(tr).append(td);
+			td = document.createElement("td");
+			$(td).text(command);
+			$(tr).append(td);
+			commandTable.append(tr);
+			
+			// Add row for classpath
+			if (classpath && classpath.length > 0) {
+				tr = document.createElement("tr");
+				td = document.createElement("td");
+				$(td).append("<b>Classpath</b>");
+				$(tr).append(td);
+				td = document.createElement("td");
+				$(td).append(classpath[0]);
+				for (i = 1; i < classpath.length; i++) {
+					$(td).append("<br/>" + classpath[i]);
+				}
+				$(tr).append(td);
+				commandTable.append(tr);
+			}
+			
+			// Add row for params
+			if (params && params.length > 0) {
+				tr = document.createElement("tr");
+				td = document.createElement("td");
+				$(td).append("<b>Params</b>");
+				$(tr).append(td);
+				td = document.createElement("td");
+				$(td).append(params[0]);
+				for (i = 1; i < params.length; i++) {
+					$(td).append("<br/>" + params[i]);
+				}
+				$(tr).append(td);
+				commandTable.append(tr);
+			}
+		}
+	},
+	renderJobTable: function(headers, data, prefix) {
+		if (headers) {
+			// Add table headers
+			var header = $("#" + prefix + "Header");
+			var tr = document.createElement("tr");
+			var i;
+			for (i = 0; i < headers.length; i++) {
+				var th = document.createElement("th");
+				$(th).text(headers[i]);
+				$(tr).append(th);
+			}
+			header.append(tr);
+			
+			// Add table body
+			var body = $("#" + prefix + "Body");
+			for (i = 0; i < data.length; i++) {
+				tr = document.createElement("tr");
+				var row = data[i];
+				for (var j = 0; j < headers.length; j++) {
+					var td = document.createElement("td");
+					$(td).text(row[j]);
+					$(tr).append(td);
+				}
+				body.append(tr);
+			}
+		}
+	}
+});
+
+var jobTabView;
+azkaban.JobTabView = Backbone.View.extend({
+	events: {
+		'click #jobSummaryViewLink': 'handleJobSummaryViewLinkClick',
+		'click #jobLogViewLink': 'handleJobLogViewLinkClick'
+	},
+
+	initialize: function(settings) {
+		var selectedView = settings.selectedView;
+		if (selectedView == 'joblog') {
+			this.handleJobLogViewLinkClick();
+		}
+		else {
+			this.handleJobSummaryViewLinkClick();
+		}
+	},
+
+	render: function() {
+	},
+
+	handleJobLogViewLinkClick: function() {
+		$('#jobSummaryViewLink').removeClass('selected');
+		$('#jobSummaryView').hide();
+		$('#jobLogViewLink').addClass('selected');
+		$('#jobLogView').show();
+	},
+	
+	handleJobSummaryViewLinkClick: function() {
+		$('#jobSummaryViewLink').addClass('selected');
+		$('#jobSummaryView').show();
+		$('#jobLogViewLink').removeClass('selected');
+		$('#jobLogView').hide();
+	},
+});
+
+var showDialog = function(title, message) {
+  $('#messageTitle').text(title);
+  $('#messageBox').text(message);
+  $('#messageDialog').modal({
+		closeHTML: "<a href='#' title='Close' class='modal-close'>x</a>",
+		position: ["20%",],
+		containerId: 'confirm-container',
+		containerCss: {
+			'height': '220px',
+			'width': '565px'
+		},
+		onShow: function (dialog) {
+		}
+	});
+}
+
+$(function() {
+	var selected;
+	logModel = new azkaban.LogModel();
+	jobLogView = new azkaban.JobLogView({
+		el: $('#jobLogView'), 
+		model: logModel
+	});
+
+	summaryModel = new azkaban.SummaryModel();
+	jobSummaryView = new azkaban.JobSummaryView({
+		el: $('#jobSummaryView'), 
+		model: summaryModel
+	});
+
+	jobTabView = new azkaban.JobTabView({
+		el: $('#headertabs')
+	});
+
+	if (window.location.hash) {
+		var hash = window.location.hash;
+		if (hash == '#joblog') {
+			jobTabView.handleJobLogViewLinkClick();
+		}
+		else if (hash == '#jobsummary') {
+			jobTabView.handleJobSummaryViewLinkClick();
+		}
+	}
+});