adding workflow status bar to workflow results

This commit is contained in:
jaredevantabor 2016-11-09 11:09:33 -05:00
parent 9cb002b4cb
commit 478fc33710
8 changed files with 177 additions and 236 deletions

View File

@ -4,13 +4,12 @@
* All Rights Reserved
*************************************************/
import workflowStatusBar from './workflow-status-bar/main';
import route from './workflow-results.route.js';
import workflowResultsService from './workflow-results.service';
export default
angular.module('workflowResults', [])
angular.module('workflowResults', [workflowStatusBar.name])
.run(['$stateExtender', function($stateExtender) {
$stateExtender.addState(route);
}])

View File

@ -164,136 +164,6 @@
</div>
</div>
</div>
<!-- STATUS DETAIL -->
<!-- <div
class="form-group
WorkflowResults-resultRow
toggle-show">
<label
class="WorkflowResults-resultRowLabel
col-lg-2 col-md-2
col-sm-2 col-xs-3
control-label">
Status
</label>
<div class="WorkflowResults-resultRowText
col-lg-10 col-md-10 col-sm-10 col-xs-9">
<i
class="WorkflowResults-statusIcon--results
fa
icon-job-{{ job.status }}">
</i> {{ status_label }}
</div>
</div> -->
<!-- SCHEDULED BY DETAIL -->
<!-- <div
class="form-group
WorkflowResults-resultRow toggle-show"
ng-show="workflow.summary_fields.schedule_by.username">
<label
class="WorkflowResults-resultRowLabel
col-lg-2 col-md-2
col-sm-2 col-xs-3
control-label">
Launched By
</label>
<div class="WorkflowResults-resultRowText">
<a href="{{ scheduled_by_link }}"
aw-tool-tip="Edit the Schedule"
data-placement="top">
{{ job.summary_fields.scheduled_by.username }}
</a>
</div>
</div> -->
<!-- ELAPSED TIME DETAIL -->
<!-- <div
class="form-group
WorkflowResults-resultRow toggle-show"
ng-show="workflow_status.started">
<label
class="WorkflowResults-resultRowLabel
col-lg-2 col-md-2
col-sm-2 col-xs-3
control-label">
Elapsed
</label>
<div class="WorkflowResults-resultRowText">
{{ job_status.elapsed }}
</div>
</div> -->
<!-- EXPLANATION DETAIL -->
<!-- <div
class="form-group
WorkflowResults-resultRow
toggle-show"
ng-show="workflow_status.explanation">
<label
class="WorkflowResults-resultRowLabel
col-lg-2 col-md-2
col-sm-2 col-xs-3
control-label">
Explanation
</label> -->
<!-- PREVIOUS TASK SUCCEEDED -->
<!-- <div class="WorkflowResults-resultRowText
col-lg-10 col-md-10 col-sm-10 col-xs-9
job_status_explanation"
ng-show="!previousTaskFailed"
ng-bind-html="job_status.explanation">
<i
class="WorkflowResults-statusIcon--results
fa
icon-job-{{ job_status.status }}">
</i> {{ job_status.status_label }}
</div> -->
<!-- PREVIOUS TASK FAILED -->
<!-- <div class="WorkflowResults-resultRowText
col-lg-10 col-md-10 col-sm-10 col-xs-9
job_status_explanation"
ng-show="previousTaskFailed">
Previous Task Failed
<a
href=""
id="explanation_help"
aw-pop-over="{{ task_detail }}"
aw-pop-over-watch="task_detail"
data-placement="bottom"
data-container="body"
class="help-link"
over-title="Failure Detail"
title=""
tabindex="-1">
<i class="fa fa-question-circle">
</i>
</a>
</div> -->
<!-- </div> -->
<!-- RESULTS TRACEBACK DETAIL -->
<!-- <div
class="form-group
WorkflowResults-resultRow
toggle-show" ng-show="workflow.result_traceback">
<label
class="WorkflowResults-resultRowLabel
col-lg-2 col-md-12
col-sm-12 col-xs-12">
Results Traceback
</label>
<div class="WorkflowResults-resultRowText
col-lg-10 col-md-12 col-sm-12 col-xs-12
job_status_traceback"
ng-bind-html="job.result_traceback">
</div>
</div> -->
</div>
</div>
@ -319,7 +189,7 @@
Total Jobs
</div>
<span class="badge List-titleBadge">
{{ workflow_nodes.count || 0}}
{{ workflow_nodes.length || 0}}
</span>
<!-- ELAPSED TIME -->
@ -357,7 +227,7 @@
</div>
</div>
<host-status-bar></host-status-bar>
<workflow-status-bar></workflow-status-bar>
<!-- <job-results-standard-out></job-results-standard-out> -->
</div>

View File

@ -56,7 +56,7 @@ export default {
Rest.setUrl(workflowData.related.workflow_nodes);
Rest.get()
.success(function(data) {
defer.resolve(data.data);
defer.resolve(data.results);
})
.error(function() {
defer.resolve(data);

View File

@ -7,102 +7,14 @@
export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun) {
var val = {
// the playbook_on_stats event returns the count data in a weird format.
// format to what we need!
getCountsFromStatsEvent: function(event_data) {
var hosts = {},
hostsArr;
// iterate over the event_data and populate an object with hosts
// and their status data
Object.keys(event_data).forEach(key => {
// failed passes boolean not integer
if (key === "failed") {
// array of hosts from failed type
hostsArr = Object.keys(event_data[key]);
hostsArr.forEach(host => {
if (!hosts[host]) {
// host has not been added to hosts object
// add now
hosts[host] = {};
}
hosts[host][key] = event_data[key][host];
});
} else {
// array of hosts from each type ("changed", "dark", etc.)
hostsArr = Object.keys(event_data[key]);
hostsArr.forEach(host => {
if (!hosts[host]) {
// host has not been added to hosts object
// add now
hosts[host] = {};
}
if (!hosts[host][key]) {
// host doesn't have key
hosts[host][key] = 0;
}
hosts[host][key] += event_data[key][host];
});
}
});
// use the hosts data populate above to get the count
var count = {
ok : _.filter(hosts, function(o){
return !o.failures && !o.changed && o.ok > 0;
}),
skipped : _.filter(hosts, function(o){
return o.skipped > 0;
}),
unreachable : _.filter(hosts, function(o){
return o.dark > 0;
}),
failures : _.filter(hosts, function(o){
return o.failed === true;
}),
changed : _.filter(hosts, function(o){
return o.changed > 0;
})
};
// turn the count into an actual count, rather than a list of host
// names
Object.keys(count).forEach(key => {
count[key] = count[key].length;
});
return count;
},
getEvents: function(url) {
var val = $q.defer();
Rest.setUrl(url);
Rest.get()
.success(function(data) {
val.resolve({results: data.results,
next: data.next});
})
.error(function(obj, status) {
ProcessErrors(null, obj, status, null, {
hdr: 'Error!',
msg: `Could not get job events.
Returned status: ${status}`
});
val.reject(obj);
});
return val.promise;
},
deleteJob: function(job) {
deleteJob: function(workflow) {
Prompt({
hdr: 'Delete Job',
body: `<div class='Prompt-bodyQuery'>
Are you sure you want to delete the job below?
Are you sure you want to delete the workflow below?
</div>
<div class='Prompt-bodyTarget'>
#${job.id} ${$filter('sanitize')(job.name)}
#${workflow.id} ${$filter('sanitize')(workflow.name)}
</div>`,
action: function() {
Wait('start');
@ -126,9 +38,9 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
actionText: 'DELETE'
});
},
cancelJob: function(job) {
cancelJob: function(workflow) {
var doCancel = function() {
Rest.setUrl(job.url + 'cancel');
Rest.setUrl(workflow.url + 'cancel');
Rest.post({})
.success(function() {
Wait('stop');
@ -139,23 +51,23 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
$('#prompt-modal').modal('hide');
ProcessErrors(null, obj, status, null, {
hdr: 'Error!',
msg: `Could not cancel job.
msg: `Could not cancel workflow.
Returned status: ${status}`
});
});
};
Prompt({
hdr: 'Cancel Job',
hdr: 'Cancel Workflow',
body: `<div class='Prompt-bodyQuery'>
Are you sure you want to cancel the job below?
Are you sure you want to cancel the workflow below?
</div>
<div class='Prompt-bodyTarget'>
#${job.id} ${$filter('sanitize')(job.name)}
#${workflow.id} ${$filter('sanitize')(workflow.name)}
</div>`,
action: function() {
Wait('start');
Rest.setUrl(job.url + 'cancel');
Rest.setUrl(workflow.url + 'cancel');
Rest.get()
.success(function(data) {
if (data.can_cancel === true) {
@ -179,7 +91,7 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
$('#prompt-modal').modal('hide');
ProcessErrors(null, obj, status, null, {
hdr: 'Error!',
msg: `Could not cancel job.
msg: `Could not cancel workflow.
Returned status: ${status}`
});
});
@ -188,7 +100,7 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
});
},
relaunchJob: function(scope) {
InitiatePlaybookRun({ scope: scope, id: scope.job.id,
InitiatePlaybookRun({ scope: scope, id: scope.workflow.id,
relaunch: true });
}
};

View File

@ -0,0 +1,11 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import workflowStatusBar from './workflow-status-bar.directive';
export default
angular.module('workflowStatusBarDirective', [])
.directive('workflowStatusBar', workflowStatusBar);

View File

@ -0,0 +1,80 @@
@import '../../shared/branding/colors.default.less';
.WorkflowStatusBar {
display: flex;
flex: 0 0 auto;
width: 100%;
margin-top: 10px;
}
.WorkflowStatusBar-ok,
.WorkflowStatusBar-changed,
.WorkflowStatusBar-unreachable,
.WorkflowStatusBar-failures,
.WorkflowStatusBar-skipped,
.WorkflowStatusBar-noData {
height: 15px;
border-top: 5px solid @default-bg;
border-bottom: 5px solid @default-bg;
}
.WorkflowStatusBar-ok {
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 {
background-color: @default-err;
flex: 0 0 auto;
}
.WorkflowStatusBar-skipped {
background-color: @default-link;
flex: 0 0 auto;
}
.WorkflowStatusBar-noData {
background-color: @default-icon-hov;
flex: 1 0 auto;
}
.WorkflowStatusBar-tooltipLabel {
text-transform: uppercase;
margin-right: 15px;
}
.WorkflowStatusBar-tooltipBadge {
border-radius: 5px;
}
.WorkflowStatusBar-tooltipBadge--ok {
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 {
background-color: @default-err;
}

View File

@ -0,0 +1,43 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* 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
scope.$watch('count', function(val) {
if (val) {
Object.keys(val).forEach(key => {
// reposition the hosts status bar by setting
// the various flex values to the count of
// those hosts
$(`.WorkflowStatusBar-${key}`)
.css('flex', `${val[key]} 0 auto`);
// set the tooltip to give how many hosts 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>`;
}
});
// if there are any hosts that have finished, don't
// show default grey bar
scope.hostsFinished = (Object
.keys(val)
.filter(key => (val[key] > 0)).length > 0);
}
});
}
};
}];

View File

@ -0,0 +1,26 @@
<div class="WorkflowStatusBar">
<div class="WorkflowStatusBar-ok"
data-placement="top"
aw-tool-tip="{{okCountTip}}"
data-tip-watch="okCountTip"></div>
<div class="WorkflowStatusBar-changed"
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>
<div class="WorkflowStatusBar-noData"
aw-tool-tip="NO HOSTS FINISHED"
ng-hide="hostsFinished"
data-placement="top"></div>
</div>