azkaban-developers

Merge pull request #168 from davidzchen/job-summary-plugin Move

2/12/2014 5:46:16 PM

Details

diff --git a/src/java/azkaban/webapp/AzkabanWebServer.java b/src/java/azkaban/webapp/AzkabanWebServer.java
index 2133db8..ebb0aa0 100644
--- a/src/java/azkaban/webapp/AzkabanWebServer.java
+++ b/src/java/azkaban/webapp/AzkabanWebServer.java
@@ -999,7 +999,7 @@ public class AzkabanWebServer extends AzkabanServer {
 			
 			String pluginName = pluginProps.getString("viewer.name");
 			String pluginWebPath = pluginProps.getString("viewer.path");
-			String pluginJobType = pluginProps.getString("viewer.jobtype", null);
+			String pluginJobTypes = pluginProps.getString("viewer.jobtypes", null);
 			int pluginOrder = pluginProps.getInt("viewer.order", 0);
 			boolean pluginHidden = pluginProps.getBoolean("viewer.hidden", false);
 			List<String> extLibClasspath = pluginProps.getStringList("viewer.external.classpaths", (List<String>)null);
@@ -1108,7 +1108,7 @@ public class AzkabanWebServer extends AzkabanServer {
 						pluginWebPath, 
 						pluginOrder, 
 						pluginHidden,
-						pluginJobType));
+						pluginJobTypes));
 		}
 		
 		// Velocity needs the jar resource paths to be set.
diff --git a/src/java/azkaban/webapp/plugin/PluginRegistry.java b/src/java/azkaban/webapp/plugin/PluginRegistry.java
index b9382b6..a6f74d2 100644
--- a/src/java/azkaban/webapp/plugin/PluginRegistry.java
+++ b/src/java/azkaban/webapp/plugin/PluginRegistry.java
@@ -24,34 +24,37 @@ import java.util.TreeSet;
 
 public class PluginRegistry {
 
-  private static PluginRegistry registry;
+	private static PluginRegistry registry;
 
-  public TreeSet<ViewerPlugin> viewerPlugins;
+	public TreeSet<ViewerPlugin> viewerPlugins;
 
 	public Map<String, TreeSet<ViewerPlugin>> jobTypeViewerPlugins;
 
-  private PluginRegistry() {
+	private PluginRegistry() {
 		viewerPlugins = new TreeSet<ViewerPlugin>(ViewerPlugin.COMPARATOR);
 		jobTypeViewerPlugins = new HashMap<String, TreeSet<ViewerPlugin>>();
-  }
+	}
 
-  public void register(ViewerPlugin plugin) {
+	public void register(ViewerPlugin plugin) {
 		viewerPlugins.add(plugin);
-		String jobType = plugin.getJobType();
-		if (jobType == null) {
+		List<String> jobTypes = plugin.getJobTypes();
+		if (jobTypes == null) {
 			return;
 		}
-		TreeSet<ViewerPlugin> plugins = null;
-		if (!jobTypeViewerPlugins.containsKey(jobType)) {
-			plugins = new TreeSet<ViewerPlugin>(ViewerPlugin.COMPARATOR);
-			plugins.add(plugin);
-			jobTypeViewerPlugins.put(jobType, plugins);
-		}
-		else {
-			plugins = jobTypeViewerPlugins.get(jobType);
-			plugins.add(plugin);
+
+		for (String jobType : jobTypes) {
+			TreeSet<ViewerPlugin> plugins = null;
+			if (!jobTypeViewerPlugins.containsKey(jobType)) {
+				plugins = new TreeSet<ViewerPlugin>(ViewerPlugin.COMPARATOR);
+				plugins.add(plugin);
+				jobTypeViewerPlugins.put(jobType, plugins);
+			}
+			else {
+				plugins = jobTypeViewerPlugins.get(jobType);
+				plugins.add(plugin);
+			}
 		}
-  }
+	}
 
 	public List<ViewerPlugin> getViewerPlugins() {
 		return new ArrayList<ViewerPlugin>(viewerPlugins);
@@ -65,10 +68,10 @@ public class PluginRegistry {
 		return new ArrayList<ViewerPlugin>(plugins);
 	}
 
-  public static PluginRegistry getRegistry() {
-    if (registry == null) {
-      registry = new PluginRegistry();
-    }
-    return registry;
-  }
+	public static PluginRegistry getRegistry() {
+		if (registry == null) {
+			registry = new PluginRegistry();
+		}
+		return registry;
+	}
 }
diff --git a/src/java/azkaban/webapp/plugin/ViewerPlugin.java b/src/java/azkaban/webapp/plugin/ViewerPlugin.java
index 9e167ff..b2bb851 100644
--- a/src/java/azkaban/webapp/plugin/ViewerPlugin.java
+++ b/src/java/azkaban/webapp/plugin/ViewerPlugin.java
@@ -16,14 +16,16 @@
 
 package azkaban.webapp.plugin;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Comparator;
 
 public class ViewerPlugin {
 	private final String pluginName;
 	private final String pluginPath;
-	private final String jobType;
 	private final int order;
 	private boolean hidden;
+	private final List<String> jobTypes;
 
 	public static final Comparator<ViewerPlugin> COMPARATOR = 
 			new Comparator<ViewerPlugin>() {
@@ -41,12 +43,12 @@ public class ViewerPlugin {
 			String pluginPath, 
 			int order, 
 			boolean hidden,
-			String jobType) {
+			String jobTypes) {
 		this.pluginName = pluginName;
 		this.pluginPath = pluginPath;
 		this.order = order;
 		this.setHidden(hidden);
-		this.jobType = jobType;
+		this.jobTypes = parseJobTypes(jobTypes);
 	}
 
 	public String getPluginName() {
@@ -69,7 +71,19 @@ public class ViewerPlugin {
 		this.hidden = hidden;
 	}
 
-	public String getJobType() {
-		return jobType;
+	protected List<String> parseJobTypes(String jobTypesStr) {
+		if (jobTypesStr == null) {
+			return null;
+		}
+		String[] parts = jobTypesStr.split(",");
+		List<String> jobTypes = new ArrayList<String>();
+		for (int i = 0; i < parts.length; ++i) {
+			jobTypes.add(parts[i].trim());
+		}
+		return jobTypes;
+	}
+
+	public List<String> getJobTypes() {
+		return jobTypes;
 	}
 }
diff --git a/src/java/azkaban/webapp/servlet/velocity/jobdetailsheader.vm b/src/java/azkaban/webapp/servlet/velocity/jobdetailsheader.vm
index 27863ea..8d11704 100644
--- a/src/java/azkaban/webapp/servlet/velocity/jobdetailsheader.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/jobdetailsheader.vm
@@ -49,11 +49,9 @@
 
 			<ul class="nav nav-tabs nav-sm" id="headertabs">
 	#if ($current_page == "executing")
-				<li id="jobLogViewLink"><a href="#logs">Job Logs</a></li>
-				<li id="jobSummaryViewLink"><a href="#summary">Summary</a></li>
+				<li class="active" id="jobLogViewLink"><a href="#logs">Job Logs</a></li>
 	#else
 				<li id="jobLogViewLink"><a href="${context}/executor?execid=${execid}&job=${jobid}#logs">Job Logs</a></li>
-				<li id="jobSummaryViewLink"><a href="${context}/executor?execid=${execid}&job=${jobid}#summary">Summary</a></li>
 	#end
 	#foreach ($jobViewerPlugin in $jobViewerPlugins)
 				<li#if($current_page == $jobViewerPlugin.pluginName) class="active"#end><a href="$!context/${jobViewerPlugin.pluginPath}?execid=${execid}&jobid=${jobid}">$jobViewerPlugin.pluginName</a></li>
diff --git a/src/java/azkaban/webapp/servlet/velocity/jobdetailspage.vm b/src/java/azkaban/webapp/servlet/velocity/jobdetailspage.vm
index 27f5d40..80d90e1 100644
--- a/src/java/azkaban/webapp/servlet/velocity/jobdetailspage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/jobdetailspage.vm
@@ -22,7 +22,7 @@
 #parse("azkaban/webapp/servlet/velocity/javascript.vm")
 
 		<script type="text/javascript" src="${context}/js/azkaban/util/ajax.js"></script>
-		<script type="text/javascript" src="${context}/js/azkaban/model/log-data.js"></script>
+		<script type="text/javascript" src="${context}/js/azkaban/model/job-log.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/job-details.js"></script>
 		<script type="text/javascript">
 			var contextURL = "${context}";
@@ -68,75 +68,10 @@
             </div>
           </div>
         </div>
-			</div>
-    </div>
-
-	## Job Summary
-
-    <div class="container-full" id="jobSummaryView">
-      <div class="row">
-        <div class="col-lg-12">
-          <h3>
-            Job Summary
-            <div class="pull-right">
-              <button type="button" id="updateSummaryBtn" class="btn btn-xs btn-default">Refresh</button>
-            </div>
-          </h3>
-
-          <div id="jobType">
-            <table id="jobTypeTable" class="table table-striped table-bordered table-hover">
-            </table>
-          </div>
-
-          <div id="command-summary">
-            <h4>Command Summary</h4>
-            <table id="commandTable" class="table table-striped table-bordered table-hover">
-            </table>
-          </div>
-        
-          <div id="pigJobSummary">
-            <h4>Pig Job Summary</h4>
-            <table class="table table-striped table-bordered table-hover">
-              <thead id="summaryHeader">
-              </thead>
-              <tbody id="summaryBody">
-              </tbody>
-            </table>
-          </div>
-        
-          <div id="pigJobStats">
-            <h4>Pig Job Stats</h4>
-              <div class="panel-body-stats">
-                <table class="table table-striped table-bordered table-hover table-condensed">
-                  <thead id="statsHeader">
-                  </thead>
-                  <tbody id="statsBody">
-                  </tbody>
-                </table>
-            </div>
-          </div>
-
-          <div id="hiveJobSummary">
-            <h4>Hive Job Summary</h4>
-            <table class="table table-striped table-bordered table-hover" id="hiveTable">
-              <thead id="hiveTableHeader">
-              </thead>
-              <tbody id="hiveTableBody">
-              </tbody>
-            </table>
-          </div>
-
-          <div id="jobIds">
-            <h4>Map Reduce Jobs</h4>
-            <table class="table table-striped table-bordered table-hover">
-              <tbody id="jobIdsTableBody"></tbody>
-            </table>
-          </div>
-        </div>
       </div>
     </div>
-			
-	## Error message message dialog.
+	
+    ## Error message message dialog.
 
     <div class="container-full">
 			<div class="modal" id="messageDialog">
diff --git a/src/web/js/azkaban/model/job-log.js b/src/web/js/azkaban/model/job-log.js
new file mode 100644
index 0000000..d295578
--- /dev/null
+++ b/src/web/js/azkaban/model/job-log.js
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 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');
+
+azkaban.JobLogModel = Backbone.Model.extend({
+  initialize: function() {
+    this.set("offset", 0);
+    this.set("logData", "");
+  },
+
+  refresh: function() {
+    var requestURL = contextURL + "/executor"; 
+    var finished = false;
+
+    var date = new Date();
+    var startTime = date.getTime();
+    
+    while (!finished) {
+      var requestData = {
+        "execid": execId,
+        "jobId": jobId,
+        "ajax":"fetchExecJobLogs",
+        "offset": this.get("offset"),
+        "length": 50000,
+        "attempt": attempt
+      };
+
+      var self = this;
+
+      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.");
+          }
+
+          self.set("offset", data.offset + data.length);
+          self.set("logData", self.get("logData") + data.data);
+        }
+      }
+
+      $.ajax({
+        url: requestURL,
+        type: "get",
+        async: false,
+        data: requestData,
+        dataType: "json",
+        error: function(data) {
+          console.log(data);
+          finished = true;
+        },
+        success: successHandler
+      });
+    }
+  },
+});
diff --git a/src/web/js/azkaban/view/job-details.js b/src/web/js/azkaban/view/job-details.js
index 07b823e..7620b5a 100644
--- a/src/web/js/azkaban/view/job-details.js
+++ b/src/web/js/azkaban/view/job-details.js
@@ -38,247 +38,6 @@ azkaban.JobLogView = Backbone.View.extend({
 	}
 });
 
-var jobSummaryView;
-azkaban.JobSummaryView = Backbone.View.extend({
-	events: {
-		"click #updateSummaryBtn" : "refresh"
-	},
-
-	initialize: function(settings) {
-		$("#jobType").hide();
-		$("#commandSummary").hide();
-		$("#pigJobSummary").hide();
-		$("#pigJobStats").hide();
-		$("#hiveJobSummary").hide();
-		$("#jobIds").hide();
-
-		this.listenTo(this.model, "change:jobType", this.renderJobTypeTable);
-		this.listenTo(this.model, "change:commandProperties", this.renderCommandTable);
-		this.listenTo(this.model, "change:pigSummary", this.renderPigSummaryTable);
-		this.listenTo(this.model, "change:pigStats", this.renderPigStatsTable);
-		this.listenTo(this.model, "change:hiveSummary", this.renderHiveTable);
-		this.listenTo(this.model, "change:jobIds", this.renderJobIdsTable);
-	},
-
-	refresh: function() {
-		this.model.refresh();
-	},
-
-	handleUpdate: function(evt) {
-		renderJobTable(jobSummary.summaryTableHeaders, jobSummary.summaryTableData, "summary");
-		renderJobTable(jobSummary.statTableHeaders, jobSummary.statTableData, "stats");
-		renderHiveTable(jobSummary.hiveQueries, jobSummary.hiveQueryJobs);
-	},
-
-	renderJobTypeTable: function() {
-		var jobTypeTable = $("#jobTypeTable");
-		var jobType = this.model.get("jobType");
-
-		var tr = document.createElement("tr");
-		var td = document.createElement("td");
-		$(td).html("<b>Job Type</b>");
-		$(tr).append(td);
-		td = document.createElement("td");
-		$(td).html(jobType);
-		$(tr).append(td);
-
-		jobTypeTable.append(tr);
-
-		$("#jobType").show();
-	},
-
-	renderJobIdsTable: function() {
-		var oldBody = $("#jobIdsTableBody");
-		var newBody = $(document.createElement("tbody")).attr("id", "jobIdsTableBody");
-
-		var jobIds = this.model.get("jobIds");
-		var jobUrls = this.model.get("jobTrackerUrls");
-		var numJobs = jobIds.length;
-		for (var i = 0; i < numJobs; i++) {
-			var job = jobIds[i];
-			var tr = document.createElement("tr");
-			var td = document.createElement("td");
-			var html = jobUrls[job] ? "<a href='" + jobUrls[job] + "'>" + job + "</a>" : job;
-			$(td).html(html);
-			$(tr).append(td);
-			newBody.append(tr);
-		}
-
-		oldBody.replaceWith(newBody);
-
-		$("#jobIds").show();
-	},
-
-	renderCommandTable: function() {
-		var commandTable = $("#commandTable");
-		var commandProperties = this.model.get("commandProperties");
-
-		for (var key in commandProperties) {
-			if (commandProperties.hasOwnProperty(key)) {
-				var value = commandProperties[key];
-				if (Array.isArray(value)) {
-					value = value.join("<br/>");
-				}
-				var tr = document.createElement("tr");
-				var keyTd = document.createElement("td");
-				var valueTd = document.createElement("td");
-				$(keyTd).html("<b>" + key + "</b>");
-				$(valueTd).html(value);
-				$(tr).append(keyTd);
-				$(tr).append(valueTd);
-				commandTable.append(tr);
-			}
-		}
-
-		$("#commandSummary").show();
-	},
-	renderPigTable: function(tableName, data) {
-		// Add table headers
-		var header = $("#" + tableName + "Header");
-		var tr = document.createElement("tr");
-		var i;
-		var headers = data[0];
-		var numColumns = headers.length;
-		for (i = 0; i < numColumns; i++) {
-			var th = document.createElement("th");
-			$(th).text(headers[i]);
-			$(tr).append(th);
-		}
-		header.append(tr);
-		
-		// Add table body
-		var body = $("#" + tableName + "Body");
-		for (i = 1; i < data.length; i++) {
-			tr = document.createElement("tr");
-			var row = data[i];
-			for (var j = 0; j < numColumns; j++) {
-				var td = document.createElement("td");
-				if (j == 0) {
-					// first column is a link to job details page 
-					$(td).html(row[j]);
-				} else {
-					$(td).text(row[j]);
-				}
-				$(tr).append(td);
-			}
-			body.append(tr);
-		}
-
-		$("#pigJob" + tableName.charAt(0).toUpperCase() + tableName.substring(1)).show();
-	},
-	renderPigSummaryTable: function() {
-		this.renderPigTable("summary", this.model.get("pigSummary"));
-	},
-	renderPigStatsTable: function() {
-		this.renderPigTable("stats", this.model.get("pigStats"));
-	},
-	renderHiveTable: function() {
-		var hiveSummary = this.model.get("hiveSummary");
-		var queries = hiveSummary.hiveQueries;
-		var queryJobs = hiveSummary.hiveQueryJobs;
-
-		// Set up table column headers
-		var header = $("#hiveTableHeader");
-		var tr = document.createElement("tr");
-
-		var headers;
-		if (this.model.get("hasCumulativeCPU")) {
-			headers = ["Query","Job","Map","Reduce","Cumulative CPU","HDFS Read","HDFS Write"];
-		} else {
-			headers = ["Query","Job","Map","Reduce","HDFS Read","HDFS Write"];
-		}
-
-		var i;
-		for (i = 0; i < headers.length; i++) {
-			var th = document.createElement("th");
-			$(th).text(headers[i]);
-			$(tr).append(th);
-		}
-		header.html(tr);
-		
-		// Construct table body
-		var oldBody = $("#hiveTableBody");
-		var newBody = $(document.createElement("tbody")).attr("id", "hiveTableBody");
-		for (i = 0; i < queries.length; i++) {
-			// new query
-			tr = document.createElement("tr");
-			var td = document.createElement("td");
-			$(td).html("<b>" + queries[i] + "</b>");
-			$(tr).append(td);
-			
-			var jobs = queryJobs[i];
-			if (jobs != null) {
-				// add first job for this query
-				var jobValues = jobs[0];
-				var j;
-				for (j = 0; j < jobValues.length; j++) {
-					td = document.createElement("td");
-					$(td).html(jobValues[j]);
-					$(tr).append(td);
-				}
-				newBody.append(tr);
-				
-				// add remaining jobs for this query
-				for (j = 1; j < jobs.length; j++) {
-					jobValues = jobs[j];
-					tr = document.createElement("tr");
-					
-					// add empty cell for query column
-					td = document.createElement("td");
-					$(td).html("&nbsp;");
-					$(tr).append(td);
-					
-					// add job values
-					for (var k = 0; k < jobValues.length; k++) {
-						td = document.createElement("td");
-						$(td).html(jobValues[k]);
-						$(tr).append(td);
-					}
-					newBody.append(tr);
-				}
-				
-			} else {
-				newBody.append(tr);
-			}
-		}
-		oldBody.replaceWith(newBody);
-
-		$("#hiveJobSummary").show();
-	}
-});
-
-var jobTabView;
-azkaban.JobTabView = Backbone.View.extend({
-	events: {
-		'click #jobSummaryViewLink': 'handleJobSummaryViewLinkClick',
-		'click #jobLogViewLink': 'handleJobLogViewLinkClick'
-	},
-
-	initialize: function(settings) {
-		var selectedView = settings.selectedView;
-		if (selectedView == 'summary') {
-			this.handleJobSummaryViewLinkClick();
-		}
-		else {
-			this.handleJobLogViewLinkClick();
-		}
-	},
-
-	handleJobLogViewLinkClick: function() {
-		$('#jobSummaryViewLink').removeClass('active');
-		$('#jobSummaryView').hide();
-		$('#jobLogViewLink').addClass('active');
-		$('#jobLogView').show();
-	},
-	
-	handleJobSummaryViewLinkClick: function() {
-		$('#jobSummaryViewLink').addClass('active');
-		$('#jobSummaryView').show();
-		$('#jobLogViewLink').removeClass('active');
-		$('#jobLogView').hide();
-	},
-});
-
 var showDialog = function(title, message) {
   $('#messageTitle').text(title);
   $('#messageBox').text(message);
@@ -296,31 +55,10 @@ var showDialog = function(title, message) {
 }
 
 $(function() {
-	var logDataModel = new azkaban.LogDataModel();
-	
+  var jobLogModel = new azkaban.JobLogModel();
 	jobLogView = new azkaban.JobLogView({
 		el: $('#jobLogView'), 
-		model: logDataModel
-	});
-
-	jobSummaryView = new azkaban.JobSummaryView({
-		el: $('#jobSummaryView'), 
-		model: logDataModel
+		model: jobLogModel 
 	});
-
-	jobTabView = new azkaban.JobTabView({
-		el: $('#headertabs')
-	});
-
-	logDataModel.refresh();
-
-	if (window.location.hash) {
-		var hash = window.location.hash;
-		if (hash == '#logs') {
-			jobTabView.handleJobLogViewLinkClick();
-		}
-		else if (hash == '#summary') {
-			jobTabView.handleJobSummaryViewLinkClick();
-		}
-	}
+  jobLogModel.refresh();
 });