azkaban-developers
Changes
src/tl/flowstats.tl 22(+22 -0)
src/tl/flowsummary.tl 10(+5 -5)
src/web/js/azkaban/view/exflow.js 163(+82 -81)
src/web/js/azkaban/view/flow.js 149(+87 -62)
src/web/js/azkaban/view/flow-stats.js 71(+56 -15)
Details
diff --git a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
index 3ecc9cf..c8cbc5c 100644
--- a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
@@ -1,12 +1,12 @@
#*
* 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
@@ -14,16 +14,18 @@
* the License.
*#
-<!DOCTYPE html>
+<!DOCTYPE html>
<html lang="en">
<head>
#parse("azkaban/webapp/servlet/velocity/style.vm")
#parse("azkaban/webapp/servlet/velocity/javascript.vm")
#parse("azkaban/webapp/servlet/velocity/svgflowincludes.vm")
+ <script type="text/javascript" src="${context}/js/raphael.min.js"></script>
+ <script type="text/javascript" src="${context}/js/morris.min.js"></script>
<script type="text/javascript" src="${context}/js/moment.min.js"></script>
<script type="text/javascript" src="${context}/js/bootstrap-datetimepicker.min.js"></script>
-
+
<script type="text/javascript" src="${context}/js/dust-full-2.2.3.min.js"></script>
<script type="text/javascript" src="${context}/js/flowstats.js"></script>
<script type="text/javascript" src="${context}/js/flowstats-no-data.js"></script>
@@ -43,7 +45,7 @@
var flowId = "${flowid}";
var execId = "${execid}";
</script>
-
+ <link rel="stylesheet" type="text/css" href="${context}/css/morris.css" />
<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-1.10.1.custom.css" />
</head>
<body>
@@ -97,7 +99,7 @@
<div class="container-full">
#parse ("azkaban/webapp/servlet/velocity/alerts.vm")
-
+
## Tabs and buttons.
<ul class="nav nav-tabs nav-sm" id="headertabs">
@@ -116,7 +118,7 @@
## Graph View
#parse ("azkaban/webapp/servlet/velocity/flowgraphview.vm")
-
+
## Job List View
<div class="container-full" id="jobListView">
@@ -178,7 +180,7 @@
</div>
</div><!-- /.row -->
</div><!-- /.container-fill -->
-
+
## Error message message dialog.
<div class="container-full">
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index acb0573..0394da9 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -1,12 +1,12 @@
#*
* 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
@@ -14,7 +14,7 @@
* the License.
*#
-<!DOCTYPE html>
+<!DOCTYPE html>
<html lang="en">
<head>
@@ -25,12 +25,12 @@
<script type="text/javascript" src="${context}/js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="${context}/js/raphael.min.js"></script>
<script type="text/javascript" src="${context}/js/morris.min.js"></script>
-
+
<script type="text/javascript" src="${context}/js/dust-full-2.2.3.min.js"></script>
<script type="text/javascript" src="${context}/js/flowsummary.js"></script>
<script type="text/javascript" src="${context}/js/flowstats-no-data.js"></script>
<script type="text/javascript" src="${context}/js/flowstats.js"></script>
-
+
<script type="text/javascript" src="${context}/js/azkaban/view/time-graph.js"></script>
<script type="text/javascript" src="${context}/js/azkaban/util/schedule.js"></script>
<script type="text/javascript" src="${context}/js/azkaban/view/schedule-sla.js"></script>
@@ -43,7 +43,7 @@
var timezone = "${timezone}";
var errorMessage = null;
var successMessage = null;
-
+
var projectId = ${project.id};
var projectName = "${project.name}";
var flowId = "${flowid}";
@@ -60,7 +60,7 @@
#if ($errorMsg)
#parse ("azkaban/webapp/servlet/velocity/errormsg.vm")
#else
-
+
## Page header.
<div class="az-page-header page-header-bare">
@@ -87,9 +87,9 @@
</div>
<div class="container-full">
-
+
#parse ("azkaban/webapp/servlet/velocity/alerts.vm")
-
+
## Tabs
<ul class="nav nav-tabs nav-sm" id="headertabs">
@@ -98,7 +98,7 @@
<li id="summaryViewLink"><a href="#summary">Summary</a></li>
</ul>
</div>
-
+
## Graph view.
#parse ("azkaban/webapp/servlet/velocity/flowgraphview.vm")
@@ -149,7 +149,7 @@
<div class="col-xs-12">
<div class="callout callout-info">
<h4>Analyze last run</h4>
- <p>Analyze the last run for aggregate performance statistics. <strong>Note:</strong> this may take a few minutes, especially if your flow is large.</p>
+ <p>Analyze the last run for aggregate performance statistics. Note: this may take a few minutes, especially if your flow is large.</p>
<p>
<button type="button" id="analyze-btn" class="btn btn-primary">Analyze</button>
</p>
diff --git a/src/java/azkaban/webapp/servlet/velocity/jobhistorypage.vm b/src/java/azkaban/webapp/servlet/velocity/jobhistorypage.vm
index b1838bb..270f8e1 100644
--- a/src/java/azkaban/webapp/servlet/velocity/jobhistorypage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/jobhistorypage.vm
@@ -1,12 +1,12 @@
#*
* 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
@@ -14,7 +14,7 @@
* the License.
*#
-<!DOCTYPE html>
+<!DOCTYPE html>
<html lang="en">
<head>
@@ -32,7 +32,7 @@
var timezone = "${timezone}";
var errorMessage = null;
var successMessage = null;
-
+
var projectId = "$projectId";
var projectName = "$projectName";
var jobName = "$jobid";
@@ -48,7 +48,7 @@
#if ($errorMsg)
#parse ("azkaban/webapp/servlet/velocity/errormsg.vm")
#else
-
+
## Page header
<div class="az-page-header page-header-bare">
@@ -66,7 +66,7 @@
</div>
<div class="container-full">
-
+
#parse ("azkaban/webapp/servlet/velocity/alerts.vm")
## Time graph and job history table.
@@ -84,7 +84,7 @@
<th class="flowid">Flow</th>
<th class="date">Start Time</th>
<th class="date">End Time</th>
- <th class="elapse">Elapse</th>
+ <th class="elapse">Elapse</th>
<th class="status">Status</th>
<th class="logs">Logs</th>
</tr>
src/tl/flowstats.tl 22(+22 -0)
diff --git a/src/tl/flowstats.tl b/src/tl/flowstats.tl
index 8fffebe..5276ce8 100644
--- a/src/tl/flowstats.tl
+++ b/src/tl/flowstats.tl
@@ -1,3 +1,25 @@
+ {?histogram}
+ <div class="row">
+ <div class="col-xs-12">
+ <div class="well well-clear well-sm">
+ <div id="job-histogram"></div>
+ </div>
+ </div>
+ </div>
+ {/histogram}
+
+ {?warnings}
+ <div class="alert alert-warning">
+ <h4>Warnings</h4>
+ <p>These stats may have reduced accuracy due to the following missing information:</p>
+ <ul>
+ {#warnings}
+ <li>{.}</li>
+ {/warnings}
+ </ul>
+ </div>
+ {/warnings}
+
<div class="row">
<div class="col-xs-12">
<h4>Resources</h4>
src/tl/flowsummary.tl 10(+5 -5)
diff --git a/src/tl/flowsummary.tl b/src/tl/flowsummary.tl
index e4f525f..82627d6 100644
--- a/src/tl/flowsummary.tl
+++ b/src/tl/flowsummary.tl
@@ -14,7 +14,7 @@
</table>
</div>
</div>
-
+
<div class="row">
<div class="col-xs-12">
<h3>
@@ -46,12 +46,12 @@
<td class="property-key">SLA</td>
<td class="property-value-half">
{?schedule.slaOptions}
- true
- {:else}
- false
+ true
+ {:else}
+ false
{/schedule.slaOptions}
<div class="pull-right">
- <button type="button" id="addSlaBtn" class="btn btn-xs btn-primary" onclick="slaView.initFromSched({schedule.scheduleId}, '{flowName}')" >Set SLA</button>
+ <button type="button" id="addSlaBtn" class="btn btn-xs btn-primary" onclick="slaView.initFromSched({schedule.scheduleId}, '{flowName}')" >View/Set SLA</button>
</div>
</td>
</tr>
src/web/js/azkaban/view/exflow.js 163(+82 -81)
diff --git a/src/web/js/azkaban/view/exflow.js b/src/web/js/azkaban/view/exflow.js
index 6129676..7c2548c 100644
--- a/src/web/js/azkaban/view/exflow.js
+++ b/src/web/js/azkaban/view/exflow.js
@@ -1,12 +1,12 @@
/*
* 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
@@ -35,16 +35,16 @@ azkaban.StatusView = Backbone.View.extend({
},
render: function(evt) {
var data = this.model.get("data");
-
+
var user = data.submitUser;
$("#submitUser").text(user);
-
+
this.statusUpdate(evt);
},
-
+
statusUpdate: function(evt) {
var data = this.model.get("data");
-
+
statusItem = $("#flowStatus");
for (var j = 0; j < statusList.length; ++j) {
var status = statusList[j];
@@ -52,27 +52,27 @@ azkaban.StatusView = Backbone.View.extend({
}
$("#flowStatus").addClass(data.status);
$("#flowStatus").text(data.status);
-
+
var startTime = data.startTime;
var endTime = data.endTime;
-
+
if (!startTime || startTime == -1) {
$("#startTime").text("-");
}
else {
var date = new Date(startTime);
$("#startTime").text(getDateFormat(date));
-
+
var lastTime = endTime;
if (endTime == -1) {
var currentDate = new Date();
lastTime = currentDate.getTime();
}
-
+
var durationString = getDuration(startTime, lastTime);
$("#duration").text(durationString);
}
-
+
if (!endTime || endTime == -1) {
$("#endTime").text("-");
}
@@ -96,17 +96,17 @@ azkaban.FlowTabView = Backbone.View.extend({
"click #resumebtn": "handleResumeClick",
"click #retrybtn": "handleRetryClick"
},
-
+
initialize: function(settings) {
$("#cancelbtn").hide();
$("#executebtn").hide();
$("#pausebtn").hide();
$("#resumebtn").hide();
$("#retrybtn").hide();
-
+
this.model.bind('change:graph', this.handleFlowStatusChange, this);
this.model.bind('change:update', this.handleFlowStatusChange, this);
-
+
var selectedView = settings.selectedView;
if (selectedView == "jobslist") {
this.handleJobslistLinkClick();
@@ -115,60 +115,60 @@ azkaban.FlowTabView = Backbone.View.extend({
this.handleGraphLinkClick();
}
},
-
+
render: function() {
console.log("render graph");
},
-
+
handleGraphLinkClick: function(){
$("#jobslistViewLink").removeClass("active");
$("#graphViewLink").addClass("active");
$("#flowLogViewLink").removeClass("active");
$("#statsViewLink").removeClass("active");
-
+
$("#jobListView").hide();
$("#graphView").show();
$("#flowLogView").hide();
$("#statsView").hide();
},
-
+
handleJobslistLinkClick: function() {
$("#graphViewLink").removeClass("active");
$("#jobslistViewLink").addClass("active");
$("#flowLogViewLink").removeClass("active");
$("#statsViewLink").removeClass("active");
-
+
$("#graphView").hide();
$("#jobListView").show();
$("#flowLogView").hide();
$("#statsView").hide();
},
-
+
handleLogLinkClick: function() {
$("#graphViewLink").removeClass("active");
$("#jobslistViewLink").removeClass("active");
$("#flowLogViewLink").addClass("active");
$("#statsViewLink").removeClass("active");
-
+
$("#graphView").hide();
$("#jobListView").hide();
$("#flowLogView").show();
$("#statsView").hide();
},
-
+
handleStatsLinkClick: function() {
$("#graphViewLink").removeClass("active");
$("#jobslistViewLink").removeClass("active");
$("#flowLogViewLink").removeClass("active");
$("#statsViewLink").addClass("active");
-
+
$("#graphView").hide();
$("#jobListView").hide();
$("#flowLogView").hide();
statsView.show();
$("#statsView").show();
},
-
+
handleFlowStatusChange: function() {
var data = this.model.get("data");
$("#cancelbtn").hide();
@@ -203,7 +203,7 @@ azkaban.FlowTabView = Backbone.View.extend({
$("#executebtn").show();
}
},
-
+
handleCancelClick: function(evt) {
var requestURL = contextURL + "/executor";
var requestData = {"execid": execId, "ajax": "cancelFlow"};
@@ -219,7 +219,7 @@ azkaban.FlowTabView = Backbone.View.extend({
};
ajaxCall(requestURL, requestData, successHandler);
},
-
+
handleRetryClick: function(evt) {
var graphData = graphModel.get("data");
var requestURL = contextURL + "/executor";
@@ -236,11 +236,11 @@ azkaban.FlowTabView = Backbone.View.extend({
};
ajaxCall(requestURL, requestData, successHandler);
},
-
+
handleRestartClick: function(evt) {
console.log("handleRestartClick");
var data = graphModel.get("data");
-
+
var executingData = {
project: projectName,
ajax: "executeFlow",
@@ -250,7 +250,7 @@ azkaban.FlowTabView = Backbone.View.extend({
};
flowExecuteDialogView.show(executingData);
},
-
+
handlePauseClick: function(evt) {
var requestURL = contextURL + "/executor";
var requestData = {"execid": execId, "ajax":"pauseFlow"};
@@ -266,7 +266,7 @@ azkaban.FlowTabView = Backbone.View.extend({
};
ajaxCall(requestURL, requestData, successHandler);
},
-
+
handleResumeClick: function(evt) {
var requestURL = contextURL + "/executor";
var requestData = {"execid": execId, "ajax":"resumeFlow"};
@@ -304,17 +304,17 @@ azkaban.FlowLogView = Backbone.View.extend({
},
handleUpdate: function(evt) {
var offset = this.model.get("offset");
- var requestURL = contextURL + "/executor";
+ var requestURL = contextURL + "/executor";
var model = this.model;
console.log("fetchLogs offset is " + offset)
$.ajax({
- async: false,
+ async: false,
url: requestURL,
data: {
- "execid": execId,
- "ajax": "fetchExecFlowLogs",
- "offset": offset,
+ "execid": execId,
+ "ajax": "fetchExecFlowLogs",
+ "offset": offset,
"length": 50000
},
success: function(data) {
@@ -346,7 +346,7 @@ var statsView;
azkaban.StatsView = Backbone.View.extend({
events: {
},
-
+
initialize: function(settings) {
this.model.bind('change:graph', this.statusUpdate, this);
this.model.bind('change:update', this.statusUpdate, this);
@@ -385,22 +385,22 @@ var updateStatus = function(updateTime) {
var requestURL = contextURL + "/executor";
var oldData = graphModel.get("data");
var nodeMap = graphModel.get("nodeMap");
-
+
if (!updateTime) {
updateTime = oldData.updateTime ? oldData.updateTime : 0;
}
var requestData = {
- "execid": execId,
- "ajax": "fetchexecflowupdate",
+ "execid": execId,
+ "ajax": "fetchexecflowupdate",
"lastUpdateTime": updateTime
};
-
+
var successHandler = function(data) {
console.log("data updated");
if (data.updateTime) {
updateGraph(oldData, data);
-
+
graphModel.set({"update": data});
graphModel.trigger("change:update");
}
@@ -415,12 +415,12 @@ var updateGraph = function(data, update) {
data.updateTime = update.updateTime;
data.status = update.status;
update.changedNode = data;
-
+
if (update.nodes) {
for (var i = 0; i < update.nodes.length; ++i) {
var newNode = update.nodes[i];
var oldNode = nodeMap[newNode.id];
-
+
updateGraph(oldNode, newNode);
}
}
@@ -429,17 +429,17 @@ var updateGraph = function(data, update) {
var updateTime = -1;
var updaterFunction = function() {
var oldData = graphModel.get("data");
- var keepRunning =
- oldData.status != "SUCCEEDED" &&
- oldData.status != "FAILED" &&
+ var keepRunning =
+ oldData.status != "SUCCEEDED" &&
+ oldData.status != "FAILED" &&
oldData.status != "KILLED";
if (keepRunning) {
updateStatus();
var data = graphModel.get("data");
- if (data.status == "UNKNOWN" ||
- data.status == "WAITING" ||
+ if (data.status == "UNKNOWN" ||
+ data.status == "WAITING" ||
data.status == "PREPARING") {
setTimeout(function() {updaterFunction();}, 1000);
}
@@ -459,9 +459,9 @@ var updaterFunction = function() {
var logUpdaterFunction = function() {
var oldData = graphModel.get("data");
- var keepRunning =
- oldData.status != "SUCCEEDED" &&
- oldData.status != "FAILED" &&
+ var keepRunning =
+ oldData.status != "SUCCEEDED" &&
+ oldData.status != "FAILED" &&
oldData.status != "KILLED";
if (keepRunning) {
// update every 30 seconds for the logs until finished
@@ -479,7 +479,7 @@ var exNodeClickCallback = function(event) {
var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowId + "&job=" + jobId;
var visualizerURL = contextURL + "/pigvisualizer?execid=" + execId + "&jobid=" + jobId;
- var menu = [
+ var menu = [
{title: "Open Job...", callback: function() {window.location.href = requestURL;}},
{title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
{title: "Visualize Job...", callback: function() {window.location.href = visualizerURL;}}
@@ -494,7 +494,7 @@ var exJobClickCallback = function(event) {
var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowId + "&job=" + jobId;
var visualizerURL = contextURL + "/pigvisualizer?execid=" + execId + "&jobid=" + jobId;
- var menu = [
+ var menu = [
{title: "Open Job...", callback: function() {window.location.href = requestURL;}},
{title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
{title: "Visualize Job...", callback: function() {window.location.href = visualizerURL;}}
@@ -511,13 +511,13 @@ var exGraphClickCallback = function(event) {
console.log("Graph clicked callback");
var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowId;
- var menu = [
+ var menu = [
{title: "Open Flow...", callback: function() {window.location.href=requestURL;}},
{title: "Open Flow in New Window...", callback: function() {window.open(requestURL);}},
{break: 1},
{title: "Center Graph", callback: function() {graphModel.trigger("resetPanZoom");}}
];
-
+
contextMenuView.show(event, menu);
}
@@ -526,68 +526,69 @@ var flowStatsModel;
$(function() {
var selected;
-
+
graphModel = new azkaban.GraphModel();
logModel = new azkaban.LogModel();
-
+
flowTabView = new azkaban.FlowTabView({
- el: $('#headertabs'),
+ el: $('#headertabs'),
model: graphModel
});
-
+
mainSvgGraphView = new azkaban.SvgGraphView({
- el: $('#svgDiv'),
- model: graphModel,
- rightClick: {
- "node": nodeClickCallback,
- "edge": edgeClickCallback,
- "graph": graphClickCallback
+ el: $('#svgDiv'),
+ model: graphModel,
+ rightClick: {
+ "node": nodeClickCallback,
+ "edge": edgeClickCallback,
+ "graph": graphClickCallback
}
});
-
+
jobsListView = new azkaban.JobListView({
- el: $('#joblist-panel'),
- model: graphModel,
+ el: $('#joblist-panel'),
+ model: graphModel,
contextMenuCallback: jobClickCallback
});
-
+
flowLogView = new azkaban.FlowLogView({
- el: $('#flowLogView'),
+ el: $('#flowLogView'),
model: logModel
});
-
+
statusView = new azkaban.StatusView({
- el: $('#flow-status'),
+ el: $('#flow-status'),
model: graphModel
});
-
+
flowStatsModel = new azkaban.FlowStatsModel();
flowStatsView = new azkaban.FlowStatsView({
el: $('#flow-stats-container'),
- model: flowStatsModel
+ model: flowStatsModel,
+ histogram: false
});
statsView = new azkaban.StatsView({
- el: $('#statsView'),
+ el: $('#statsView'),
model: graphModel
});
-
+
executionListView = new azkaban.ExecutionListView({
- el: $('#jobListView'),
+ el: $('#jobListView'),
model: graphModel
});
-
+
var requestURL = contextURL + "/executor";
var requestData = {"execid": execId, "ajax":"fetchexecflow"};
var successHandler = function(data) {
console.log("data fetched");
graphModel.addFlow(data);
graphModel.trigger("change:graph");
-
+
updateTime = Math.max(updateTime, data.submitTime);
updateTime = Math.max(updateTime, data.startTime);
updateTime = Math.max(updateTime, data.endTime);
-
+
if (window.location.hash) {
var hash = window.location.hash;
if (hash == "#jobslist") {
src/web/js/azkaban/view/flow.js 149(+87 -62)
diff --git a/src/web/js/azkaban/view/flow.js b/src/web/js/azkaban/view/flow.js
index 3934055..6ed8d89 100644
--- a/src/web/js/azkaban/view/flow.js
+++ b/src/web/js/azkaban/view/flow.js
@@ -1,12 +1,12 @@
/*
* 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
@@ -18,7 +18,7 @@ $.namespace('azkaban');
var handleJobMenuClick = function(action, el, pos) {
var jobid = el[0].jobid;
- var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" +
+ var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" +
flowId + "&job=" + jobid;
if (action == "open") {
window.location.href = requestURL;
@@ -35,7 +35,7 @@ azkaban.FlowTabView = Backbone.View.extend({
"click #executionsViewLink": "handleExecutionLinkClick",
"click #summaryViewLink": "handleSummaryLinkClick"
},
-
+
initialize: function(settings) {
var selectedView = settings.selectedView;
if (selectedView == "executions") {
@@ -45,26 +45,26 @@ azkaban.FlowTabView = Backbone.View.extend({
this.handleGraphLinkClick();
}
},
-
+
render: function() {
console.log("render graph");
},
-
+
handleGraphLinkClick: function(){
$("#executionsViewLink").removeClass("active");
$("#graphViewLink").addClass("active");
$('#summaryViewLink').removeClass('active');
-
+
$("#executionsView").hide();
$("#graphView").show();
$('#summaryView').hide();
},
-
+
handleExecutionLinkClick: function() {
$("#graphViewLink").removeClass("active");
$("#executionsViewLink").addClass("active");
$('#summaryViewLink').removeClass('active');
-
+
$("#graphView").hide();
$("#executionsView").show();
$('#summaryView').hide();
@@ -90,35 +90,35 @@ azkaban.ExecutionsView = Backbone.View.extend({
events: {
"click #pageSelection li": "handleChangePageSelection"
},
-
+
initialize: function(settings) {
this.model.bind('change:view', this.handleChangeView, this);
this.model.bind('render', this.render, this);
this.model.set({page: 1, pageSize: 16});
this.model.bind('change:page', this.handlePageChange, this);
},
-
+
render: function(evt) {
console.log("render");
// Render page selections
var tbody = $("#execTableBody");
tbody.empty();
-
+
var executions = this.model.get("executions");
for (var i = 0; i < executions.length; ++i) {
var row = document.createElement("tr");
-
+
var tdId = document.createElement("td");
var execA = document.createElement("a");
$(execA).attr("href", contextURL + "/executor?execid=" + executions[i].execId);
$(execA).text(executions[i].execId);
tdId.appendChild(execA);
row.appendChild(tdId);
-
+
var tdUser = document.createElement("td");
$(tdUser).text(executions[i].submitUser);
row.appendChild(tdUser);
-
+
var startTime = "-";
if (executions[i].startTime != -1) {
var startDateTime = new Date(executions[i].startTime);
@@ -128,7 +128,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
var tdStartTime = document.createElement("td");
$(tdStartTime).text(startTime);
row.appendChild(tdStartTime);
-
+
var endTime = "-";
var lastTime = executions[i].endTime;
if (executions[i].endTime != -1) {
@@ -142,11 +142,11 @@ azkaban.ExecutionsView = Backbone.View.extend({
var tdEndTime = document.createElement("td");
$(tdEndTime).text(endTime);
row.appendChild(tdEndTime);
-
+
var tdElapsed = document.createElement("td");
$(tdElapsed).text( getDuration(executions[i].startTime, lastTime));
row.appendChild(tdElapsed);
-
+
var tdStatus = document.createElement("td");
var status = document.createElement("div");
$(status).addClass("status");
@@ -160,22 +160,22 @@ azkaban.ExecutionsView = Backbone.View.extend({
tbody.append(row);
}
-
+
this.renderPagination(evt);
},
-
+
renderPagination: function(evt) {
var total = this.model.get("total");
total = total? total : 1;
var pageSize = this.model.get("pageSize");
var numPages = Math.ceil(total / pageSize);
-
+
this.model.set({"numPages": numPages});
var page = this.model.get("page");
-
+
//Start it off
$("#pageSelection .active").removeClass("active");
-
+
// Disable if less than 5
console.log("Num pages " + numPages)
var i = 1;
@@ -185,7 +185,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
for (; i <= 5; ++i) {
$("#page" + i).addClass("disabled");
}
-
+
// Disable prev/next if necessary.
if (page > 1) {
$("#previous").removeClass("disabled");
@@ -195,7 +195,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
else {
$("#previous").addClass("disabled");
}
-
+
if (page < numPages) {
$("#next")[0].page = page + 1;
$("#next").removeClass("disabled");
@@ -205,7 +205,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
$("#next")[0].page = page + 1;
$("#next").addClass("disabled");
}
-
+
// Selection is always in middle unless at barrier.
var startPage = 0;
var selectionPosition = 0;
@@ -235,14 +235,14 @@ azkaban.ExecutionsView = Backbone.View.extend({
for (var j = 0; j < 5; ++j) {
var realPage = startPage + j;
var elementId = "#page" + (j+1);
-
+
$(elementId)[0].page = realPage;
var a = $(elementId + " a");
a.text(realPage);
a.attr("href", "#page" + realPage);
}
},
-
+
handleChangePageSelection: function(evt) {
if ($(evt.currentTarget).hasClass("disabled")) {
return;
@@ -250,7 +250,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
var page = evt.currentTarget.page;
this.model.set({"page": page});
},
-
+
handleChangeView: function(evt) {
if (this.init) {
return;
@@ -259,23 +259,23 @@ azkaban.ExecutionsView = Backbone.View.extend({
this.handlePageChange(evt);
this.init = true;
},
-
+
handlePageChange: function(evt) {
var page = this.model.get("page") - 1;
var pageSize = this.model.get("pageSize");
var requestURL = contextURL + "/manager";
-
+
var model = this.model;
var requestData = {
- "project": projectName,
- "flow": flowId,
- "ajax": "fetchFlowExecutions",
- "start": page * pageSize,
+ "project": projectName,
+ "flow": flowId,
+ "ajax": "fetchFlowExecutions",
+ "start": page * pageSize,
"length": pageSize
};
var successHandler = function(data) {
model.set({
- "executions": data.executions,
+ "executions": data.executions,
"total": data.total
});
model.trigger("render");
@@ -289,11 +289,11 @@ azkaban.SummaryView = Backbone.View.extend({
events: {
'click #analyze-btn': 'fetchLastRun'
},
-
+
initialize: function(settings) {
this.model.bind('change:view', this.handleChangeView, this);
this.model.bind('render', this.render, this);
-
+
this.fetchDetails();
this.fetchSchedule();
this.model.trigger('render');
@@ -306,7 +306,7 @@ azkaban.SummaryView = Backbone.View.extend({
'project': projectName,
'flow': flowId
};
-
+
var model = this.model;
var successHandler = function(data) {
@@ -318,7 +318,7 @@ azkaban.SummaryView = Backbone.View.extend({
};
$.get(requestURL, requestData, successHandler, 'json');
},
-
+
fetchSchedule: function() {
var requestURL = contextURL + "/schedule"
var requestData = {
@@ -327,9 +327,34 @@ azkaban.SummaryView = Backbone.View.extend({
'flowId': flowId
};
var model = this.model;
+ var view = this;
var successHandler = function(data) {
model.set({'schedule': data.schedule});
model.trigger('render');
+ view.fetchSla();
+ };
+ $.get(requestURL, requestData, successHandler, 'json');
+ },
+
+ fetchSla: function() {
+ var schedule = this.model.get('schedule');
+ if (schedule == null || schedule.scheduleId == null) {
+ return;
+ }
+
+ var requestURL = contextURL + "/schedule"
+ var requestData = {
+ "scheduleId": schedule.scheduleId,
+ "ajax": "slaInfo"
+ };
+ var model = this.model;
+ var successHandler = function(data) {
+ if (data == null || data.settings == null || data.settings.length == 0) {
+ return;
+ }
+ schedule.slaOptions = true;
+ model.set({'schedule': schedule});
+ model.trigger('render');
};
$.get(requestURL, requestData, successHandler, 'json');
},
@@ -390,10 +415,10 @@ $(function() {
// Execution model has to be created before the window switches the tabs.
executionModel = new azkaban.ExecutionModel();
executionsView = new azkaban.ExecutionsView({
- el: $('#executionsView'),
+ el: $('#executionsView'),
model: executionModel
});
-
+
summaryModel = new azkaban.SummaryModel();
summaryView = new azkaban.SummaryView({
el: $('#summaryView'),
@@ -407,35 +432,35 @@ $(function() {
});
flowTabView = new azkaban.FlowTabView({
- el: $('#headertabs'),
- selectedView: selected
+ el: $('#headertabs'),
+ selectedView: selected
});
graphModel = new azkaban.GraphModel();
mainSvgGraphView = new azkaban.SvgGraphView({
- el: $('#svgDiv'),
- model: graphModel,
- rightClick: {
- "node": nodeClickCallback,
- "edge": edgeClickCallback,
- "graph": graphClickCallback
+ el: $('#svgDiv'),
+ model: graphModel,
+ rightClick: {
+ "node": nodeClickCallback,
+ "edge": edgeClickCallback,
+ "graph": graphClickCallback
}
});
-
+
jobsListView = new azkaban.JobListView({
- el: $('#joblist-panel'),
- model: graphModel,
+ el: $('#joblist-panel'),
+ model: graphModel,
contextMenuCallback: jobClickCallback
});
-
+
executionsTimeGraphView = new azkaban.TimeGraphView({
- el: $('#timeGraph'),
+ el: $('#timeGraph'),
model: executionModel,
modelField: 'executions'
});
-
+
slaView = new azkaban.ChangeSlaView({el:$('#sla-options')});
-
+
var requestURL = contextURL + "/manager";
// Set up the Flow options view. Create a new one every time :p
$('#executebtn').click(function() {
@@ -451,15 +476,15 @@ $(function() {
});
var requestData = {
- "project": projectName,
- "ajax": "fetchflowgraph",
+ "project": projectName,
+ "ajax": "fetchflowgraph",
"flow": flowId
};
var successHandler = function(data) {
console.log("data fetched");
graphModel.addFlow(data);
graphModel.trigger("change:graph");
-
+
// Handle the hash changes here so the graph finishes rendering first.
if (window.location.hash) {
var hash = window.location.hash;
@@ -470,7 +495,7 @@ $(function() {
flowTabView.handleSummaryLinkClick();
}
else if (hash == "#graph") {
- // Redundant, but we may want to change the default.
+ // Redundant, but we may want to change the default.
selected = "graph";
}
else {
src/web/js/azkaban/view/flow-stats.js 71(+56 -15)
diff --git a/src/web/js/azkaban/view/flow-stats.js b/src/web/js/azkaban/view/flow-stats.js
index 5f2919e..d991a4c 100644
--- a/src/web/js/azkaban/view/flow-stats.js
+++ b/src/web/js/azkaban/view/flow-stats.js
@@ -1,12 +1,12 @@
/*
* 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
@@ -21,11 +21,16 @@ azkaban.FlowStatsView = Backbone.View.extend({
events: {
},
+ histogram: true,
+
initialize: function(settings) {
this.model.bind('change:view', this.handleChangeView, this);
this.model.bind('render', this.render, this);
+ if (settings.histogram != null) {
+ this.histogram = settings.histogram;
+ }
},
-
+
render: function(evt) {
},
@@ -38,10 +43,10 @@ azkaban.FlowStatsView = Backbone.View.extend({
var requestData = {"execid": execId, "ajax":"fetchexecflow"};
var jobs = [];
var successHandler = function(data) {
- for (var i = 0; i < data.nodes.length; ++i) {
- var node = data.nodes[i];
- jobs.push(node.id);
- }
+ data.nodes.sort(function(a, b) {
+ return a.startTime - b.startTime;
+ });
+ jobs = data.nodes;
};
$.ajax({
url: requestURL,
@@ -127,7 +132,7 @@ azkaban.FlowStatsView = Backbone.View.extend({
}
}
if (str.indexOf('Xms') > -1) {
- if (str.length <= 4) {
+ if (str.length <= 4) {
continue;
}
var size = str.substring(4, str.length);
@@ -179,13 +184,13 @@ azkaban.FlowStatsView = Backbone.View.extend({
stats.fileBytesWritten.max = fileBytesWritten;
stats.fileBytesWritten.job = job;
}
-
+
var hdfsBytesRead = parseInt(fileSystemCounters['HDFS_BYTES_READ']);
if (hdfsBytesRead >= stats.hdfsBytesRead.max) {
stats.hdfsBytesRead.max = hdfsBytesRead;
stats.hdfsBytesRead.job = job;
}
-
+
var hdfsBytesWritten = parseInt(fileSystemCounters['HDFS_BYTES_WRITTEN']);
if (hdfsBytesWritten >= stats.hdfsBytesWritten.max) {
stats.hdfsBytesWritten.max = hdfsBytesWritten;
@@ -219,6 +224,8 @@ azkaban.FlowStatsView = Backbone.View.extend({
success: false,
message: null,
warnings: [],
+ durations: [],
+ histogram: this.histogram,
stats: {
mapSlots: {
max: 0,
@@ -275,18 +282,36 @@ azkaban.FlowStatsView = Backbone.View.extend({
}
};
+ var jobsAnalyzed = 0;
for (var i = 0; i < jobs.length; ++i) {
var job = jobs[i];
- var jobStats = this.fetchJobStats(job, execId);
+ var duration = job.endTime - job.startTime;
+ data.durations.push({
+ job: job.id,
+ duration: duration
+ });
+
+ var jobStats = this.fetchJobStats(job.id, execId);
if (jobStats.jobStats == null) {
data.warnings.push("No job stats available for job " + job.id);
continue;
}
for (var j = 0; j < jobStats.jobStats.length; ++j) {
- this.updateStats(jobStats.jobStats[j], data, job);
+ this.updateStats(jobStats.jobStats[j], data, job.id);
}
+ ++jobsAnalyzed;
+ }
+
+ // If no jobs were analyzed, then no jobs had any job stats available. In
+ // this case, display a No Flow Stats Available message.
+ if (jobsAnalyzed == 0) {
+ data.success = false;
+ data.message = "There were no job stats provided by any job.";
+ }
+ else {
+ this.finalizeStats(data);
}
- this.finalizeStats(data);
+
this.model.set({'data': data});
this.model.trigger('render');
},
@@ -300,14 +325,30 @@ azkaban.FlowStatsView = Backbone.View.extend({
view.display(out);
});
}
- else if (data.success == "false") {
+ else if (data.success == false) {
dust.render("flowstats-no-data", data, function(err, out) {
view.display(out);
});
}
else {
+ var histogram = this.histogram;
dust.render("flowstats", data, function(err, out) {
view.display(out);
+ if (histogram == true) {
+ var yLabelFormatCallback = function(y) {
+ var seconds = y / 1000.0;
+ return seconds.toString() + " s";
+ };
+
+ Morris.Bar({
+ element: "job-histogram",
+ data: data.durations,
+ xkey: "job",
+ ykeys: ["duration"],
+ labels: ["Duration"],
+ yLabelFormat: yLabelFormatCallback
+ });
+ }
});
}
},