mirror of
https://github.com/ansible/awx.git
synced 2026-01-17 04:31:21 -03:30
Adding job results bar files
This commit is contained in:
parent
29c8ef72e0
commit
fcb9d8b6b5
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 getTowerLink = function(key) {
|
||||
if ($scope.job.related[key]) {
|
||||
@ -57,7 +57,7 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', '$scope', 'ParseTypeCh
|
||||
$scope.toggleStdoutFullscreen = function() {
|
||||
$scope.stdoutFullScreen = !$scope.stdoutFullScreen;
|
||||
};
|
||||
|
||||
|
||||
$scope.deleteJob = function() {
|
||||
jobResultsService.deleteJob($scope.job);
|
||||
};
|
||||
@ -66,9 +66,23 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', '$scope', 'ParseTypeCh
|
||||
jobResultsService.cancelJob($scope.job);
|
||||
};
|
||||
|
||||
// Set the job status
|
||||
// TODO: pull from websockets
|
||||
$scope.job_status = {"status": ""};
|
||||
$scope.job_status.status = (jobData.status === 'waiting' ||
|
||||
jobData.status === 'new') ? 'pending' : jobData.status;
|
||||
$rootScope.event_socket.on("job_events-" + $scope.job.id, function(data) {
|
||||
console.log(data);
|
||||
if(data.event_name === "playbook_on_stats"){
|
||||
// get the data for populating the host status bar
|
||||
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-panelHeaderText">
|
||||
<i class="JobResults-statusResultIcon
|
||||
fa icon-job-{{ job_status.status }}">
|
||||
fa icon-job-{{ job.status }}">
|
||||
</i>
|
||||
{{ job.name }}
|
||||
</div>
|
||||
@ -483,7 +483,7 @@
|
||||
Elapsed
|
||||
</div>
|
||||
<span class="badge List-titleBadge">
|
||||
{{ job_status.elapsed || "00:00:01"}}
|
||||
{{ job.elapsed * 1000 | duration: "hh:mm:ss" }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -513,6 +513,8 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<host-status-bar></host-status-bar>
|
||||
<!-- <standard-out-log stdout-endpoint="job.related.stdout"></standard-out-log> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -74,6 +74,20 @@ export default {
|
||||
val.reject(data);
|
||||
});
|
||||
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'),
|
||||
|
||||
@ -5,8 +5,67 @@
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default ['Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', function (Prompt, $filter, Wait, Rest, $state, ProcessErrors) {
|
||||
return {
|
||||
export default ['Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', '$rootScope', function (Prompt, $filter, Wait, Rest, $state, ProcessErrors, $rootScope) {
|
||||
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) {
|
||||
Prompt({
|
||||
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 jobResultsService from './job-results.service';
|
||||
import hostStatusBarDirective from './host-status-bar/main';
|
||||
import durationFilter from './duration.filter';
|
||||
|
||||
export default
|
||||
angular.module('jobResults', [])
|
||||
angular.module('jobResults', [hostStatusBarDirective.name])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}])
|
||||
.service('jobResultsService', jobResultsService);
|
||||
.service('jobResultsService', jobResultsService)
|
||||
.filter('duration', durationFilter);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user