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:
parent
6bffaec6f9
commit
59e3552fa3
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
@ -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();
|
||||
}];
|
||||
|
@ -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>
|
||||
|
@ -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": []
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user