azkaban-developers
Changes
src/less/bootstrap-azkaban.less 6(+5 -1)
src/tl/flowscheduling.tl 34(+34 -0)
src/tl/flowsummary.tl 169(+46 -123)
src/web/js/azkaban.flow.view.js 147(+80 -67)
Details
diff --git a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index ba37274..6099b5c 100644
--- a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -366,7 +366,8 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
break;
}
}
- } catch (ScheduleManagerException e) {
+ }
+ catch (ScheduleManagerException e) {
// TODO Auto-generated catch block
throw new ServletException(e);
}
@@ -395,7 +396,8 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
//project.info("Project removing by '" + user.getUserId() + "'");
try {
projectManager.removeProject(project, user);
- } catch (ProjectManagerException e) {
+ }
+ catch (ProjectManagerException e) {
this.setErrorMessageInCookie(resp, e.getMessage());
resp.sendRedirect(req.getRequestURI() + "?project=" + projectName);
return;
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index 3ac004b..c20fd66 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -23,8 +23,11 @@
<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-core-2.2.2.min.js"></script>
+
+ <script type="text/javascript" src="${context}/js/dust-core-2.2.2.min.js"></script>
<script type="text/javascript" src="${context}/js/flowsummary.js"></script>
+ <script type="text/javascript" src="${context}/js/flowscheduling.js"></script>
+
<script type="text/javascript" src="${context}/js/azkaban.date.utils.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.ajax.utils.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.common.utils.js"></script>
@@ -108,6 +111,7 @@
<ul class="nav nav-tabs" id="headertabs">
<li id="graphViewLink"><a href="#graph">Graph</a></li>
<li id="executionsViewLink"><a href="#executions">Executions</a></li>
+ <li id="schedulingViewLink"><a href="#scheduling">Scheduling</a></li>
<li id="summaryViewLink"><a href="#summary">Summary</a></li>
</ul>
@@ -145,12 +149,15 @@
</ul>
</div>
</div>
+
+ ## Scheduling view.
+
+ <div class="row" id="schedulingView">
+ </div>
## Summary view.
- <div class="row">
- <div class="col-lg-12" id="summaryView">
- </div>
+ <div class="row" id="summaryView">
</div><!-- /#summaryView -->
<div id="contextMenu">
src/less/bootstrap-azkaban.less 6(+5 -1)
diff --git a/src/less/bootstrap-azkaban.less b/src/less/bootstrap-azkaban.less
index f1afe32..3795278 100644
--- a/src/less/bootstrap-azkaban.less
+++ b/src/less/bootstrap-azkaban.less
@@ -26,11 +26,15 @@
}
// Flow summary.
-td.worksheet-key {
+td.flow-summary-key {
width: 25%;
font-weight: bold;
}
+td.flow-summary-value {
+ width: 25%;
+}
+
// Job table.
#all-jobs {
.tb-name {
src/tl/flowscheduling.tl 34(+34 -0)
diff --git a/src/tl/flowscheduling.tl b/src/tl/flowscheduling.tl
new file mode 100644
index 0000000..f8a01a1
--- /dev/null
+++ b/src/tl/flowscheduling.tl
@@ -0,0 +1,34 @@
+ <div class="col-lg-12">
+ <table class="table table-striped table-condensed table-bordered table-hover">
+ <thead>
+ <tr>
+ <!--th class="execid">Execution Id</th-->
+ <th>ID</th>
+ <th>Submitted By</th>
+ <th class="date">First Scheduled to Run</th>
+ <th class="date">Next Execution Time</th>
+ <th class="date">Repeats Every</th>
+ <th>Has SLA</th>
+ <th colspan="2" class="action ignoresort">Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ {#schedules}
+ <tr>
+ <td>{scheduleId}</td>
+ <td>{submitUser}</td>
+ <td>{firstSchedTime}</td>
+ <td>{nextExecTime}</td>
+ <td>{period}</td>
+ <td>{?slaOptions} true {:else} false {/slaOptions}</td>
+ <td><button type="button" id="removeSchedBtn" class="btn btn-sm btn-danger" onclick="removeSched({scheduleId})" >Remove Schedule</button></td>
+ <td><button type="button" id="addSlaBtn" class="btn btn-sm btn-primary" onclick="slaView.initFromSched({scheduleId}, '{flowName}')" >Set SLA</button></td>
+ </tr>
+ {:else}
+ <tr>
+ <td colspan="8">No scheduled flow found.</td>
+ </tr>
+ {/schedules}
+ </tbody>
+ </table>
+ </div>
src/tl/flowsummary.tl 169(+46 -123)
diff --git a/src/tl/flowsummary.tl b/src/tl/flowsummary.tl
index c9f71d3..94b5a82 100644
--- a/src/tl/flowsummary.tl
+++ b/src/tl/flowsummary.tl
@@ -1,136 +1,59 @@
- <div class="panel panel-default">
- <table class="table table-striped table-bordered table-condensed table-hover">
- <tbody>
- <tr>
- <td class="worksheet-key">Workflow name</td>
- <td>{general.flowId}</td>
- <td class="worksheet-key">Project name</td>
- <td>{general.projectName}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Workflow Hadoop User Name</td>
- <td>{general.user}</td>
- <td class="worksheet-key">Job Types Used</td>
- <td>{general.apis}</td>
- </tr>
- </tbody>
- </table>
- </div>
-
- <div class="panel panel-default">
- <div class="panel-heading">Scheduling</div>
- <table class="table table-striped table-bordered table-condensed table-hover">
- <tbody>
- <tr>
- <td class="worksheet-key">Recurrence</td>
- <td>{scheduling.schedule}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Scheduled Time</td>
- <td>{scheduling.launchTime}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Total Workflow Run Time (hours)</td>
- <td>{scheduling.totalFlowTime}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Expected Time of Completion</td>
- <td>{scheduling.expectedCompletionTime}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Max Permitted Delay</td>
- <td>{scheduling.maxPermittedDelay}</td>
- </tr>
- </tbody>
- </table>
- </div>
+ <div class="col-lg-12">
+ <table class="table table-striped table-bordered table-condensed table-hover">
+ <tbody>
+ <tr>
+ <td class="flow-summary-key">Workflow name</td>
+ <td class="flow-summary-value">{general.flowId}</td>
+ <td class="flow-summary-key">Project name</td>
+ <td class="flow-summary-value">{general.projectName}</td>
+ </tr>
+ <tr>
+ <td class="flow-summary-key">Workflow Hadoop User Name</td>
+ <td class="flow-summary-value">{general.user}</td>
+ <td class="flow-summary-key">Job Types Used</td>
+ <td class="flow-summary-value">{general.apis}</td>
+ </tr>
+ </tbody>
+ </table>
<div class="panel panel-default">
- <div class="panel-heading">Performance</div>
+ <div class="panel-heading">Last Run Stats</div>
+ {?lastRun}
<table class="table table-striped table-bordered table-condensed table-hover">
<tbody>
<tr>
- <td class="worksheet-key">Max Map Slots from Largest Job</td>
- <td>{scheduling.maxMapSlots}</td>
+ <td class="flow-summary-key">Max Map Slots from Largest Job</td>
+ <td>{lastRun.maxMapSlots}</td>
+ </tr>
+ <tr>
+ <td class="flow-summary-key">Max Reduce Slots from Largest Job</td>
+ <td>{lastRun.maxReduceSlots}</td>
</tr>
<tr>
- <td class="worksheet-key">Max Reduce Slots from Largest Job</td>
- <td>{scheduling.maxReduceSlots}</td>
+ <td class="flow-summary-key">Total Map Slots from All Jobs</td>
+ <td>{lastRun.totalMapSlots}</td>
+ </tr>
+ <tr>
+ <td class="flow-summary-key">Total Reduce Slots from All Jobs</td>
+ <td>{lastRun.totalReduceSlots}</td>
</tr>
<tr>
- <td class="worksheet-key">Total Reduce Slots from All Jobs</td>
- <td>{scheduling.totalReduceSlots}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Total Number of Jobs</td>
- <td>{scheduling.numJobs}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Longest Task Time</td>
- <td>{scheduling.longestTaskTime}</td>
+ <td class="flow-summary-key">Total Number of Jobs</td>
+ <td>{lastRun.numJobs}</td>
+ </tr>
+ <tr>
+ <td class="flow-summary-key">Longest Task Time</td>
+ <td>{lastRun.longestTaskTime}</td>
</tr>
</tbody>
</table>
+ {:else}
+ <div class="panel-body">
+ <div class="alert alert-info">
+ <h4>No last run stats available</h4>
+ <p>Last run stats requires at least one successful run of the flow.</p>
+ </div>
+ </div>
+ {/lastRun}
</div>
-
- <div class="panel panel-default">
- <div class="panel-heading">Resources</div>
- <table class="table table-striped table-bordered table-condensed table-hover">
- <tbody>
- <tr>
- <td class="worksheet-key">Is any of the code specifically multi-threaded?</td>
- <td colspan="3">{resources.multithreaded}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Do you 'fat jar' any hadoop-core jars?</td>
- <td colspan="3">{resources.fatJar}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Which job has the largest spill count?</td>
- <td>{resources.largestSpill.job}</td>
- <td class="worksheet-key">Largest spill count for any given task?</td>
- <td>{resources.largestSpill.count}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Is there a distributed cache in use?</td>
- <td>{resources.distributedCache.using}</td>
- <td class="worksheet-key">How big is the distributed cache?</td>
- <td>{resources.distributedCache.size}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Size of largest <code>-Xmx</code> value?</td>
- <td>{resources.largestXmx.size}</td>
- <td class="worksheet-key">If this is above 1G, please explain why</td>
- <td>{resources.largestXmx.reason}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Do any jobs use <code>-Xms</code>?</td>
- <td>{resources.xms.using}</td>
- <td class="worksheet-key">If so, why?</td>
- <td>{resources.xms.reason}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Is <em>intermediate compression</em> specifically turned on?</td>
- <td>{resources.intermediateCompression.on}</td>
- <td class="worksheet-key">If so, which codec</td>
- <td>{resources.intermediateCompression.codec}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Are there combiners in use?</td>
- <td colspan="3">{resources.combiners}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Size of largest <code>mapred.job.map.memory.mb</code></td>
- <td>{resources.largestMapredJobMapMemoryMb.size}</td>
- <td class="worksheet-key">Used by job</td>
- <td>{resources.largestMapredJobMapMemoryMb.job}</td>
- </tr>
- <tr>
- <td class="worksheet-key">Size of largest <code>mapred.job.reduce.memory.mb</code></td>
- <td>{resources.largestMapredJobReduceMemoryMb.size}</td>
- <td class="worksheet-key">Used by job</td>
- <td>{resources.largestMapredJobMapMemoryMb.job}</td>
- </tr>
- </tbody>
- </table>
- </div>
+ </div><!-- /.col-lg-12 -->
src/web/js/azkaban.flow.view.js 147(+80 -67)
diff --git a/src/web/js/azkaban.flow.view.js b/src/web/js/azkaban.flow.view.js
index 8d8eb6e..9bb3d12 100644
--- a/src/web/js/azkaban.flow.view.js
+++ b/src/web/js/azkaban.flow.view.js
@@ -46,6 +46,7 @@ azkaban.FlowTabView = Backbone.View.extend({
events: {
"click #graphViewLink": "handleGraphLinkClick",
"click #executionsViewLink": "handleExecutionLinkClick",
+ "click #schedulingViewLink": "handleSchedulingLinkClick",
"click #summaryViewLink": "handleSummaryLinkClick"
},
@@ -66,31 +67,49 @@ azkaban.FlowTabView = Backbone.View.extend({
handleGraphLinkClick: function(){
$("#executionsViewLink").removeClass("active");
$("#graphViewLink").addClass("active");
+ $('#schedulingViewLink').removeClass('active');
$('#summaryViewLink').removeClass('active');
$("#executionsView").hide();
$("#graphView").show();
+ $('#schedulingView').hide();
$('#summaryView').hide();
},
handleExecutionLinkClick: function() {
$("#graphViewLink").removeClass("active");
$("#executionsViewLink").addClass("active");
+ $('#schedulingViewLink').removeClass('active');
$('#summaryViewLink').removeClass('active');
$("#graphView").hide();
$("#executionsView").show();
+ $('#schedulingView').hide();
$('#summaryView').hide();
executionModel.trigger("change:view");
},
- handleSummaryLinkClick: function() {
+ handleSchedulingLinkClick: function() {
$('#graphViewLink').removeClass('active');
$('#executionsViewLink').removeClass('active');
+ $('#schedulingViewLink').addClass('active');
+ $('#summaryViewLink').removeClass('active');
+
+ $('#graphView').hide();
+ $('#executionsView').hide();
+ $('#schedulingView').show();
+ $('#summaryView').hide();
+ },
+
+ handleSummaryLinkClick: function() {
+ $('#graphViewLink').removeClass('active');
+ $('#executionsViewLink').removeClass('active');
+ $('#schedulingViewLink').removeClass('active');
$('#summaryViewLink').addClass('active');
$('#graphView').hide();
$('#executionsView').hide();
+ $('#schedulingView').hide();
$('#summaryView').show();
},
});
@@ -181,7 +200,7 @@ azkaban.ExecutionsView = Backbone.View.extend({
var total = this.model.get("total");
total = total? total : 1;
var pageSize = this.model.get("pageSize");
- var numPages = Math.ceil(total/pageSize);
+ var numPages = Math.ceil(total / pageSize);
this.model.set({"numPages": numPages});
var page = this.model.get("page");
@@ -292,33 +311,65 @@ azkaban.ExecutionsView = Backbone.View.extend({
}
});
+var schedulingView;
+azkaban.SchedulingView = Backbone.View.extend({
+ events: {
+ },
+
+ initialize: function(settings) {
+ console.log("schedulingView initialize");
+ this.model.bind('change:view', this.handleChangeView, this);
+ this.model.bind('render', this.render, this);
+ var schedules = [
+ {
+ scheduleId: "0",
+ submitUser: "azkaban",
+ firstSchedTime: "1",
+ nextExecTime: "2",
+ period: "3",
+ }
+ ];
+
+ this.model.set({
+ 'schedules': schedules
+ });
+ this.model.trigger('render');
+ },
+
+ handleChangeView: function(evt) {
+ console.log("schedulingView handleChangeView");
+ },
+
+ render: function(evt) {
+ console.log("schedulingView render");
+ var data = {
+ schedules: this.model.get('schedules')
+ };
+ dust.render("flowscheduling", data, function(err, out) {
+ console.log(err);
+ $('#schedulingView').html(out);
+ });
+ },
+});
+
var summaryView;
azkaban.SummaryView = Backbone.View.extend({
events: {
- "click": "closeEditingTarget",
- "click table .editable": "handleEditField"
},
initialize: function(settings) {
console.log("summaryView initialize");
var general = {
- flowName: "",
- flowDescription: "",
projectName: projectName,
flowId: flowId
};
-
- var scheduling = {};
- var resources = {};
- var io = {};
+ var lastRun = {};
this.model.bind('change:view', this.handleChangeView, this);
this.model.bind('render', this.render, this);
this.model.set({
'general': general,
- 'scheduling': scheduling,
- 'resources': resources,
- 'io': io
+ 'lastRun': lastRun
});
this.model.trigger('render');
},
@@ -327,61 +378,11 @@ azkaban.SummaryView = Backbone.View.extend({
console.log("summaryView handleChangeView");
},
- handleEditField: function(evt) {
- var curTarget = evt.currentTarget;
- console.log("summaryView handleEditField");
- if (this.editingTarget != curTarget) {
- this.closeEditingTarget(evt);
-
- var text = $(curTarget).children('.spanValue').text();
- $(curTarget).empty();
-
- var input = document.createElement('input');
- $(input).attr('type', 'text');
- $(input).css('width', '100%');
- $(input).val(text);
-
- $(curTarget).addClass('editing');
- $(curTarget).append(input);
- $(input).focus();
- var obj = this;
- $(input).keypress(function(evt) {
- if (evt.which == 13) {
- obj.closeEditingTarget(evt);
- }
- });
- this.editingTarget = curTarget;
- }
- evt.preventDefault();
- evt.stopPropagation();
- },
-
- closeEditingTarget: function(evt) {
- console.log("summaryView closeEditingTarget");
- if (this.editingTarget != null &&
- this.editingTarget != evt.target &&
- this.editingTarget != evt.target.myparent) {
- var input = $(this.editingTarget).children("input")[0];
- var text = $(input).val();
- $(input).remove();
-
- var valueData = document.createElement("span");
- $(valueData).addClass("spanValue");
- $(valueData).text(text);
-
- $(this.editingTarget).removeClass("editing");
- $(this.editingTarget).append(valueData);
- valueData.myparent = this.editingTarget;
- this.editingTarget = null;
- }
- },
-
render: function(evt) {
console.log("summaryView render");
var data = {
general: this.model.get('general'),
- scheduling: this.model.get('scheduling'),
- resources: this.model.get('resources')
+ lastRun: this.model.get('lastRun')
};
dust.render("flowsummary", data, function(err, out) {
$('#summaryView').html(out);
@@ -441,6 +442,9 @@ azkaban.GraphModel = Backbone.Model.extend({});
var executionModel;
azkaban.ExecutionModel = Backbone.Model.extend({});
+var schedulingModel;
+azkaban.SchedulingModel = Backbone.Model.extend({});
+
var summaryModel;
azkaban.SummaryModel = Backbone.Model.extend({});
@@ -454,12 +458,20 @@ $(function() {
el: $('#executionsView'),
model: executionModel
});
- summaryModel = new azkaban.SummaryModel();
+
+ schedulingModel = new azkaban.SchedulingModel();
+ schedulingView = new azkaban.SchedulingView({
+ el: $('#schedulingView'),
+ model: schedulingModel
+ });
+
+ summaryModel = new azkaban.SummaryModel();
summaryView = new azkaban.SummaryView({
el: $('#summaryView'),
model: summaryModel
});
- flowTabView = new azkaban.FlowTabView({
+
+ flowTabView = new azkaban.FlowTabView({
el: $('#headertabs'),
selectedView: selected
});
@@ -474,7 +486,8 @@ $(function() {
"graph": exGraphClickCallback
}
});
- jobsListView = new azkaban.JobListView({
+
+ jobsListView = new azkaban.JobListView({
el: $('#jobList'),
model: graphModel,
contextMenuCallback: exJobClickCallback