1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 16:51:11 +03:00

Hooking up sockets to the workflow details page

This commit is contained in:
Michael Abashian 2016-11-22 16:23:52 -05:00
parent 6bffaec6f9
commit 59e3552fa3
7 changed files with 134 additions and 106 deletions

View File

@ -93,6 +93,12 @@ export default
// ex: 'ws-jobs-<jobId>'
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"){
// The naming scheme is "ws" then a
// dash (-) and the group_name, then the job ID

View File

@ -67,3 +67,12 @@
.WorkflowChart-nodeTypeLetter {
fill: @default-bg;
}
.workflowChart-nodeStatus--running {
fill: @default-icon;
}
.workflowChart-nodeStatus--success {
fill: @default-succ;
}
.workflowChart-nodeStatus--failed {
fill: @default-err;
}

View File

@ -282,6 +282,36 @@ export default [
d3.select("#node-" + d.id + "-remove")
.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")
.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) {
return (d.target.y + d.source.y + rectW) / 2;
})
@ -416,7 +446,7 @@ export default [
});
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 + ")"; });
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" : "");
})
.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() {

View File

@ -3,8 +3,11 @@ export default [function(){
searchTree: function(params) {
// params.element
// 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;
}else if (params.element.children && params.element.children.length > 0){
let result = null;
@ -12,7 +15,8 @@ export default [function(){
_.forEach(params.element.children, function(child) {
result = thisService.searchTree({
element: child,
matchingId: params.matchingId
matchingId: params.matchingId,
byNodeId: params.byNodeId ? params.byNodeId : false
});
if(result) {
return false;
@ -205,7 +209,8 @@ export default [function(){
originalEdge: params.edgeType,
originalNodeObj: _.clone(params.nodesObj[params.nodeId]),
promptValues: {},
isRoot: params.isRoot ? params.isRoot : false
isRoot: params.isRoot ? params.isRoot : false,
//jobStatus: 'failed' successful waiting running pending
};
params.treeData.data.totalNodes++;
@ -216,6 +221,10 @@ export default [function(){
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
_.forEach(params.nodesObj[params.nodeId].success_nodes, function(successNodeId) {
treeNode.children.push(_this.buildBranch({
@ -250,6 +259,22 @@ export default [function(){
});
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;
}
}
};
}];

View File

@ -74,8 +74,6 @@ export default ['workflowData',
// Click binding for the expand/collapse button on the standard out log
$scope.stdoutFullScreen = false;
$scope.stdoutArr = [];
$scope.treeData = WorkflowService.buildTree({
workflowNodes: workflowNodes
});
@ -113,92 +111,18 @@ export default ['workflowData',
workflowResultsService.relaunchJob($scope);
};
// EVENT STUFF BELOW
init();
// just putting the event queue on scope so it can be inspected in the
// console
// $scope.event_queue = eventQueue.queue;
// $scope.defersArr = eventQueue.populateDefers;
$scope.$on(`ws-workflow_events-${$scope.workflow.id}`, function(e, data) {
// This is where the async updates to the UI actually happen.
// Flow is event queue munging in the service -> $scope setting in here
// var processEvent = function(event) {
// // put the event in the queue
// 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);
// });
// };
WorkflowService.updateStatusOfNode({
treeData: $scope.treeData,
nodeId: data.workflow_node_id,
status: data.status
});
// PULL! grab completed event data and process each event
// 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);
// });
$scope.$broadcast("refreshWorkflowChart");
});
// Processing of job-status messages from the websocket
$scope.$on(`ws-jobs`, function(e, data) {
@ -206,6 +130,4 @@ export default ['workflowData',
$scope.workflow.status = data.status;
}
});
init();
}];

View File

@ -177,7 +177,7 @@
<div class="StandardOut-panelHeader">
<div class="StandardOut-panelHeaderText">
<i class="WorkflowResults-statusResultIcon
fa icon-job-{{ job.status }}">
fa icon-job-{{ workflow.status }}">
</i>
{{ workflow.name }}
</div>
@ -213,18 +213,6 @@
<i class="fa fa-arrows-alt"></i>
</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>
<workflow-status-bar></workflow-status-bar>

View File

@ -13,11 +13,12 @@ export default {
url: '/workflows/:id',
ncyBreadcrumb: {
parent: 'jobs',
label: '{{ job.id }} - {{ job.name }}'
label: '{{ workflow.id }} - {{ workflow.name }}'
},
data: {
socket: {
"groups":{
"jobs": ["status_changed"],
"workflow_events": []
}
}