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:
jaredevantabor 2016-12-01 17:19:22 -08:00
parent 26adcf5972
commit fd61096291
11 changed files with 118 additions and 86 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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="">

View File

@ -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

View File

@ -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;

View File

@ -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");
}
});
}];

View File

@ -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) {

View File

@ -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',

View File

@ -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;
}

View File

@ -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>`;

View File

@ -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>