mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 17:37:37 -02:30
Adding job results bar files
This commit is contained in:
120
awx/ui/client/src/job-results/duration.filter.js
Normal file
120
awx/ui/client/src/job-results/duration.filter.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// TODO: come back and get this library
|
||||||
|
export default function() {
|
||||||
|
var DURATION_FORMATS_SPLIT = /((?:[^ydhms']+)|(?:'(?:[^']|'')*')|(?:y+|d+|h+|m+|s+))(.*)/;
|
||||||
|
var DURATION_FORMATS = {
|
||||||
|
'y': { // years
|
||||||
|
// "longer" years are not supported
|
||||||
|
value: 365 * 24 * 60 * 60 * 1000
|
||||||
|
},
|
||||||
|
'yy': {
|
||||||
|
value: 'y',
|
||||||
|
pad: 2
|
||||||
|
},
|
||||||
|
'd': { // days
|
||||||
|
value: 24 * 60 * 60 * 1000
|
||||||
|
},
|
||||||
|
'dd': {
|
||||||
|
value: 'd',
|
||||||
|
pad: 2
|
||||||
|
},
|
||||||
|
'h': { // hours
|
||||||
|
value: 60 * 60 * 1000
|
||||||
|
},
|
||||||
|
'hh': { // padded hours
|
||||||
|
value: 'h',
|
||||||
|
pad: 2
|
||||||
|
},
|
||||||
|
'm': { // minutes
|
||||||
|
value: 60 * 1000
|
||||||
|
},
|
||||||
|
'mm': { // padded minutes
|
||||||
|
value: 'm',
|
||||||
|
pad: 2
|
||||||
|
},
|
||||||
|
's': { // seconds
|
||||||
|
value: 1000
|
||||||
|
},
|
||||||
|
'ss': { // padded seconds
|
||||||
|
value: 's',
|
||||||
|
pad: 2
|
||||||
|
},
|
||||||
|
'sss': { // milliseconds
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
'ssss': { // padded milliseconds
|
||||||
|
value: 'sss',
|
||||||
|
pad: 4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function _parseFormat(string) {
|
||||||
|
// @inspiration AngularJS date filter
|
||||||
|
var parts = [];
|
||||||
|
var format = string;
|
||||||
|
while(format) {
|
||||||
|
var match = DURATION_FORMATS_SPLIT.exec(format);
|
||||||
|
if (match) {
|
||||||
|
parts = parts.concat(match.slice(1));
|
||||||
|
|
||||||
|
format = parts.pop();
|
||||||
|
} else {
|
||||||
|
parts.push(format);
|
||||||
|
format = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
function _formatDuration(timestamp, format) {
|
||||||
|
var text = '';
|
||||||
|
var values = { };
|
||||||
|
format.filter(function(format) { // filter only value parts of format
|
||||||
|
return DURATION_FORMATS.hasOwnProperty(format);
|
||||||
|
}).map(function(format) { // get formats with values only
|
||||||
|
var config = DURATION_FORMATS[format];
|
||||||
|
if(config.hasOwnProperty('pad')) {
|
||||||
|
return config.value;
|
||||||
|
} else {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}).filter(function(format, index, arr) { // remove duplicates
|
||||||
|
return (arr.indexOf(format) === index);
|
||||||
|
}).map(function(format) { // get format configurations with values
|
||||||
|
return angular.extend({
|
||||||
|
name: format,
|
||||||
|
}, DURATION_FORMATS[format]);
|
||||||
|
}).sort(function(a, b) { // sort formats descending by value
|
||||||
|
return b.value - a.value;
|
||||||
|
}).forEach(function(format) { // create values for format parts
|
||||||
|
var value = values[format.name] = Math.floor(timestamp / format.value);
|
||||||
|
timestamp = timestamp - (value * format.value);
|
||||||
|
});
|
||||||
|
format.forEach(function(part) {
|
||||||
|
var format = DURATION_FORMATS[part];
|
||||||
|
if(format) {
|
||||||
|
var value = values[format.value];
|
||||||
|
text += (format.hasOwnProperty('pad') ? _padNumber(value, Math.max(format.pad, value.toString().length)) : values[part]);
|
||||||
|
} else {
|
||||||
|
text += part.replace(/(^'|'$)/g, '').replace(/''/g, '\'');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
function _padNumber(number, len) {
|
||||||
|
return ((new Array(len + 1)).join('0') + number).slice(-len);
|
||||||
|
}
|
||||||
|
return function(value, format) {
|
||||||
|
if (typeof value !== "number") {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var timestamp = parseInt(value.valueOf(), 10);
|
||||||
|
if(isNaN(timestamp)) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return _formatDuration(
|
||||||
|
timestamp,
|
||||||
|
_parseFormat(format)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
@import '../../shared/branding/colors.default.less';
|
||||||
|
|
||||||
|
.JobResults-hostStatusBar{
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.JobResults-hostStatusBar--ok{
|
||||||
|
background-color: @default-succ;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.JobResults-hostStatusBar--changed{
|
||||||
|
background-color: @default-warning;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.JobResults-hostStatusBar--unreachable{
|
||||||
|
background-color: @default-unreachable;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.JobResults-hostStatusBar--failures{
|
||||||
|
background-color: @default-err;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.JobResults-hostStatusBar--skipped{
|
||||||
|
background-color: @default-link;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
// import hostStatusBarController from './host-status-bar.controller';
|
||||||
|
export default [ 'templateUrl',
|
||||||
|
function(templateUrl) {
|
||||||
|
return {
|
||||||
|
scope: {
|
||||||
|
jobData: '='
|
||||||
|
},
|
||||||
|
templateUrl: templateUrl('job-results/host-status-bar/host-status-bar'),
|
||||||
|
restrict: 'E',
|
||||||
|
// controller: standardOutLogController,
|
||||||
|
link: function(scope) {
|
||||||
|
// All of our DOM related stuff will go in here
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<div class="JobResults-hostStatusBar">
|
||||||
|
<div class="JobResults-hostStatusBar--ok"></div>
|
||||||
|
<div class="JobResults-hostStatusBar--changed"></div>
|
||||||
|
<div class="JobResults-hostStatusBar--failures"></div>
|
||||||
|
<div class="JobResults-hostStatusBar--unreachable"></div>
|
||||||
|
<div class="JobResults-hostStatusBar--skipped"></div>
|
||||||
|
</div>
|
||||||
11
awx/ui/client/src/job-results/host-status-bar/main.js
Normal file
11
awx/ui/client/src/job-results/host-status-bar/main.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
import hostStatusBar from './host-status-bar.directive';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('hostStatusBarDirective', [])
|
||||||
|
.directive('hostStatusBar', hostStatusBar);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export default ['jobData', 'jobDataOptions', 'jobLabels', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', function(jobData, jobDataOptions, jobLabels, $scope, ParseTypeChange, ParseVariableString, jobResultsService) {
|
export default ['jobData', 'jobDataOptions', 'jobLabels', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', '$rootScope', function(jobData, jobDataOptions, jobLabels, $scope, ParseTypeChange, ParseVariableString, jobResultsService, $rootScope) {
|
||||||
var getTowerLinks = function() {
|
var getTowerLinks = function() {
|
||||||
var getTowerLink = function(key) {
|
var getTowerLink = function(key) {
|
||||||
if ($scope.job.related[key]) {
|
if ($scope.job.related[key]) {
|
||||||
@@ -66,9 +66,23 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', '$scope', 'ParseTypeCh
|
|||||||
jobResultsService.cancelJob($scope.job);
|
jobResultsService.cancelJob($scope.job);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set the job status
|
$rootScope.event_socket.on("job_events-" + $scope.job.id, function(data) {
|
||||||
// TODO: pull from websockets
|
console.log(data);
|
||||||
$scope.job_status = {"status": ""};
|
if(data.event_name === "playbook_on_stats"){
|
||||||
$scope.job_status.status = (jobData.status === 'waiting' ||
|
// get the data for populating the host status bar
|
||||||
jobData.status === 'new') ? 'pending' : jobData.status;
|
jobResultsService.getHostStatusBarCounts(data.event_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$rootScope.$on('JobStatusChange-jobDetails', function(e, data) {
|
||||||
|
console.log(data);
|
||||||
|
if (parseInt(data.unified_job_id, 10) === parseInt($scope.job.id,10)) {
|
||||||
|
$scope.job.status = data.status;
|
||||||
|
|
||||||
|
// $scope.job.elapsed = data.elapsed;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
|||||||
@@ -447,7 +447,7 @@
|
|||||||
<div class="StandardOut-panelHeader">
|
<div class="StandardOut-panelHeader">
|
||||||
<div class="StandardOut-panelHeaderText">
|
<div class="StandardOut-panelHeaderText">
|
||||||
<i class="JobResults-statusResultIcon
|
<i class="JobResults-statusResultIcon
|
||||||
fa icon-job-{{ job_status.status }}">
|
fa icon-job-{{ job.status }}">
|
||||||
</i>
|
</i>
|
||||||
{{ job.name }}
|
{{ job.name }}
|
||||||
</div>
|
</div>
|
||||||
@@ -483,7 +483,7 @@
|
|||||||
Elapsed
|
Elapsed
|
||||||
</div>
|
</div>
|
||||||
<span class="badge List-titleBadge">
|
<span class="badge List-titleBadge">
|
||||||
{{ job_status.elapsed || "00:00:01"}}
|
{{ job.elapsed * 1000 | duration: "hh:mm:ss" }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -513,6 +513,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<host-status-bar></host-status-bar>
|
||||||
|
<!-- <standard-out-log stdout-endpoint="job.related.stdout"></standard-out-log> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,6 +74,20 @@ export default {
|
|||||||
val.reject(data);
|
val.reject(data);
|
||||||
});
|
});
|
||||||
return val.promise;
|
return val.promise;
|
||||||
|
}],
|
||||||
|
jobEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
|
||||||
|
if (!$rootScope.event_socket) {
|
||||||
|
$rootScope.event_socket = Socket({
|
||||||
|
scope: $rootScope,
|
||||||
|
endpoint: "job_events"
|
||||||
|
});
|
||||||
|
$rootScope.event_socket.init();
|
||||||
|
// returns should really be providing $rootScope.event_socket
|
||||||
|
// otherwise, we have to inject the entire $rootScope into the controller
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
templateUrl: templateUrl('job-results/job-results'),
|
templateUrl: templateUrl('job-results/job-results'),
|
||||||
|
|||||||
@@ -5,8 +5,67 @@
|
|||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
export default ['Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', function (Prompt, $filter, Wait, Rest, $state, ProcessErrors) {
|
export default ['Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', '$rootScope', function (Prompt, $filter, Wait, Rest, $state, ProcessErrors, $rootScope) {
|
||||||
return {
|
var val = {
|
||||||
|
getHostStatusBarCounts: function(event_data) {
|
||||||
|
var hosts = {};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
var 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.)
|
||||||
|
var 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;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return count;
|
||||||
|
},
|
||||||
deleteJob: function(job) {
|
deleteJob: function(job) {
|
||||||
Prompt({
|
Prompt({
|
||||||
hdr: 'Delete Job',
|
hdr: 'Delete Job',
|
||||||
@@ -100,4 +159,5 @@ export default ['Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors',
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
return val;
|
||||||
}];
|
}];
|
||||||
|
|||||||
@@ -6,10 +6,13 @@
|
|||||||
|
|
||||||
import route from './job-results.route.js';
|
import route from './job-results.route.js';
|
||||||
import jobResultsService from './job-results.service';
|
import jobResultsService from './job-results.service';
|
||||||
|
import hostStatusBarDirective from './host-status-bar/main';
|
||||||
|
import durationFilter from './duration.filter';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('jobResults', [])
|
angular.module('jobResults', [hostStatusBarDirective.name])
|
||||||
.run(['$stateExtender', function($stateExtender) {
|
.run(['$stateExtender', function($stateExtender) {
|
||||||
$stateExtender.addState(route);
|
$stateExtender.addState(route);
|
||||||
}])
|
}])
|
||||||
.service('jobResultsService', jobResultsService);
|
.service('jobResultsService', jobResultsService)
|
||||||
|
.filter('duration', durationFilter);
|
||||||
|
|||||||
Reference in New Issue
Block a user