azkaban-aplcache
Changes
src/java/azkaban/flow/Flow.java 22(+22 -0)
src/web/css/azkaban.css 63(+35 -28)
src/web/js/azkaban.exflow.options.view.js 215(+207 -8)
src/web/js/azkaban.exflow.view.js 7(+5 -2)
src/web/js/azkaban.flow.graph.view.js 21(+13 -8)
src/web/js/azkaban.flow.job.view.js 5(+3 -2)
src/web/js/azkaban.flow.view.js 404(+9 -395)
Details
diff --git a/src/java/azkaban/executor/ExecutorManager.java b/src/java/azkaban/executor/ExecutorManager.java
index b6fd420..a48a9c3 100644
--- a/src/java/azkaban/executor/ExecutorManager.java
+++ b/src/java/azkaban/executor/ExecutorManager.java
@@ -159,14 +159,15 @@ public class ExecutorManager {
return flows;
}
- public boolean isFlowRunning(String projectId, String flowId) {
+ public List<ExecutableFlow> getFlowRunningFlows(String projectId, String flowId) {
+ ArrayList<ExecutableFlow> list = new ArrayList<ExecutableFlow>();
for (ExecutableFlow flow: getRunningFlows()) {
if (flow.getProjectId().equals(projectId) && flow.getFlowId().equals(flowId)) {
- return true;
+ list.add(flow);
}
}
- return false;
+ return list;
}
public List<ExecutableFlow> getRunningFlows() {
src/java/azkaban/flow/Flow.java 22(+22 -0)
diff --git a/src/java/azkaban/flow/Flow.java b/src/java/azkaban/flow/Flow.java
index aaf206a..caae883 100644
--- a/src/java/azkaban/flow/Flow.java
+++ b/src/java/azkaban/flow/Flow.java
@@ -25,6 +25,8 @@ public class Flow {
private HashMap<String, Set<Edge>> inEdges = new HashMap<String, Set<Edge>>();
private HashMap<String, FlowProps> flowProps = new HashMap<String, FlowProps>();
+ private List<String> failureEmail = new ArrayList<String>();
+ private List<String> successEmail = new ArrayList<String>();
private ArrayList<String> errors;
private boolean isLayedOut = false;
@@ -78,6 +80,22 @@ public class Flow {
return nodes.get(nodeId);
}
+ public List<String> getSuccessEmails() {
+ return successEmail;
+ }
+
+ public List<String> getFailureEmails() {
+ return failureEmail;
+ }
+
+ public void addSuccessEmails(Collection<String> emails) {
+ successEmail.addAll(emails);
+ }
+
+ public void addFailureEmails(Collection<String> emails) {
+ failureEmail.addAll(emails);
+ }
+
public int getNumLevels() {
return numLevels;
}
@@ -182,6 +200,8 @@ public class Flow {
flowObj.put("props", objectizeProperties());
flowObj.put("nodes", objectizeNodes());
flowObj.put("edges", objectizeEdges());
+ flowObj.put("failure.email", failureEmail);
+ flowObj.put("success.email", successEmail);
flowObj.put("layedout", isLayedOut);
if (errors != null) {
flowObj.put("errors", errors);
@@ -246,6 +266,8 @@ public class Flow {
List<Edge> edges = loadEdgeFromObjects(edgeList, nodes);
flow.addAllEdges(edges);
+ flow.failureEmail = (List<String>)flowObject.get("failure.email");
+ flow.successEmail = (List<String>)flowObject.get("success.email");
return flow;
}
diff --git a/src/java/azkaban/scheduler/ScheduleManager.java b/src/java/azkaban/scheduler/ScheduleManager.java
index f5eeb75..742bf42 100644
--- a/src/java/azkaban/scheduler/ScheduleManager.java
+++ b/src/java/azkaban/scheduler/ScheduleManager.java
@@ -204,7 +204,7 @@ public class ScheduleManager {
private void saveSchedule() {
loader.saveSchedule(getSchedule());
}
-
+
/**
* Thread that simply invokes the running of flows when the schedule is
* ready.
diff --git a/src/java/azkaban/utils/DirectoryFlowLoader.java b/src/java/azkaban/utils/DirectoryFlowLoader.java
index ff1cca2..7c80368 100644
--- a/src/java/azkaban/utils/DirectoryFlowLoader.java
+++ b/src/java/azkaban/utils/DirectoryFlowLoader.java
@@ -5,6 +5,7 @@ import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -204,6 +205,16 @@ public class DirectoryFlowLoader {
for (Node base: nodeMap.values()) {
if (!nonRootNodes.contains(base.getId())) {
Flow flow = new Flow(base.getId());
+ Props jobProp = jobPropsMap.get(base.getId());
+
+ // Dedup with sets
+ @SuppressWarnings("unchecked")
+ Set<String> successEmail = new HashSet<String>(jobProp.getStringList("success.emails", Collections.EMPTY_LIST));
+ @SuppressWarnings("unchecked")
+ Set<String> failureEmail = new HashSet<String>(jobProp.getStringList("failure.emails", Collections.EMPTY_LIST));
+ flow.addFailureEmails(failureEmail);
+ flow.addSuccessEmails(successEmail);
+
flow.addAllFlowProperties(flowPropsList);
constructFlow(flow, base, visitedNodes);
flow.initialize();
diff --git a/src/java/azkaban/webapp/servlet/ExecutorServlet.java b/src/java/azkaban/webapp/servlet/ExecutorServlet.java
index 0aaf143..29745ef 100644
--- a/src/java/azkaban/webapp/servlet/ExecutorServlet.java
+++ b/src/java/azkaban/webapp/servlet/ExecutorServlet.java
@@ -22,15 +22,19 @@ import azkaban.flow.Flow;
import azkaban.project.Project;
import azkaban.project.ProjectManager;
import azkaban.project.ProjectManagerException;
+import azkaban.scheduler.ScheduleManager;
+import azkaban.scheduler.ScheduledFlow;
import azkaban.user.Permission;
import azkaban.user.User;
import azkaban.user.Permission.Type;
+import azkaban.utils.Utils;
import azkaban.webapp.session.Session;
public class ExecutorServlet extends LoginAbstractAzkabanServlet {
private static final long serialVersionUID = 1L;
private ProjectManager projectManager;
private ExecutorManager executorManager;
+ private ScheduleManager scheduleManager;
private static final int STRING_BUFFER_SIZE = 1024*5;
@Override
@@ -38,6 +42,7 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
super.init(config);
projectManager = this.getApplication().getProjectManager();
executorManager = this.getApplication().getExecutorManager();
+ scheduleManager = this.getApplication().getScheduleManager();
}
@Override
@@ -227,6 +232,11 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
String flowName = getParam(req, "flow");
ajaxIsFlowRunning(req, resp, ret, session.getUser(), projectName, flowName);
}
+ else if (ajaxName.equals("flowInfo")) {
+ String projectName = getParam(req, "project");
+ String flowName = getParam(req, "flow");
+ ajaxFetchFlowInfo(req, resp, ret, session.getUser(), projectName, flowName);
+ }
else {
String projectName = getParam(req, "project");
@@ -315,7 +325,35 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
}
}
-
+ private void ajaxFetchFlowInfo(HttpServletRequest req, HttpServletResponse resp, HashMap<String, Object> ret, User user, String projectId, String flowId) throws ServletException {
+ Project project = getProjectAjaxByPermission(ret, projectId, user, Type.READ);
+ if (project == null) {
+ return;
+ }
+
+ Flow flow = project.getFlow(flowId);
+ if (flow == null) {
+ ret.put("error", "Error loading flow. Flow " + flowId + " doesn't exist in " + projectId);
+ return;
+ }
+
+ ret.put("successEmails", flow.getSuccessEmails());
+ ret.put("failureEmails", flow.getFailureEmails());
+ ret.put("running", executorManager.getFlowRunningFlows(projectId, flowId));
+
+ ScheduledFlow sflow = null;
+ for (ScheduledFlow schedFlow: scheduleManager.getSchedule()) {
+ if (schedFlow.getProjectId().equals(projectId) && schedFlow.getFlowId().equals(flowId)) {
+ sflow = schedFlow;
+ break;
+ }
+ }
+
+ if (sflow != null) {
+ ret.put("scheduled", sflow.getNextExecTime().getMillis());
+ }
+ }
+
private void ajaxCancelFlow(HttpServletRequest req, HttpServletResponse resp, HashMap<String, Object> ret, User user, ExecutableFlow exFlow) throws ServletException{
Project project = getProjectAjaxByPermission(ret, exFlow.getProjectId(), user, Type.EXECUTE);
if (project == null) {
@@ -335,7 +373,7 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
return;
}
- ret.put("running", executorManager.isFlowRunning(projectId, flowId));
+ ret.put("running", executorManager.getFlowRunningFlows(projectId, flowId));
}
private void ajaxRestartFlow(HttpServletRequest req, HttpServletResponse resp, HashMap<String, Object> ret, User user, ExecutableFlow exFlow) throws ServletException{
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index f8096c1..68c06e0 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -201,20 +201,20 @@
</div>
<div class="optionsPane">
<div id="generalPanel" class="generalPanel panel">
- <div>
+ <div id="completeActions">
<h4>Completion Actions</h4>
<dl>
<dt>Failure Action</dt>
<dd>
<select name="failureAction">
- <option value="finishCurrent">Finish Current</option>
+ <option value="finishCurrent">Finish Current Running</option>
<option value="cancelImmediately">Cancel All</option>
<option value="finishPossible">Finish All Possible</option>
</select>
</dd>
<dt>Failure Email</dt>
<dd>
- <textarea></textarea>
+ <textarea id="failureEmails"></textarea>
</dd>
<dt>Notify on Failure</dt>
<dd>
@@ -223,7 +223,13 @@
</dd>
<dt>Success Email</dt>
<dd>
- <textarea></textarea>
+ <textarea id="successEmails"></textarea>
+ </dd>
+ <dt>Executing Job</dt>
+ <dd id="executingJob">
+ <input id="ignore" class="radio" type="radio" name="concurrent" value="ignore" checked /><label class="radioLabel" for="ignore">Run Concurrently</label>
+ <input id="pipeline" class="radio" type="radio" name="concurrent" value="pipeline" /><label class="radioLabel" for="pipeline">Pipeline</label>
+ <input id="queue" class="radio" type="radio" name="concurrent" value="queue" /><label class="radioLabel" for="queue">Queue Job</label>
</dd>
</dl>
</div>
@@ -245,15 +251,17 @@
</div>
</div>
<div id="graphPanel" class="graphPanel panel">
- <div class="jobList">
- <div id="filterList2">
- <input id="filter2" placeholder=" Job Filter" />
+ <div id="jobListCustom" class="jobList">
+ <div class="filterList">
+ <input class="filter" placeholder=" Job Filter" />
</div>
- <div id="list2">
+ <div class="list">
</div>
- <div id="resetPanZoomBtn2" class="btn5" >Reset Pan Zoom</div>
+ <div class="btn5 resetPanZoomBtn" >Reset Pan Zoom</div>
</div>
- <div class="svgDiv" >
+ <div id="svgDivCustom" class="svgDiv" >
+ <svg class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
+ </svg>
</div>
</div>
</div>
@@ -265,7 +273,12 @@
</div>
#end
- <ul id="jobMenu" class="contextMenu flowSubmenu">
+ <ul id="jobMenu" class="contextMenu">
+ <li class="open"><a href="#open">Open...</a></li>
+ <li class="openwindow"><a href="#openwindow">Open in New Window...</a></li>
+ </ul>
+
+ <ul id="disableJobMenu" class="contextMenu flowSubmenu">
<li class="open"><a href="#open">Open...</a></li>
<li class="openwindow"><a href="#openwindow">Open in New Window...</a></li>
<li id="disable" class="disable separator"><a href="#disable">Disable</a><div id="disableArrow" class="context-sub-icon"></div></li>
src/web/css/azkaban.css 63(+35 -28)
diff --git a/src/web/css/azkaban.css b/src/web/css/azkaban.css
index 2d3fdb1..20d62f8 100644
--- a/src/web/css/azkaban.css
+++ b/src/web/css/azkaban.css
@@ -1280,14 +1280,14 @@ tr:hover td {
margin-left: 3px;
}
-#svgGraph {
+.svgGraph {
width: 100%;
height: 100%;
background: #fff;
}
-#svgDiv {
+.svgDiv {
position: absolute;
top: 0px;
right: 0px;
@@ -1295,16 +1295,8 @@ tr:hover td {
bottom: 0px;
}
-#svgDiv.svgDivStaging {
- top: 30px;
-}
-
-#svgDivModal {
- position: absolute;
- top: 0px;
- right: 0px;
- left: 260px;
- height: 100%;
+#graphPanel {
+ background-color: #F0F0F0;
}
#executing-options {
@@ -1314,10 +1306,19 @@ tr:hover td {
bottom: 40px;
}
+#scheduled {
+
+}
+
+.radioLabel.disabled {
+ opacity: 0.3;
+}
+
#executing-options .svgDiv {
position: absolute;
background-color: #CCC;
- left: 260px;
+ padding: 1px;
+ left: 270px;
right: 0px;
top: 0px;
bottom: 0px;
@@ -1325,10 +1326,15 @@ tr:hover td {
#executing-options .jobList {
position: absolute;
- width: 260px;
+ width: 255px;
top: 0px;
bottom: 0px;
- background-color: #F00;
+ padding: 5px;
+ background-color: #F0F0F0;
+}
+
+#executing-options .list {
+ width: 255px;
}
#executing-options ul.optionsPicker {
@@ -1477,19 +1483,6 @@ tr:hover td {
width: 250px;
}
-#list {
- position: absolute;
- background-color: #fff;
- margin-right: 10px;
- border: solid;
- border-color: #CCC;
- border-width: 1px;
- overflow: auto;
- top: 26px;
- width: 100%;
- bottom: 120px;
-}
-
.resetPanZoomBtn {
position: absolute;
bottom: 90px;
@@ -1512,6 +1505,20 @@ tr:hover td {
width: 100%;
}
+
+.list {
+ position: absolute;
+ background-color: #fff;
+ margin-right: 10px;
+ border: solid;
+ border-color: #CCC;
+ border-width: 1px;
+ overflow: auto;
+ top: 26px;
+ width: 100%;
+ bottom: 120px;
+}
+
.list ul {
white-space: nowrap
}
src/web/js/azkaban.exflow.options.view.js 215(+207 -8)
diff --git a/src/web/js/azkaban.exflow.options.view.js b/src/web/js/azkaban.exflow.options.view.js
index 1c19104..a9a5034 100644
--- a/src/web/js/azkaban.exflow.options.view.js
+++ b/src/web/js/azkaban.exflow.options.view.js
@@ -1,4 +1,53 @@
var executeFlowView;
+var customSvgGraphView;
+var customJobListView;
+var cloneModel;
+
+azkaban.ContextMenu = Backbone.View.extend({
+ events : {
+ "click #disableArrow" : "handleDisabledClick",
+ "click #enableArrow" : "handleEnabledClick"
+ },
+ initialize: function(settings) {
+ $('#disableSub').hide();
+ $('#enableSub').hide();
+ },
+ handleEnabledClick: function(evt) {
+ if(evt.stopPropagation) {
+ evt.stopPropagation();
+ }
+ evt.cancelBubble=true;
+
+ if (evt.currentTarget.expanded) {
+ evt.currentTarget.expanded=false;
+ $('#enableArrow').removeClass('collapse');
+ $('#enableSub').hide();
+ }
+ else {
+ evt.currentTarget.expanded=true;
+ $('#enableArrow').addClass('collapse');
+ $('#enableSub').show();
+ }
+ },
+ handleDisabledClick: function(evt) {
+ if(evt.stopPropagation) {
+ evt.stopPropagation();
+ }
+ evt.cancelBubble=true;
+
+ if (evt.currentTarget.expanded) {
+ evt.currentTarget.expanded=false;
+ $('#disableArrow').removeClass('collapse');
+ $('#disableSub').hide();
+ }
+ else {
+ evt.currentTarget.expanded=true;
+ $('#disableArrow').addClass('collapse');
+ $('#disableSub').show();
+ }
+ }
+});
+
azkaban.ExecuteFlowView = Backbone.View.extend({
events : {
"click" : "closeEditingTarget",
@@ -12,14 +61,36 @@ azkaban.ExecuteFlowView = Backbone.View.extend({
"click table .editable": "handleEditColumn",
"click table .removeIcon": "handleRemoveColumn"
},
- initialize: function(evt) {
- $('#executebtn').click( function() {
- $('#modalBackground').show();
- $('#executing-options').show();
- });
-
+ initialize: function(setting) {
+ this.contextMenu = new azkaban.ContextMenu({el:$('#disableJobMenu')});
this.handleGeneralOptionsSelect();
},
+ show: function() {
+ this.handleGeneralOptionsSelect();
+ $('#modalBackground').show();
+ $('#executing-options').show();
+
+ var executeURL = contextURL + "/executor";
+ $.get(
+ executeURL,
+ {"project": projectName, "ajax":"flowInfo", "flow":flowName},
+ function(data) {
+ if (data.error) {
+ alert(data.error);
+ }
+ else {
+ $('#successEmails').val(data.successEmails);
+ $('#failureEmails').val(data.failureEmails);
+
+ if (data.running.length == 0) {
+ $(".radio").attr("disabled", "disabled");
+ $(".radioLabel").addClass("disabled", "disabled");
+ }
+ }
+ },
+ "json"
+ );
+ },
handleCancelExecution: function(evt) {
var executeURL = contextURL + "/executor";
$('#modalBackground').hide();
@@ -38,12 +109,24 @@ azkaban.ExecuteFlowView = Backbone.View.extend({
$('#graphPanel').show();
$('#generalPanel').hide();
+
+ if (this.flowSetup) {
+ return;
+ }
+
+ this.cloneModel = this.model.clone();
+ cloneModel = this.cloneModel;
+ customSvgGraphView = new azkaban.SvgGraphView({el:$('#svgDivCustom'), model: this.cloneModel, rightClick: {id: 'disableJobMenu', callback: this.handleDisableMenuClick}});
+ customJobsListView = new azkaban.JobListView({el:$('#jobListCustom'), model: this.cloneModel, rightClick: {id: 'disableJobMenu', callback: this.handleDisableMenuClick}});
+ this.cloneModel.trigger("change:graph");
+
+ this.flowSetup = true;
},
handleExecuteFlow: function(evt) {
var executeURL = contextURL + "/executor";
$.get(
executeURL,
- {"project": projectName, "ajax":"executeFlow", "flow":flowName, "disabled":graphModel.get("disabled")},
+ {"project": projectName, "ajax":"executeFlow", "flow":flowName, "disabled":this.cloneModel.get("disabled")},
function(data) {
if (data.error) {
alert(data.error);
@@ -134,5 +217,121 @@ azkaban.ExecuteFlowView = Backbone.View.extend({
},
addRowData : function(evt) {
- }
+ },
+ handleDisableMenuClick : function(action, el, pos) {
+ var jobid = el[0].jobid;
+ var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowName + "&job=" + jobid;
+ if (action == "open") {
+ window.location.href = requestURL;
+ }
+ else if(action == "openwindow") {
+ window.open(requestURL);
+ }
+ else if(action == "disable") {
+ var disabled = cloneModel.get("disabled");
+
+ disabled[jobid] = true;
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if (action == "disableParents") {
+ var disabled = cloneModel.get("disabled");
+ var nodes = cloneModel.get("nodes");
+ var inNodes = nodes[jobid].inNodes;
+
+ if (inNodes) {
+ for (var key in inNodes) {
+ disabled[key] = true;
+ }
+ }
+
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if (action == "disableChildren") {
+ var disabled = cloneModel.get("disabled");
+ var nodes = cloneModel.get("nodes");
+ var outNodes = nodes[jobid].outNodes;
+
+ if (outNodes) {
+ for (var key in outNodes) {
+ disabled[key] = true;
+ }
+ }
+
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if (action == "disableAncestors") {
+ var disabled = cloneModel.get("disabled");
+ var nodes = cloneModel.get("nodes");
+
+ recurseAllAncestors(nodes, disabled, jobid, true);
+
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if (action == "disableDescendents") {
+ var disabled = cloneModel.get("disabled");
+ var nodes = cloneModel.get("nodes");
+
+ recurseAllDescendents(nodes, disabled, jobid, true);
+
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if(action == "enable") {
+ var disabled = cloneModel.get("disabled");
+
+ disabled[jobid] = false;
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if (action == "enableParents") {
+ var disabled = cloneModel.get("disabled");
+ var nodes = cloneModel.get("nodes");
+ var inNodes = nodes[jobid].inNodes;
+
+ if (inNodes) {
+ for (var key in inNodes) {
+ disabled[key] = false;
+ }
+ }
+
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if (action == "enableChildren") {
+ var disabled = cloneModel.get("disabled");
+ var nodes = cloneModel.get("nodes");
+ var outNodes = nodes[jobid].outNodes;
+
+ if (outNodes) {
+ for (var key in outNodes) {
+ disabled[key] = false;
+ }
+ }
+
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if (action == "enableAncestors") {
+ var disabled = cloneModel.get("disabled");
+ var nodes = cloneModel.get("nodes");
+
+ recurseAllAncestors(nodes, disabled, jobid, false);
+
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ else if (action == "enableDescendents") {
+ var disabled = cloneModel.get("disabled");
+ var nodes = cloneModel.get("nodes");
+
+ recurseAllDescendents(nodes, disabled, jobid, false);
+
+ cloneModel.set({disabled: disabled});
+ cloneModel.trigger("change:disabled");
+ }
+ }
});
\ No newline at end of file
src/web/js/azkaban.exflow.view.js 7(+5 -2)
diff --git a/src/web/js/azkaban.exflow.view.js b/src/web/js/azkaban.exflow.view.js
index ff1cb39..318e514 100644
--- a/src/web/js/azkaban.exflow.view.js
+++ b/src/web/js/azkaban.exflow.view.js
@@ -508,13 +508,16 @@ $(function() {
graphModel = new azkaban.GraphModel();
logModel = new azkaban.LogModel();
flowTabView = new azkaban.FlowTabView({el:$( '#headertabs'), model: graphModel});
- svgGraphView = new azkaban.SvgGraphView({el:$('#svgDiv'), model: graphModel});
- jobsListView = new azkaban.JobListView({el:$('#jobList'), model: graphModel});
+ svgGraphView = new azkaban.SvgGraphView({el:$('#svgDiv'), model: graphModel, rightClick: {id: 'jobMenu', callback: handleJobMenuClick}});
+ jobsListView = new azkaban.JobListView({el:$('#jobList'), model: graphModel, rightClick: {id: 'jobMenu', callback: handleJobMenuClick}});
statusView = new azkaban.StatusView({el:$('#flow-status'), model: graphModel});
flowLogView = new azkaban.FlowLogView({el:$('#flowLogView'), model: logModel});
executionListView = new azkaban.ExecutionListView({el: $('#jobListView'), model:graphModel});
var requestURL = contextURL + "/executor";
+ // This is to set up the execution flow
+
+
ajaxCall(
requestURL,
{"execid": execId, "ajax":"fetchexecflow"},
src/web/js/azkaban.flow.graph.view.js 21(+13 -8)
diff --git a/src/web/js/azkaban.flow.graph.view.js b/src/web/js/azkaban.flow.graph.view.js
index 75b3096..33db1ec 100644
--- a/src/web/js/azkaban.flow.graph.view.js
+++ b/src/web/js/azkaban.flow.graph.view.js
@@ -9,7 +9,8 @@ azkaban.SvgGraphView = Backbone.View.extend({
this.model.bind('resetPanZoom', this.resetPanZoom, this);
this.model.bind('change:update', this.handleStatusUpdate, this);
this.model.bind('change:disabled', this.handleDisabledChange, this);
-
+ this.model.bind('change:updateAll', this.handleUpdateAllStatus, this);
+
this.svgns = "http://www.w3.org/2000/svg";
this.xlinksn = "http://www.w3.org/1999/xlink";
@@ -17,6 +18,8 @@ azkaban.SvgGraphView = Backbone.View.extend({
var svg = $(this.el).find('svg')[0];
this.svgGraph = svg;
+ this.contextMenu = settings.rightClick;
+
var gNode = document.createElementNS(this.svgns, 'g');
svg.appendChild(gNode);
this.mainG = gNode;
@@ -140,15 +143,17 @@ azkaban.SvgGraphView = Backbone.View.extend({
var updateNode = updateData.nodes[i];
var g = this.gNodes[updateNode.id];
-
- for (var j = 0; j < statusList.length; ++j) {
- var status = statusList[j];
- removeClass(g, status);
- }
+ this.handleRemoveAllStatus(g);
addClass(g, updateNode.status);
}
},
+ handleRemoveAllStatus: function(gNode) {
+ for (var j = 0; j < statusList.length; ++j) {
+ var status = statusList[j];
+ removeClass(g, status);
+ }
+ },
clickGraph: function(self) {
console.log("click");
if (self.currentTarget.jobid) {
@@ -246,9 +251,9 @@ azkaban.SvgGraphView = Backbone.View.extend({
nodeG.setAttributeNS(null, "class", "node");
nodeG.jobid=node.id;
$(nodeG).contextMenu({
- menu: 'jobMenu'
+ menu: this.contextMenu.id
},
- handleJobMenuClick
+ this.contextMenu.callback
);
},
addBounds: function(toBounds, addBounds) {
src/web/js/azkaban.flow.job.view.js 5(+3 -2)
diff --git a/src/web/js/azkaban.flow.job.view.js b/src/web/js/azkaban.flow.job.view.js
index 210b404..b165e28 100644
--- a/src/web/js/azkaban.flow.job.view.js
+++ b/src/web/js/azkaban.flow.job.view.js
@@ -12,6 +12,7 @@ azkaban.JobListView = Backbone.View.extend({
this.filterInput = $(this.el).find(".filter");
this.list = $(this.el).find(".list");
+ this.contextMenu = settings.rightClick;
},
filterJobs: function(self) {
var filter = this.filterInput.val();
@@ -123,9 +124,9 @@ azkaban.JobListView = Backbone.View.extend({
li.jobid=nodeArray[i].id;
$(li).contextMenu({
- menu: 'jobMenu'
+ menu: this.contextMenu.id
},
- handleJobMenuClick
+ this.contextMenu.callback
);
this.listNodes[nodeArray[i].id] = li;
src/web/js/azkaban.flow.view.js 404(+9 -395)
diff --git a/src/web/js/azkaban.flow.view.js b/src/web/js/azkaban.flow.view.js
index d65dc9d..ad6dec9 100644
--- a/src/web/js/azkaban.flow.view.js
+++ b/src/web/js/azkaban.flow.view.js
@@ -21,112 +21,6 @@ var handleJobMenuClick = function(action, el, pos) {
else if(action == "openwindow") {
window.open(requestURL);
}
- else if(action == "disable") {
- var disabled = graphModel.get("disabled");
-
- disabled[jobid] = true;
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if (action == "disableParents") {
- var disabled = graphModel.get("disabled");
- var nodes = graphModel.get("nodes");
- var inNodes = nodes[jobid].inNodes;
-
- if (inNodes) {
- for (var key in inNodes) {
- disabled[key] = true;
- }
- }
-
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if (action == "disableChildren") {
- var disabled = graphModel.get("disabled");
- var nodes = graphModel.get("nodes");
- var outNodes = nodes[jobid].outNodes;
-
- if (outNodes) {
- for (var key in outNodes) {
- disabled[key] = true;
- }
- }
-
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if (action == "disableAncestors") {
- var disabled = graphModel.get("disabled");
- var nodes = graphModel.get("nodes");
-
- recurseAllAncestors(nodes, disabled, jobid, true);
-
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if (action == "disableDescendents") {
- var disabled = graphModel.get("disabled");
- var nodes = graphModel.get("nodes");
-
- recurseAllDescendents(nodes, disabled, jobid, true);
-
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if(action == "enable") {
- var disabled = graphModel.get("disabled");
-
- disabled[jobid] = false;
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if (action == "enableParents") {
- var disabled = graphModel.get("disabled");
- var nodes = graphModel.get("nodes");
- var inNodes = nodes[jobid].inNodes;
-
- if (inNodes) {
- for (var key in inNodes) {
- disabled[key] = false;
- }
- }
-
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if (action == "enableChildren") {
- var disabled = graphModel.get("disabled");
- var nodes = graphModel.get("nodes");
- var outNodes = nodes[jobid].outNodes;
-
- if (outNodes) {
- for (var key in outNodes) {
- disabled[key] = false;
- }
- }
-
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if (action == "enableAncestors") {
- var disabled = graphModel.get("disabled");
- var nodes = graphModel.get("nodes");
-
- recurseAllAncestors(nodes, disabled, jobid, false);
-
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
- else if (action == "enableDescendents") {
- var disabled = graphModel.get("disabled");
- var nodes = graphModel.get("nodes");
-
- recurseAllDescendents(nodes, disabled, jobid, false);
-
- graphModel.set({disabled: disabled});
- graphModel.trigger("change:disabled");
- }
}
function recurseAllAncestors(nodes, disabledMap, id, disable) {
@@ -216,246 +110,7 @@ azkaban.FlowTabView= Backbone.View.extend({
var jobListView;
var svgGraphView;
-/*
-azkaban.SvgGraphView = Backbone.View.extend({
- events: {
- "click g" : "clickGraph"
- },
- initialize: function(settings) {
- this.model.bind('change:selected', this.changeSelected, this);
- this.model.bind('change:disabled', this.handleDisabledChange, this);
- this.model.bind('change:graph', this.render, this);
- this.model.bind('resetPanZoom', this.resetPanZoom, this);
-
- this.svgns = "http://www.w3.org/2000/svg";
- this.xlinksn = "http://www.w3.org/1999/xlink";
-
- var graphDiv = this.el[0];
- var svg = $('#svgGraph')[0];
- this.svgGraph = svg;
-
- var gNode = document.createElementNS(this.svgns, 'g');
- gNode.setAttribute("id", "group");
- svg.appendChild(gNode);
- this.mainG = gNode;
-
- $(svg).svgNavigate();
- },
- initializeDefs: function(self) {
- var def = document.createElementNS(svgns, 'defs');
- def.setAttributeNS(null, "id", "buttonDefs");
-
- // ArrowHead
- var arrowHeadMarker = document.createElementNS(svgns, 'marker');
- arrowHeadMarker.setAttribute("id", "triangle");
- arrowHeadMarker.setAttribute("viewBox", "0 0 10 10");
- arrowHeadMarker.setAttribute("refX", "5");
- arrowHeadMarker.setAttribute("refY", "5");
- arrowHeadMarker.setAttribute("markerUnits", "strokeWidth");
- arrowHeadMarker.setAttribute("markerWidth", "4");
- arrowHeadMarker.setAttribute("markerHeight", "3");
- arrowHeadMarker.setAttribute("orient", "auto");
- var path = document.createElementNS(svgns, 'polyline');
- arrowHeadMarker.appendChild(path);
- path.setAttribute("points", "0,0 10,5 0,10 1,5");
- def.appendChild(arrowHeadMarker);
-
- this.svgGraph.appendChild(def);
- },
- render: function(self) {
- console.log("graph render");
-
- var data = this.model.get("data");
- var nodes = data.nodes;
- var edges = data.edges;
- if (nodes.length == 0) {
- console.log("No results");
- return;
- };
-
- // layout
- layoutGraph(nodes, edges);
-
- var bounds = {};
- this.nodes = {};
- for (var i = 0; i < nodes.length; ++i) {
- this.nodes[nodes[i].id] = nodes[i];
- }
-
- for (var i = 0; i < edges.length; ++i) {
- this.drawEdge(this, edges[i]);
- }
-
- for (var i = 0; i < nodes.length; ++i) {
- this.drawNode(this, nodes[i], bounds);
- }
-
- bounds.minX = bounds.minX ? bounds.minX - 200 : -200;
- bounds.minY = bounds.minY ? bounds.minY - 200 : -200;
- bounds.maxX = bounds.maxX ? bounds.maxX + 200 : 200;
- bounds.maxY = bounds.maxY ? bounds.maxY + 200 : 200;
-
- this.graphBounds = bounds;
- this.resetPanZoom();
- },
- changeSelected: function(self) {
- console.log("change selected");
- var selected = this.model.get("selected");
- var previous = this.model.previous("selected");
-
- if (previous) {
- // Unset previous
- var g = document.getElementById(previous);
- removeClass(g, "selected");
- }
-
- if (selected) {
- var g = document.getElementById(selected);
- var node = this.nodes[selected];
-
- addClass(g, "selected");
-
- var offset = 200;
- var widthHeight = offset*2;
- var x = node.x - offset;
- var y = node.y - offset;
-
-
- $("#svgGraph").svgNavigate("transformToBox", {x: x, y: y, width: widthHeight, height: widthHeight});
- }
- },
- clickGraph: function(self) {
- console.log("click");
- if (self.currentTarget.jobid) {
- this.model.set({"selected": self.currentTarget.jobid});
- }
- },
- drawEdge: function(self, edge) {
- var svg = self.svgGraph;
- var svgns = self.svgns;
-
- var startNode = this.nodes[edge.from];
- var endNode = this.nodes[edge.target];
-
- if (edge.guides) {
- var pointString = "" + startNode.x + "," + startNode.y + " ";
-
- for (var i = 0; i < edge.guides.length; ++i ) {
- edgeGuidePoint = edge.guides[i];
- pointString += edgeGuidePoint.x + "," + edgeGuidePoint.y + " ";
- }
-
- pointString += endNode.x + "," + endNode.y;
- var polyLine = document.createElementNS(svgns, "polyline");
- polyLine.setAttributeNS(null, "class", "edge");
- polyLine.setAttributeNS(null, "points", pointString);
- polyLine.setAttributeNS(null, "style", "fill:none;");
- self.mainG.appendChild(polyLine);
- }
- else {
- var line = document.createElementNS(svgns, 'line');
- line.setAttributeNS(null, "class", "edge");
- line.setAttributeNS(null, "x1", startNode.x);
- line.setAttributeNS(null, "y1", startNode.y);
- line.setAttributeNS(null, "x2", endNode.x);
- line.setAttributeNS(null, "y2", endNode.y);
-
- self.mainG.appendChild(line);
- }
- },
- handleDisabledChange: function(evt) {
- var disabledMap = this.model.get("disabled");
- for(var id in disabledMap) {
- if(disabledMap.hasOwnProperty(id)) {
- var disabled = disabledMap[id];
- this.nodes[id].disabled = disabled;
- var g = document.getElementById(id);
-
- if (disabled) {
- this.nodes[id].disabled = disabled;
- addClass(g, "disabled");
- }
- else {
- removeClass(g, "disabled");
- }
- }
- }
- },
- drawNode: function(self, node, bounds) {
- var svg = self.svgGraph;
- var svgns = self.svgns;
-
- var xOffset = 10;
- var yOffset = 10;
-
- var nodeG = document.createElementNS(svgns, "g");
- nodeG.setAttributeNS(null, "class", "jobnode");
- nodeG.setAttributeNS(null, "id", node.id);
- nodeG.setAttributeNS(null, "font-family", "helvetica");
- nodeG.setAttributeNS(null, "transform", "translate(" + node.x + "," + node.y + ")");
-
- var innerG = document.createElementNS(svgns, "g");
- innerG.setAttributeNS(null, "transform", "translate(-10,-10)");
-
- var circle = document.createElementNS(svgns, 'circle');
- circle.setAttributeNS(null, "cy", 10);
- circle.setAttributeNS(null, "cx", 10);
- circle.setAttributeNS(null, "r", 12);
- circle.setAttributeNS(null, "style", "width:inherit;stroke-opacity:1");
-
-
- var text = document.createElementNS(svgns, 'text');
- var textLabel = document.createTextNode(node.label);
- text.appendChild(textLabel);
- text.setAttributeNS(null, "x", 4);
- text.setAttributeNS(null, "y", 15);
- text.setAttributeNS(null, "height", 10);
-
- this.addBounds(bounds, {minX:node.x - xOffset, minY: node.y - yOffset, maxX: node.x + xOffset, maxY: node.y + yOffset});
-
- var backRect = document.createElementNS(svgns, 'rect');
- backRect.setAttributeNS(null, "x", 0);
- backRect.setAttributeNS(null, "y", 2);
- backRect.setAttributeNS(null, "class", "backboard");
- backRect.setAttributeNS(null, "width", 10);
- backRect.setAttributeNS(null, "height", 15);
-
- innerG.appendChild(circle);
- innerG.appendChild(backRect);
- innerG.appendChild(text);
- innerG.jobid = node.id;
-
- nodeG.appendChild(innerG);
- self.mainG.appendChild(nodeG);
-
- // Need to get text width after attaching to SVG.
- var computeText = text.getComputedTextLength();
- var halfWidth = computeText/2;
- text.setAttributeNS(null, "x", -halfWidth + 10);
- backRect.setAttributeNS(null, "x", -halfWidth);
- backRect.setAttributeNS(null, "width", computeText + 20);
-
- nodeG.setAttributeNS(null, "class", "node");
- nodeG.jobid=node.id;
- $(nodeG).contextMenu({
- menu: 'jobMenu'
- },
- handleJobMenuClick
- );
- },
- addBounds: function(toBounds, addBounds) {
- toBounds.minX = toBounds.minX ? Math.min(toBounds.minX, addBounds.minX) : addBounds.minX;
- toBounds.minY = toBounds.minY ? Math.min(toBounds.minY, addBounds.minY) : addBounds.minY;
- toBounds.maxX = toBounds.maxX ? Math.max(toBounds.maxX, addBounds.maxX) : addBounds.maxX;
- toBounds.maxY = toBounds.maxY ? Math.max(toBounds.maxY, addBounds.maxY) : addBounds.maxY;
- },
- resetPanZoom : function(self) {
- var bounds = this.graphBounds;
- $("#svgGraph").svgNavigate("transformToBox", {x: bounds.minX, y: bounds.minY, width: (bounds.maxX - bounds.minX), height: (bounds.maxY - bounds.minY) });
- }
-});
-*/
var executionsView;
azkaban.ExecutionsView = Backbone.View.extend({
events: {
@@ -641,52 +296,6 @@ azkaban.ExecutionsView = Backbone.View.extend({
}
});
-var contextMenu;
-azkaban.ContextMenu = Backbone.View.extend({
- events : {
- "click #disableArrow" : "handleDisabledClick",
- "click #enableArrow" : "handleEnabledClick"
- },
- initialize: function(settings) {
- $('#disableSub').hide();
- $('#enableSub').hide();
- },
- handleEnabledClick: function(evt) {
- if(evt.stopPropagation) {
- evt.stopPropagation();
- }
- evt.cancelBubble=true;
-
- if (evt.currentTarget.expanded) {
- evt.currentTarget.expanded=false;
- $('#enableArrow').removeClass('collapse');
- $('#enableSub').hide();
- }
- else {
- evt.currentTarget.expanded=true;
- $('#enableArrow').addClass('collapse');
- $('#enableSub').show();
- }
- },
- handleDisabledClick: function(evt) {
- if(evt.stopPropagation) {
- evt.stopPropagation();
- }
- evt.cancelBubble=true;
-
- if (evt.currentTarget.expanded) {
- evt.currentTarget.expanded=false;
- $('#disableArrow').removeClass('collapse');
- $('#disableSub').hide();
- }
- else {
- evt.currentTarget.expanded=true;
- $('#disableArrow').addClass('collapse');
- $('#disableSub').show();
- }
- }
-});
-
var graphModel;
azkaban.GraphModel = Backbone.Model.extend({});
@@ -770,17 +379,22 @@ $(function() {
var selected;
// Execution model has to be created before the window switches the tabs.
executionModel = new azkaban.ExecutionModel();
+ executionsView = new azkaban.ExecutionsView({el:$('#executionsView'), model: executionModel});
flowTabView = new azkaban.FlowTabView({el:$( '#headertabs'), selectedView: selected });
graphModel = new azkaban.GraphModel();
- svgGraphView = new azkaban.SvgGraphView({el:$('#svgDiv'), model: graphModel});
- jobsListView = new azkaban.JobListView({el:$('#jobList'), model: graphModel});
- contextMenu = new azkaban.ContextMenu({el:$('#jobMenu')});
+ svgGraphView = new azkaban.SvgGraphView({el:$('#svgDiv'), model: graphModel, rightClick: {id: 'jobMenu', callback: handleJobMenuClick}});
+ jobsListView = new azkaban.JobListView({el:$('#jobList'), model: graphModel, rightClick: {id: 'jobMenu', callback: handleJobMenuClick}});
scheduleFlowView = new azkaban.ScheduleFlowView({el:$('#schedule-flow')});
- executeFlowView = new azkaban.ExecuteFlowView({el:$('#executing-options')});
+ executeFlowView = new azkaban.ExecuteFlowView({el:$('#executing-options'), model: graphModel});
var requestURL = contextURL + "/manager";
+ // Set up the Flow options view. Create a new one every time :p
+ $('#executebtn').click( function() {
+ executeFlowView.show();
+ });
+
$.get(
requestURL,
{"project": projectName, "ajax":"fetchflowgraph", "flow":flowName},