mirror of
https://github.com/ansible/awx.git
synced 2026-05-14 21:07:39 -02:30
Hooking up sockets to the workflow details page
This commit is contained in:
@@ -93,6 +93,12 @@ export default
|
|||||||
// ex: 'ws-jobs-<jobId>'
|
// ex: 'ws-jobs-<jobId>'
|
||||||
str = `ws-${data.group_name}-${data.job}`;
|
str = `ws-${data.group_name}-${data.job}`;
|
||||||
}
|
}
|
||||||
|
else if(data.group_name==="workflow_events"){
|
||||||
|
// The naming scheme is "ws" then a
|
||||||
|
// dash (-) and the group_name, then the job ID
|
||||||
|
// ex: 'ws-jobs-<jobId>'
|
||||||
|
str = `ws-${data.group_name}-${data.workflow_job_id}`;
|
||||||
|
}
|
||||||
else if(data.group_name==="ad_hoc_command_events"){
|
else if(data.group_name==="ad_hoc_command_events"){
|
||||||
// The naming scheme is "ws" then a
|
// The naming scheme is "ws" then a
|
||||||
// dash (-) and the group_name, then the job ID
|
// dash (-) and the group_name, then the job ID
|
||||||
|
|||||||
@@ -67,3 +67,12 @@
|
|||||||
.WorkflowChart-nodeTypeLetter {
|
.WorkflowChart-nodeTypeLetter {
|
||||||
fill: @default-bg;
|
fill: @default-bg;
|
||||||
}
|
}
|
||||||
|
.workflowChart-nodeStatus--running {
|
||||||
|
fill: @default-icon;
|
||||||
|
}
|
||||||
|
.workflowChart-nodeStatus--success {
|
||||||
|
fill: @default-succ;
|
||||||
|
}
|
||||||
|
.workflowChart-nodeStatus--failed {
|
||||||
|
fill: @default-err;
|
||||||
|
}
|
||||||
|
|||||||
@@ -282,6 +282,36 @@ export default [
|
|||||||
d3.select("#node-" + d.id + "-remove")
|
d3.select("#node-" + d.id + "-remove")
|
||||||
.classed("removeHovering", false);
|
.classed("removeHovering", false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
thisNode.append("circle")
|
||||||
|
.attr("class", function(d) {
|
||||||
|
|
||||||
|
let statusClass = "WorkflowChart-nodeStatus ";
|
||||||
|
|
||||||
|
switch(d.jobStatus) {
|
||||||
|
case "pending":
|
||||||
|
statusClass = "workflowChart-nodeStatus--running";
|
||||||
|
break;
|
||||||
|
case "waiting":
|
||||||
|
statusClass = "workflowChart-nodeStatus--running";
|
||||||
|
break;
|
||||||
|
case "running":
|
||||||
|
statusClass = "workflowChart-nodeStatus--running";
|
||||||
|
break;
|
||||||
|
case "successful":
|
||||||
|
statusClass = "workflowChart-nodeStatus--success";
|
||||||
|
break;
|
||||||
|
case "failed":
|
||||||
|
statusClass = "workflowChart-nodeStatus--failed";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusClass;
|
||||||
|
})
|
||||||
|
.style("display", function(d) { return d.jobStatus ? null : "none"; })
|
||||||
|
.attr("cy", 10)
|
||||||
|
.attr("cx", 10)
|
||||||
|
.attr("r", 6);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -407,7 +437,7 @@ export default [
|
|||||||
});
|
});
|
||||||
|
|
||||||
t.selectAll(".linkCircle")
|
t.selectAll(".linkCircle")
|
||||||
.style("display", function(d) { return (d.source.placeholder || d.target.placeholder) ? "none" : null; })
|
.style("display", function(d) { return (d.source.placeholder || d.target.placeholder || scope.canAddWorkflowJobTemplate === false) ? "none" : null; })
|
||||||
.attr("cx", function(d) {
|
.attr("cx", function(d) {
|
||||||
return (d.target.y + d.source.y + rectW) / 2;
|
return (d.target.y + d.source.y + rectW) / 2;
|
||||||
})
|
})
|
||||||
@@ -416,7 +446,7 @@ export default [
|
|||||||
});
|
});
|
||||||
|
|
||||||
t.selectAll(".linkCross")
|
t.selectAll(".linkCross")
|
||||||
.style("display", function(d) { return (d.source.placeholder || d.target.placeholder) ? "none" : null; })
|
.style("display", function(d) { return (d.source.placeholder || d.target.placeholder || scope.canAddWorkflowJobTemplate === false) ? "none" : null; })
|
||||||
.attr("transform", function(d) { return "translate(" + (d.target.y + d.source.y + rectW) / 2 + "," + (d.target.x + d.source.x + rectH) / 2 + ")"; });
|
.attr("transform", function(d) { return "translate(" + (d.target.y + d.source.y + rectW) / 2 + "," + (d.target.x + d.source.x + rectH) / 2 + ")"; });
|
||||||
|
|
||||||
t.selectAll(".rect")
|
t.selectAll(".rect")
|
||||||
@@ -442,6 +472,53 @@ export default [
|
|||||||
return (d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update")) ? "P" : (d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? "I" : "");
|
return (d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update")) ? "P" : (d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? "I" : "");
|
||||||
})
|
})
|
||||||
.style("display", function(d) { return d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update" || d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? null : "none"; });
|
.style("display", function(d) { return d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update" || d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? null : "none"; });
|
||||||
|
|
||||||
|
t.selectAll(".WorkflowChart-nodeStatus")
|
||||||
|
.attr("class", function(d) {
|
||||||
|
|
||||||
|
let statusClass = "WorkflowChart-nodeStatus ";
|
||||||
|
|
||||||
|
switch(d.jobStatus) {
|
||||||
|
case "pending":
|
||||||
|
statusClass += "workflowChart-nodeStatus--running";
|
||||||
|
break;
|
||||||
|
case "waiting":
|
||||||
|
statusClass += "workflowChart-nodeStatus--running";
|
||||||
|
break;
|
||||||
|
case "running":
|
||||||
|
statusClass += "workflowChart-nodeStatus--running";
|
||||||
|
break;
|
||||||
|
case "successful":
|
||||||
|
statusClass += "workflowChart-nodeStatus--success";
|
||||||
|
break;
|
||||||
|
case "failed":
|
||||||
|
statusClass += "workflowChart-nodeStatus--failed";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusClass;
|
||||||
|
})
|
||||||
|
.style("display", function(d) { return d.jobStatus ? null : "none"; })
|
||||||
|
.transition()
|
||||||
|
.duration(0)
|
||||||
|
.attr("r", 6)
|
||||||
|
.each(function(d) {
|
||||||
|
if(d.jobStatus && (d.jobStatus === "pending" || d.jobStatus === "waiting" || d.jobStatus === "running")) {
|
||||||
|
// Pulse the circle
|
||||||
|
var circle = d3.select(this);
|
||||||
|
(function repeat() {
|
||||||
|
circle = circle.transition()
|
||||||
|
.duration(2000)
|
||||||
|
.attr("r", 6)
|
||||||
|
.transition()
|
||||||
|
.duration(2000)
|
||||||
|
.attr("r", 0)
|
||||||
|
.ease('sine')
|
||||||
|
.each("end", repeat);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_node() {
|
function add_node() {
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ export default [function(){
|
|||||||
searchTree: function(params) {
|
searchTree: function(params) {
|
||||||
// params.element
|
// params.element
|
||||||
// params.matchingId
|
// params.matchingId
|
||||||
|
// params.byNodeId
|
||||||
|
|
||||||
if(params.element.id === params.matchingId){
|
let thisNodeId = params.byNodeId ? params.element.nodeId : params.element.id;
|
||||||
|
|
||||||
|
if(thisNodeId === params.matchingId){
|
||||||
return params.element;
|
return params.element;
|
||||||
}else if (params.element.children && params.element.children.length > 0){
|
}else if (params.element.children && params.element.children.length > 0){
|
||||||
let result = null;
|
let result = null;
|
||||||
@@ -12,7 +15,8 @@ export default [function(){
|
|||||||
_.forEach(params.element.children, function(child) {
|
_.forEach(params.element.children, function(child) {
|
||||||
result = thisService.searchTree({
|
result = thisService.searchTree({
|
||||||
element: child,
|
element: child,
|
||||||
matchingId: params.matchingId
|
matchingId: params.matchingId,
|
||||||
|
byNodeId: params.byNodeId ? params.byNodeId : false
|
||||||
});
|
});
|
||||||
if(result) {
|
if(result) {
|
||||||
return false;
|
return false;
|
||||||
@@ -205,7 +209,8 @@ export default [function(){
|
|||||||
originalEdge: params.edgeType,
|
originalEdge: params.edgeType,
|
||||||
originalNodeObj: _.clone(params.nodesObj[params.nodeId]),
|
originalNodeObj: _.clone(params.nodesObj[params.nodeId]),
|
||||||
promptValues: {},
|
promptValues: {},
|
||||||
isRoot: params.isRoot ? params.isRoot : false
|
isRoot: params.isRoot ? params.isRoot : false,
|
||||||
|
//jobStatus: 'failed' successful waiting running pending
|
||||||
};
|
};
|
||||||
|
|
||||||
params.treeData.data.totalNodes++;
|
params.treeData.data.totalNodes++;
|
||||||
@@ -216,6 +221,10 @@ export default [function(){
|
|||||||
treeNode.originalParentId = params.parentId;
|
treeNode.originalParentId = params.parentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(params.nodesObj[params.nodeId].summary_fields.job) {
|
||||||
|
treeNode.jobStatus = params.nodesObj[params.nodeId].summary_fields.job.status;
|
||||||
|
}
|
||||||
|
|
||||||
// Loop across the success nodes and add them recursively
|
// Loop across the success nodes and add them recursively
|
||||||
_.forEach(params.nodesObj[params.nodeId].success_nodes, function(successNodeId) {
|
_.forEach(params.nodesObj[params.nodeId].success_nodes, function(successNodeId) {
|
||||||
treeNode.children.push(_this.buildBranch({
|
treeNode.children.push(_this.buildBranch({
|
||||||
@@ -250,6 +259,22 @@ export default [function(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
return treeNode;
|
return treeNode;
|
||||||
|
},
|
||||||
|
updateStatusOfNode: function(params) {
|
||||||
|
// params.treeData
|
||||||
|
// params.nodeId
|
||||||
|
// params.status
|
||||||
|
|
||||||
|
let matchingNode = this.searchTree({
|
||||||
|
element: params.treeData.data,
|
||||||
|
matchingId: params.nodeId,
|
||||||
|
byNodeId: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if(matchingNode) {
|
||||||
|
matchingNode.jobStatus = params.status;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}];
|
}];
|
||||||
|
|||||||
@@ -74,8 +74,6 @@ export default ['workflowData',
|
|||||||
// Click binding for the expand/collapse button on the standard out log
|
// Click binding for the expand/collapse button on the standard out log
|
||||||
$scope.stdoutFullScreen = false;
|
$scope.stdoutFullScreen = false;
|
||||||
|
|
||||||
$scope.stdoutArr = [];
|
|
||||||
|
|
||||||
$scope.treeData = WorkflowService.buildTree({
|
$scope.treeData = WorkflowService.buildTree({
|
||||||
workflowNodes: workflowNodes
|
workflowNodes: workflowNodes
|
||||||
});
|
});
|
||||||
@@ -113,92 +111,18 @@ export default ['workflowData',
|
|||||||
workflowResultsService.relaunchJob($scope);
|
workflowResultsService.relaunchJob($scope);
|
||||||
};
|
};
|
||||||
|
|
||||||
// EVENT STUFF BELOW
|
init();
|
||||||
|
|
||||||
// just putting the event queue on scope so it can be inspected in the
|
$scope.$on(`ws-workflow_events-${$scope.workflow.id}`, function(e, data) {
|
||||||
// console
|
|
||||||
// $scope.event_queue = eventQueue.queue;
|
|
||||||
// $scope.defersArr = eventQueue.populateDefers;
|
|
||||||
|
|
||||||
// This is where the async updates to the UI actually happen.
|
WorkflowService.updateStatusOfNode({
|
||||||
// Flow is event queue munging in the service -> $scope setting in here
|
treeData: $scope.treeData,
|
||||||
// var processEvent = function(event) {
|
nodeId: data.workflow_node_id,
|
||||||
// // put the event in the queue
|
status: data.status
|
||||||
// eventQueue.populate(event).then(mungedEvent => {
|
});
|
||||||
// // make changes to ui based on the event returned from the queue
|
|
||||||
// if (mungedEvent.changes) {
|
|
||||||
// mungedEvent.changes.forEach(change => {
|
|
||||||
// // we've got a change we need to make to the UI!
|
|
||||||
// // update the necessary scope and make the change
|
|
||||||
// if (change === 'startTime' && !$scope.workflow.start) {
|
|
||||||
// $scope.workflow.start = mungedEvent.startTime;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (change === 'count' && !$scope.countFinished) {
|
|
||||||
// // for all events that affect the host count,
|
|
||||||
// // update the status bar as well as the host
|
|
||||||
// // count badge
|
|
||||||
// $scope.count = mungedEvent.count;
|
|
||||||
// $scope.hostCount = getTotalHostCount(mungedEvent
|
|
||||||
// .count);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (change === 'playCount') {
|
|
||||||
// $scope.playCount = mungedEvent.playCount;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (change === 'taskCount') {
|
|
||||||
// $scope.taskCount = mungedEvent.taskCount;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (change === 'finishedTime' && !$scope.workflow.finished) {
|
|
||||||
// $scope.workflow.finished = mungedEvent.finishedTime;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (change === 'countFinished') {
|
|
||||||
// // the playbook_on_stats event actually lets
|
|
||||||
// // us know that we don't need to iteratively
|
|
||||||
// // look at event to update the host counts
|
|
||||||
// // any more.
|
|
||||||
// $scope.countFinished = true;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if(change === 'stdout'){
|
|
||||||
// angular
|
|
||||||
// .element(".JobResultsStdOut-stdoutContainer")
|
|
||||||
// .append($compile(mungedEvent
|
|
||||||
// .stdout)($scope));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // the changes have been processed in the ui, mark it in the queue
|
|
||||||
// eventQueue.markProcessed(event);
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// PULL! grab completed event data and process each event
|
$scope.$broadcast("refreshWorkflowChart");
|
||||||
// TODO: implement retry logic in case one of these requests fails
|
});
|
||||||
// var getEvents = function(url) {
|
|
||||||
// workflowResultsService.getEvents(url)
|
|
||||||
// .then(events => {
|
|
||||||
// events.results.forEach(event => {
|
|
||||||
// // get the name in the same format as the data
|
|
||||||
// // coming over the websocket
|
|
||||||
// event.event_name = event.event;
|
|
||||||
// processEvent(event);
|
|
||||||
// });
|
|
||||||
// if (events.next) {
|
|
||||||
// getEvents(events.next);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
// getEvents($scope.job.related.job_events);
|
|
||||||
|
|
||||||
// // Processing of job_events messages from the websocket
|
|
||||||
// $scope.$on(`ws-job_events-${$scope.workflow.id}`, function(e, data) {
|
|
||||||
// processEvent(data);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Processing of job-status messages from the websocket
|
// Processing of job-status messages from the websocket
|
||||||
$scope.$on(`ws-jobs`, function(e, data) {
|
$scope.$on(`ws-jobs`, function(e, data) {
|
||||||
@@ -206,6 +130,4 @@ export default ['workflowData',
|
|||||||
$scope.workflow.status = data.status;
|
$scope.workflow.status = data.status;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
init();
|
|
||||||
}];
|
}];
|
||||||
|
|||||||
@@ -177,7 +177,7 @@
|
|||||||
<div class="StandardOut-panelHeader">
|
<div class="StandardOut-panelHeader">
|
||||||
<div class="StandardOut-panelHeaderText">
|
<div class="StandardOut-panelHeaderText">
|
||||||
<i class="WorkflowResults-statusResultIcon
|
<i class="WorkflowResults-statusResultIcon
|
||||||
fa icon-job-{{ job.status }}">
|
fa icon-job-{{ workflow.status }}">
|
||||||
</i>
|
</i>
|
||||||
{{ workflow.name }}
|
{{ workflow.name }}
|
||||||
</div>
|
</div>
|
||||||
@@ -213,18 +213,6 @@
|
|||||||
<i class="fa fa-arrows-alt"></i>
|
<i class="fa fa-arrows-alt"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- DOWNLOAD ACTION -->
|
|
||||||
<a ng-show="workflow_status.status === 'failed' ||
|
|
||||||
job_status.status === 'successful' ||
|
|
||||||
job_status.status === 'canceled'"
|
|
||||||
href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}">
|
|
||||||
<button class="StandardOut-actionButton"
|
|
||||||
aw-tool-tip="Download Output"
|
|
||||||
data-placement="top">
|
|
||||||
<i class="fa fa-download"></i>
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<workflow-status-bar></workflow-status-bar>
|
<workflow-status-bar></workflow-status-bar>
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ export default {
|
|||||||
url: '/workflows/:id',
|
url: '/workflows/:id',
|
||||||
ncyBreadcrumb: {
|
ncyBreadcrumb: {
|
||||||
parent: 'jobs',
|
parent: 'jobs',
|
||||||
label: '{{ job.id }} - {{ job.name }}'
|
label: '{{ workflow.id }} - {{ workflow.name }}'
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
socket: {
|
socket: {
|
||||||
"groups":{
|
"groups":{
|
||||||
|
"jobs": ["status_changed"],
|
||||||
"workflow_events": []
|
"workflow_events": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user