mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Workflow status bar for completed jobs
adjusting workflow results link for job standard out views (job results, projects, inventories, and jobs list) Enhancing workflow status bar for running jobs removing workflow_events group from the UI adding comment fix for updating while job is running removing pending from the status bar, and reload the page on job finish
This commit is contained in:
parent
26adcf5972
commit
fd61096291
@ -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