azkaban-uncached
Changes
src/web/js/azkaban.exflow.view.js 11(+11 -0)
src/web/js/azkaban.jobsummary.view.js 116(+116 -0)
Details
diff --git a/src/java/azkaban/webapp/servlet/ExecutorServlet.java b/src/java/azkaban/webapp/servlet/ExecutorServlet.java
index 919045e..2aa7da2 100644
--- a/src/java/azkaban/webapp/servlet/ExecutorServlet.java
+++ b/src/java/azkaban/webapp/servlet/ExecutorServlet.java
@@ -71,8 +71,11 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
handleAJAXAction(req, resp, session);
}
else if (hasParam(req, "execid")) {
- if (hasParam(req, "job")) {
- handleExecutionJobPage(req, resp, session);
+ if (hasParam(req, "summary")) {
+ handleExecutionJobSummaryPage(req, resp, session);
+ }
+ else if (hasParam(req, "job")) {
+ handleExecutionJobLogPage(req, resp, session);
}
else {
handleExecutionFlowPage(req, resp, session);
@@ -83,7 +86,44 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
}
}
- private void handleExecutionJobPage(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
+ 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");
User user = session.getUser();
int execId = getIntParam(req, "execid");
@@ -97,7 +137,7 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
try {
flow = executorManager.getExecutableFlow(execId);
if (flow == null) {
- page.add("errorMsg", "Error loading executing flow " + execId + " not found.");
+ page.add("errorMsg", "Error loading executing flow " + execId + ": not found.");
page.render();
return;
}
diff --git a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
index 5c4f1d4..33e4c90 100644
--- a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
@@ -120,6 +120,7 @@
<th class="elapse">Elapsed</th>
<th class="status">Status</th>
<th class="logs">Logs</th>
+ <th class="summary">Summary</th>
</tr>
</thead>
<tbody id="executableBody">
diff --git a/src/java/azkaban/webapp/servlet/velocity/jobsummarypage.vm b/src/java/azkaban/webapp/servlet/velocity/jobsummarypage.vm
new file mode 100644
index 0000000..349a1e3
--- /dev/null
+++ b/src/java/azkaban/webapp/servlet/velocity/jobsummarypage.vm
@@ -0,0 +1,93 @@
+#*
+ * 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.
+*#
+
+<!DOCTYPE html>
+<html>
+ <head>
+#parse( "azkaban/webapp/servlet/velocity/style.vm" )
+ <script type="text/javascript" src="${context}/js/jquery/jquery-1.9.1.js"></script>
+ <script type="text/javascript" src="${context}/js/underscore-1.4.4-min.js"></script>
+ <script type="text/javascript" src="${context}/js/namespace.js"></script>
+ <script type="text/javascript" src="${context}/js/backbone-0.9.10-min.js"></script>
+ <script type="text/javascript" src="${context}/js/jquery.simplemodal-1.4.4.js"></script>
+ <script type="text/javascript" src="${context}/js/azkaban.ajax.utils.js"></script>
+ <script type="text/javascript" src="${context}/js/azkaban.nav.js"></script>
+ <script type="text/javascript" src="${context}/js/azkaban.jobsummary.view.js"></script>
+ <script type="text/javascript">
+ var contextURL = "${context}";
+ var currentTime = ${currentTime};
+ var timezone = "${timezone}";
+ var errorMessage = null;
+ var successMessage = null;
+
+ var projectName = "${projectName}";
+ var flowName = "${flowid}";
+ var execId = "${execid}";
+ var jobId = "${jobid}";
+ var attempt = ${attempt};
+ </script>
+ </head>
+ <body>
+ #set($current_page="executing")
+#parse( "azkaban/webapp/servlet/velocity/nav.vm" )
+ <div class="content">
+#if($errorMsg)
+ <div class="box-error-message">$errorMsg</div>
+#else
+#if($error_message != "null")
+ <div class="box-error-message">$error_message</div>
+#elseif($success_message != "null")
+ <div class="box-success-message">$success_message</div>
+#end
+
+ <div id="all-jobs-content">
+ <div class="section-hd flow-header">
+ <h2><a href="${context}/executor?execid=${execid}&job=${jobid}">Job Execution<span>$jobid</span></a></h2>
+ <div class="section-sub-hd">
+ <h4><a href="${context}/manager?project=${projectName}">Project <span>$projectName</span></a></h4>
+ <h4 class="separator">></h4>
+ <h4><a href="${context}/manager?project=${projectName}&flow=${flowid}">Flow <span>$flowid</span></a></h4>
+ <h4 class="separator">></h4>
+ <h4><a href="${context}/executor?execid=${execid}#jobslist">Execution <span>$execid</span></a></h4>
+ <h4 class="separator">></h4>
+ <h4><a href="${context}/manager?project=${projectName}&flow=${flowid}&job=$jobid">Job <span>$jobid</span></a></h4>
+ </div>
+ </div>
+ </div>
+
+ <div id="headertabs" class="headertabs">
+ <ul>
+ <li>Summary</li>
+ </ul>
+ </div>
+
+ <div id="jobSummaryView" class="summaryView">
+ <div class="summaryHeader"><div class="summaryButtonRow"><div id="updateSummaryBtn" class="btn7">Refresh</div></div></div>
+ <div class="summaryViewer">
+ <pre id="summarySection" class="summary"></pre>
+ </div>
+ </div>
+#end
+
+ <div id="messageDialog" class="modal">
+ <h3 id="messageTitle">Error</h3>
+ <div class="messageDiv">
+ <p id="messageBox"></p>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
src/web/js/azkaban.exflow.view.js 11(+11 -0)
diff --git a/src/web/js/azkaban.exflow.view.js b/src/web/js/azkaban.exflow.view.js
index 377703b..955b648 100644
--- a/src/web/js/azkaban.exflow.view.js
+++ b/src/web/js/azkaban.exflow.view.js
@@ -467,6 +467,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
var tdElapse = document.createElement("td");
var tdStatus = document.createElement("td");
var tdLog = document.createElement("td");
+ var tdSummary = document.createElement("td");
$(tr).append(tdName);
$(tr).append(tdTimeline);
@@ -475,6 +476,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
$(tr).append(tdElapse);
$(tr).append(tdStatus);
$(tr).append(tdLog);
+ $(tr).append(tdSummary);
$(tr).attr("id", node.id + "-row");
$(tdTimeline).attr("id", node.id + "-timeline");
$(tdStart).attr("id", node.id + "-start");
@@ -516,6 +518,15 @@ azkaban.ExecutionListView = Backbone.View.extend({
$(a).text("Log");
$(tdLog).addClass("logLink");
$(tdLog).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);
}
src/web/js/azkaban.jobsummary.view.js 116(+116 -0)
diff --git a/src/web/js/azkaban.jobsummary.view.js b/src/web/js/azkaban.jobsummary.view.js
new file mode 100644
index 0000000..eafd9c9
--- /dev/null
+++ b/src/web/js/azkaban.jobsummary.view.js
@@ -0,0 +1,116 @@
+/*
+ * 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");
+ $.ajax({
+ url: requestURL,
+ type: "get",
+ async: false,
+ dataType: "json",
+ data: {"execid": execId, "jobId": jobId, "ajax":"fetchExecJobLogs", "offset": offset, "length": 50000, "attempt": attempt},
+ error: function(data) {
+ console.log(data);
+ finished = true;
+ },
+ success: 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);
+ }
+ }
+ });
+ }
+ }
+});
+
+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});
+});