mirror of
https://github.com/ansible/awx.git
synced 2026-01-29 07:14:43 -03:30
Merge pull request #4350 from jaredevantabor/workflow-status-bar
Workflow status bar for completed jobs
This commit is contained in:
commit
3a3f7a6f81
@ -36,7 +36,10 @@ export function JobsListController($state, $rootScope, $log, $scope, $compile, $
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
|
||||
$scope[list.name].forEach(function(item, item_idx) {
|
||||
var itm = $scope[list.name][item_idx];
|
||||
|
||||
if(item.summary_fields && item.summary_fields.source_workflow_job &&
|
||||
item.summary_fields.source_workflow_job.id){
|
||||
item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
|
||||
}
|
||||
// Set the item type label
|
||||
if (list.fields.type) {
|
||||
$scope.type_choices.every(function(choice) {
|
||||
|
||||
@ -64,17 +64,25 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count'
|
||||
|
||||
// turn related api browser routes into tower routes
|
||||
getTowerLinks();
|
||||
|
||||
// the links below can't be set in getTowerLinks because the
|
||||
// links on the UI don't directly match the corresponding URL
|
||||
// on the API browser
|
||||
if(jobData.summary_fields && jobData.summary_fields.job_template &&
|
||||
jobData.summary_fields.job_template.id){
|
||||
$scope.job_template_link = `/#/templates/job_template/${$scope.job.summary_fields.job_template.id}`;
|
||||
}
|
||||
if(jobData.summary_fields && jobData.summary_fields.project_update &&
|
||||
jobData.summary_fields.project_update.status){
|
||||
$scope.project_status = jobData.summary_fields.project_update.status;
|
||||
$scope.project_status = jobData.summary_fields.project_update.status;
|
||||
}
|
||||
if(jobData.summary_fields && jobData.summary_fields.project_update &&
|
||||
jobData.summary_fields.project_update.id){
|
||||
$scope.project_update_link = `/#/scm_update/${jobData.summary_fields.project_update.id}`;
|
||||
$scope.project_update_link = `/#/scm_update/${jobData.summary_fields.project_update.id}`;
|
||||
}
|
||||
if(jobData.summary_fields && jobData.summary_fields.source_workflow_job &&
|
||||
jobData.summary_fields.source_workflow_job.id){
|
||||
$scope.workflow_result_link = `/#/workflows/${jobData.summary_fields.source_workflow_job.id}`;
|
||||
}
|
||||
|
||||
// use options labels to manipulate display of details
|
||||
|
||||
@ -43,7 +43,7 @@ export default
|
||||
ngClick: "viewJobDetails(job)",
|
||||
badgePlacement: 'right',
|
||||
badgeCustom: true,
|
||||
badgeIcon: `<a href="{{ workflow_result_link }}"
|
||||
badgeIcon: `<a href="{{ job.workflow_result_link }}"
|
||||
aw-tool-tip="View workflow results"
|
||||
data-placement="top"
|
||||
data-original-title="" title="">
|
||||
|
||||
@ -93,12 +93,6 @@ 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
|
||||
|
||||
@ -58,6 +58,10 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams,
|
||||
$scope.credential_name = (data.summary_fields.credential) ? data.summary_fields.credential.name : '';
|
||||
$scope.credential_url = (data.credential) ? '/#/credentials/' + data.credential : '';
|
||||
$scope.cloud_credential_url = (data.cloud_credential) ? '/#/credentials/' + data.cloud_credential : '';
|
||||
if(data.summary_fields && data.summary_fields.source_workflow_job &&
|
||||
data.summary_fields.source_workflow_job.id){
|
||||
$scope.workflow_result_link = `/#/workflows/${data.summary_fields.source_workflow_job.id}`;
|
||||
}
|
||||
$scope.playbook = data.playbook;
|
||||
$scope.credential = data.credential;
|
||||
$scope.cloud_credential = data.cloud_credential;
|
||||
|
||||
@ -7,6 +7,8 @@ export default ['workflowData',
|
||||
'ParseTypeChange',
|
||||
'ParseVariableString',
|
||||
'WorkflowService',
|
||||
'count',
|
||||
'$state',
|
||||
function(workflowData,
|
||||
workflowResultsService,
|
||||
workflowDataOptions,
|
||||
@ -15,7 +17,9 @@ export default ['workflowData',
|
||||
$scope,
|
||||
ParseTypeChange,
|
||||
ParseVariableString,
|
||||
WorkflowService
|
||||
WorkflowService,
|
||||
count,
|
||||
$state
|
||||
) {
|
||||
|
||||
var getTowerLinks = function() {
|
||||
@ -57,6 +61,7 @@ export default ['workflowData',
|
||||
$scope.workflow_nodes = workflowNodes;
|
||||
$scope.workflowOptions = workflowDataOptions.actions.GET;
|
||||
$scope.labels = jobLabels;
|
||||
$scope.count = count.val;
|
||||
|
||||
// turn related api browser routes into tower routes
|
||||
getTowerLinks();
|
||||
@ -113,22 +118,38 @@ export default ['workflowData',
|
||||
|
||||
init();
|
||||
|
||||
$scope.$on(`ws-workflow_events-${$scope.workflow.id}`, function(e, data) {
|
||||
|
||||
WorkflowService.updateStatusOfNode({
|
||||
treeData: $scope.treeData,
|
||||
nodeId: data.workflow_node_id,
|
||||
status: data.status,
|
||||
unified_job_id: data.unified_job_id
|
||||
});
|
||||
|
||||
$scope.$broadcast("refreshWorkflowChart");
|
||||
});
|
||||
|
||||
// Processing of job-status messages from the websocket
|
||||
$scope.$on(`ws-jobs`, function(e, data) {
|
||||
// Update the workflow job's unified job:
|
||||
if (parseInt(data.unified_job_id, 10) === parseInt($scope.workflow.id,10)) {
|
||||
$scope.workflow.status = data.status;
|
||||
$scope.workflow.status = data.status;
|
||||
|
||||
if(data.status === "successful" || data.status === "failed"){
|
||||
$state.go('.', null, { reload: true });
|
||||
}
|
||||
}
|
||||
// Update the jobs spawned by the workflow:
|
||||
if(data.hasOwnProperty('workflow_job_id') &&
|
||||
parseInt(data.workflow_job_id, 10) === parseInt($scope.workflow.id,10)){
|
||||
|
||||
WorkflowService.updateStatusOfNode({
|
||||
treeData: $scope.treeData,
|
||||
nodeId: data.workflow_node_id,
|
||||
status: data.status,
|
||||
unified_job_id: data.unified_job_id
|
||||
});
|
||||
|
||||
$scope.workflow_nodes.forEach(node => {
|
||||
if(parseInt(node.id) === parseInt(data.workflow_node_id)){
|
||||
node.summary_fields.job = {
|
||||
status: data.status
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
$scope.count = workflowResultsService
|
||||
.getCounts($scope.workflow_nodes);
|
||||
$scope.$broadcast("refreshWorkflowChart");
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
@ -18,8 +18,7 @@ export default {
|
||||
data: {
|
||||
socket: {
|
||||
"groups":{
|
||||
"jobs": ["status_changed"],
|
||||
"workflow_events": []
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -61,6 +60,18 @@ export default {
|
||||
});
|
||||
return defer.promise;
|
||||
}],
|
||||
// after the GET for the workflow & it's nodes, this helps us keep the
|
||||
// status bar from flashing as rest data comes in. If the workflow
|
||||
// is finished and there's a playbook_on_stats event, go ahead and
|
||||
// resolve the count so you don't get that flashing!
|
||||
count: ['workflowData', 'workflowNodes', 'workflowResultsService', 'Rest', '$q', function(workflowData, workflowNodes, workflowResultsService, Rest, $q) {
|
||||
var defer = $q.defer();
|
||||
defer.resolve({
|
||||
val: workflowResultsService
|
||||
.getCounts(workflowNodes),
|
||||
countFinished: true});
|
||||
return defer.promise;
|
||||
}],
|
||||
// GET for the particular jobs labels to be displayed in the
|
||||
// left-hand pane
|
||||
jobLabels: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) {
|
||||
|
||||
@ -7,6 +7,31 @@
|
||||
|
||||
export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun) {
|
||||
var val = {
|
||||
getCounts: function(workflowNodes){
|
||||
var nodeArr = [];
|
||||
workflowNodes.forEach(node => {
|
||||
if(node && node.summary_fields && node.summary_fields.job && node.summary_fields.job.status){
|
||||
nodeArr.push(node.summary_fields.job.status);
|
||||
}
|
||||
});
|
||||
// use the workflow nodes data populate above to get the count
|
||||
var count = {
|
||||
successful : _.filter(nodeArr, function(o){
|
||||
return o === "successful";
|
||||
}),
|
||||
failed : _.filter(nodeArr, function(o){
|
||||
return o === "failed" || o === "error" || o === "canceled";
|
||||
})
|
||||
};
|
||||
|
||||
// turn the count into an actual count, rather than a list of
|
||||
// statuses
|
||||
Object.keys(count).forEach(key => {
|
||||
count[key] = count[key].length;
|
||||
});
|
||||
|
||||
return count;
|
||||
},
|
||||
deleteJob: function(workflow) {
|
||||
Prompt({
|
||||
hdr: 'Delete Job',
|
||||
|
||||
@ -5,42 +5,31 @@
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-ok,
|
||||
.WorkflowStatusBar-changed,
|
||||
.WorkflowStatusBar-unreachable,
|
||||
.WorkflowStatusBar-failures,
|
||||
.WorkflowStatusBar-skipped,
|
||||
.WorkflowStatusBar-successful,
|
||||
.WorkflowStatusBar-failed,
|
||||
.WorkflowStatusBar-pending,
|
||||
.WorkflowStatusBar-noData {
|
||||
height: 15px;
|
||||
border-top: 5px solid @default-bg;
|
||||
border-bottom: 5px solid @default-bg;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-ok {
|
||||
.WorkflowStatusBar-successful {
|
||||
background-color: @default-succ;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-changed {
|
||||
background-color: @default-warning;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-unreachable {
|
||||
background-color: @default-unreachable;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-failures {
|
||||
.WorkflowStatusBar-failed {
|
||||
background-color: @default-err;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-skipped {
|
||||
background-color: @default-link;
|
||||
.WorkflowStatusBar-pending {
|
||||
background-color: @b7grey;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
@ -58,23 +47,14 @@
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-tooltipBadge--ok {
|
||||
.WorkflowStatusBar-tooltipBadge--successful {
|
||||
background-color: @default-succ;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-tooltipBadge--unreachable {
|
||||
background-color: @default-unreachable;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-tooltipBadge--skipped {
|
||||
background-color: @default-link;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-tooltipBadge--changed {
|
||||
background-color: @default-warning;
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-tooltipBadge--failures {
|
||||
.WorkflowStatusBar-tooltipBadge--failed {
|
||||
background-color: @default-err;
|
||||
|
||||
}
|
||||
|
||||
.WorkflowStatusBar-tooltipBadge--pending {
|
||||
background-color: @b7grey;
|
||||
}
|
||||
|
||||
@ -4,27 +4,25 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
// import WorkflowStatusBarController from './host-status-bar.controller';
|
||||
export default [ 'templateUrl',
|
||||
function(templateUrl) {
|
||||
return {
|
||||
scope: true,
|
||||
templateUrl: templateUrl('workflow-results/workflow-status-bar/workflow-status-bar'),
|
||||
restrict: 'E',
|
||||
// controller: standardOutLogController,
|
||||
link: function(scope) {
|
||||
// as count is changed by event data coming in,
|
||||
// update the host status bar
|
||||
// as count is changed by jobs coming in,
|
||||
// update the workflow status bar
|
||||
scope.$watch('count', function(val) {
|
||||
if (val) {
|
||||
Object.keys(val).forEach(key => {
|
||||
// reposition the hosts status bar by setting
|
||||
// reposition the workflow status bar by setting
|
||||
// the various flex values to the count of
|
||||
// those hosts
|
||||
// those jobs
|
||||
$(`.WorkflowStatusBar-${key}`)
|
||||
.css('flex', `${val[key]} 0 auto`);
|
||||
|
||||
// set the tooltip to give how many hosts of
|
||||
// set the tooltip to give how many jobs of
|
||||
// each type
|
||||
if (val[key] > 0) {
|
||||
scope[`${key}CountTip`] = `<span class='WorkflowStatusBar-tooltipLabel'>${key}</span><span class='badge WorkflowStatusBar-tooltipBadge WorkflowStatusBar-tooltipBadge--${key}'>${val[key]}</span>`;
|
||||
|
||||
@ -1,26 +1,14 @@
|
||||
<div class="WorkflowStatusBar">
|
||||
<div class="WorkflowStatusBar-ok"
|
||||
<div class="WorkflowStatusBar-successful"
|
||||
data-placement="top"
|
||||
aw-tool-tip="{{okCountTip}}"
|
||||
data-tip-watch="okCountTip"></div>
|
||||
<div class="WorkflowStatusBar-changed"
|
||||
aw-tool-tip="{{successfulCountTip}}"
|
||||
data-tip-watch="successfulCountTip"></div>
|
||||
<div class="WorkflowStatusBar-failed"
|
||||
data-placement="top"
|
||||
aw-tool-tip="{{changedCountTip}}"
|
||||
data-tip-watch="changedCountTip"></div>
|
||||
<div class="WorkflowStatusBar-failures"
|
||||
data-placement="top"
|
||||
aw-tool-tip="{{failuresCountTip}}"
|
||||
data-tip-watch="failuresCountTip"></div>
|
||||
<div class="WorkflowStatusBar-unreachable"
|
||||
data-placement="top"
|
||||
aw-tool-tip="{{unreachableCountTip}}"
|
||||
data-tip-watch="unreachableCountTip"></div>
|
||||
<div class="WorkflowStatusBar-skipped"
|
||||
data-placement="top"
|
||||
aw-tool-tip="{{skippedCountTip}}"
|
||||
data-tip-watch="skippedCountTip"></div>
|
||||
aw-tool-tip="{{failedCountTip}}"
|
||||
data-tip-watch="failedCountTip"></div>
|
||||
<div class="WorkflowStatusBar-noData"
|
||||
aw-tool-tip="NO HOSTS FINISHED"
|
||||
aw-tool-tip="NO JOBS FINISHED"
|
||||
ng-hide="hostsFinished"
|
||||
data-placement="top"></div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user