mirror of
https://github.com/ansible/awx.git
synced 2026-01-15 11:50:42 -03:30
Merge pull request #1589 from leigh-johnson/JobDetailBugs
Fixes Job Details 404s, event summary failed filter bug
This commit is contained in:
commit
caab667684
@ -42,7 +42,7 @@ export default
|
||||
.factory('DigestEvent', ['$rootScope', '$log', 'UpdatePlayStatus', 'UpdateHostStatus', 'AddHostResult',
|
||||
'GetElapsed', 'UpdateTaskStatus', 'JobIsFinished', 'AddNewTask', 'AddNewPlay',
|
||||
function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, GetElapsed,
|
||||
UpdateTaskStatus, JobIsFinished, AddNewTask, AddNewPlay, longDateFilter) {
|
||||
UpdateTaskStatus, JobIsFinished, AddNewTask, AddNewPlay) {
|
||||
return function(params) {
|
||||
|
||||
var scope = params.scope,
|
||||
@ -485,8 +485,7 @@ export default
|
||||
created = params.created,
|
||||
msg = params.message,
|
||||
item = params.item,
|
||||
counter = params.counter,
|
||||
h, host;
|
||||
counter = params.counter;
|
||||
|
||||
if (scope.jobData.hostSummaries[host_id] !== undefined) {
|
||||
scope.jobData.hostSummaries[host_id].ok += (status === 'successful') ? 1 : 0;
|
||||
@ -920,7 +919,7 @@ export default
|
||||
order: 'host_name,counter',
|
||||
};
|
||||
JobDetailService.getRelatedJobEvents(scope.job.id, params).success(function(res){
|
||||
scope.hostResults = JobDetailService.processHostEvents(res.results)
|
||||
scope.hostResults = JobDetailService.processHostEvents(res.results);
|
||||
scope.hostResultsLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
@ -110,7 +110,6 @@
|
||||
var init = function(){
|
||||
$scope.hostName = $stateParams.hostName;
|
||||
// create filter dropdown
|
||||
console.log($stateParams)
|
||||
CreateSelect2({
|
||||
element: '.HostEvents-select',
|
||||
multiple: false
|
||||
|
||||
@ -8,14 +8,13 @@
|
||||
['$scope', '$rootScope', '$stateParams', 'Wait', 'JobDetailService', 'jobSocket', 'DrawGraph', function($scope, $rootScope, $stateParams, Wait, JobDetailService, jobSocket, DrawGraph){
|
||||
|
||||
var page_size = 200;
|
||||
|
||||
$scope.loading = $scope.hosts.length > 0 ? false : true;
|
||||
$scope.filter = 'all';
|
||||
$scope.search = null;
|
||||
|
||||
var buildTooltips = function(hosts){
|
||||
var buildGraph = function(hosts){
|
||||
// status waterfall: unreachable > failed > changed > ok > skipped
|
||||
var count, grammar, text = {};
|
||||
var count;
|
||||
count = {
|
||||
ok : _.filter(hosts, function(o){
|
||||
return o.failures === 0 && o.changed === 0 && o.ok > 0;
|
||||
@ -33,6 +32,25 @@
|
||||
return o.changed > 0;
|
||||
})
|
||||
};
|
||||
return count;
|
||||
};
|
||||
var socketListener = function(){
|
||||
// emitted by the API in the same function used to persist host summary data
|
||||
// JobEvent.update_host_summary_from_stats() from /awx/main.models.jobs.py
|
||||
jobSocket.on('summary_complete', function(data) {
|
||||
// discard socket msgs we don't care about in this context
|
||||
if ($stateParams.id == data['unified_job_id']){
|
||||
init();
|
||||
}
|
||||
});
|
||||
// UnifiedJob.def socketio_emit_status() from /awx/main.models.unified_jobs.py
|
||||
jobSocket.on('status_changed', function(data) {
|
||||
if ($stateParams.id == data['unified_job_id']){
|
||||
$scope.status = data['status'];
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.buildTooltip = function(n, status){
|
||||
var grammar = function(n, status){
|
||||
var dict = {
|
||||
0: 'No host events were ',
|
||||
@ -46,28 +64,13 @@
|
||||
return n !== 0 ? n + dict[n] + status : dict[n] + status;
|
||||
}
|
||||
};
|
||||
/*
|
||||
_.forIn(count, function(value, key){
|
||||
text[key] = grammar(value.length, key);
|
||||
});
|
||||
return {count, text}
|
||||
*/
|
||||
return grammar(n, status)
|
||||
};
|
||||
var socketListener = function(){
|
||||
// emitted by the API in the same function used to persist host summary data
|
||||
// JobEvent.update_host_summary_from_stats() from /awx/main.models.jobs.py
|
||||
jobSocket.on('summary_complete', function(data) {
|
||||
// discard socket msgs we don't care about in this context
|
||||
if ($stateParams.id == data['unified_job_id']){
|
||||
init()
|
||||
}
|
||||
});
|
||||
// UnifiedJob.def socketio_emit_status() from /awx/main.models.unified_jobs.py
|
||||
jobSocket.on('status_changed', function(data) {
|
||||
if ($stateParams.id == data['unified_job_id']){
|
||||
$scope.status = data['status'];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getNextPage = function(){
|
||||
if ($scope.next){
|
||||
JobDetailService.getNextPage($scope.next).success(function(res){
|
||||
@ -80,14 +83,14 @@
|
||||
}
|
||||
};
|
||||
$scope.search = function(){
|
||||
Wait('start')
|
||||
Wait('start');
|
||||
JobDetailService.getJobHostSummaries($stateParams.id, {
|
||||
page_size: page_size,
|
||||
host_name__icontains: $scope.searchTerm,
|
||||
}).success(function(res){
|
||||
$scope.hosts = res.results;
|
||||
$scope.next = res.next;
|
||||
Wait('stop')
|
||||
Wait('stop');
|
||||
})
|
||||
};
|
||||
$scope.setFilter = function(filter){
|
||||
@ -113,12 +116,12 @@
|
||||
$scope.next = res.next;
|
||||
});
|
||||
}
|
||||
var get = filter == 'all' ? getAll() : getFailed()
|
||||
var get = filter == 'all' ? getAll() : getFailed();
|
||||
};
|
||||
|
||||
$scope.$watchCollection('hosts', function(curr, prev){
|
||||
$scope.tooltips = buildTooltips(curr);
|
||||
DrawGraph({count: $scope.tooltips.count, resize:true});
|
||||
$scope.count = buildGraph(curr);
|
||||
DrawGraph({count: $scope.count, resize:true});
|
||||
});
|
||||
|
||||
var init = function(){
|
||||
@ -129,9 +132,9 @@
|
||||
$scope.next = res.next;
|
||||
Wait('stop');
|
||||
});
|
||||
JobDetailService.getJob($stateParams.id)
|
||||
JobDetailService.getJob({id: $stateParams.id})
|
||||
.success(function(res){
|
||||
$scope.status = status;
|
||||
$scope.status = res.results[0].status;
|
||||
});
|
||||
};
|
||||
socketListener();
|
||||
|
||||
@ -14,9 +14,9 @@
|
||||
<div class="btn-group" >
|
||||
<button
|
||||
ng-click="setFilter('all')"
|
||||
class="JobDetail-tableToggle btn btn-xs" ng-class="{'btn-default': filter == 'failed', 'btn-primary': filter == 'all'}">All</button>
|
||||
class="JobDetail-tableToggle btn btn-xs" ng-class="{'btn-default': filter === 'failed', 'btn-primary': filter === 'all'}">All</button>
|
||||
<button ng-click="setFilter('failed')"
|
||||
ng-class="{'btn-default': filter == 'all', 'btn-primary': filter == 'failed'}" class="JobDetail-tableToggle btn btn-xs">Failed</button>
|
||||
ng-class="{'btn-default': filter === 'all', 'btn-primary': filter === 'failed'}" ng-disabled='count.failures == 0' class="JobDetail-tableToggle btn btn-xs">Failed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -40,11 +40,11 @@
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name})" aw-tool-tip="View events" data-placement="top">{{ host.host_name }}</a>
|
||||
</td>
|
||||
<td class="List-tableCell col-lg-6 col-md-5 col-sm-5 col-xs-5 badge-column">
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'ok'})" aw-tool-tip="{{ tooltips.text.ok }}" data-placement="top" ng-hide="host.ok == 0"><span class="badge successful-hosts">{{ host.ok - host.changed }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'changed'})" aw-tool-tip="{{ tooltips.text.changed }}" data-placement="top" ng-hide="host.changed == 0"><span class="badge changed-hosts">{{ host.changed }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'skipped'})" aw-tool-tip="{{ tooltips.text.skipped }}" data-placement="top" ng-hide="host.skipped == 0"><span class="badge skipped-hosts">{{ host.skipped }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'unreachable'})" aw-tool-tip="{{ tooltips.text.unreachable}}" data-placement="top" ng-hide="host.dark == 0"><span class="badge unreachable-hosts">{{ host.dark }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.name, filter: 'failed'})" aw-tool-tip="{{ tooltips.text.failures}}" data-placement="top" ng-hide="host.failed == 0"><span class="badge failed-hosts">{{ host.failures }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'ok'})" aw-tool-tip="{{ buildTooltip(host.ok - host.changed, 'ok') }}" data-placement="top" ng-hide="host.ok == 0"><span class="badge successful-hosts">{{ host.ok - host.changed }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'changed'})" aw-tool-tip="{{buildTooltip(host.changed, 'changed')}}" data-placement="top" ng-hide="host.changed == 0"><span class="badge changed-hosts">{{ host.changed }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'skipped'})" aw-tool-tip="{{buildTooltip(host.skipped, 'skipped')}}" data-placement="top" ng-hide="host.skipped == 0"><span class="badge skipped-hosts">{{ host.skipped }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'unreachable'})" aw-tool-tip="{{buildTooltip(host.dark, 'unreachable')}" data-placement="top" ng-hide="host.dark == 0"><span class="badge unreachable-hosts">{{ host.dark }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.name, filter: 'failed'})" aw-tool-tip="{{ buildTooltip(host.failures, 'failed')}}" data-placement="top" ng-hide="host.failed == 0"><span class="badge failed-hosts">{{ host.failures }}</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="hosts.length === 0 && status === 'pending'">
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'jobDetail.host-summary',
|
||||
resolve: {
|
||||
jobSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
|
||||
var job_socket = Socket({
|
||||
scope: $rootScope,
|
||||
endpoint: "jobs"
|
||||
});
|
||||
job_socket.init();
|
||||
return job_socket;
|
||||
}]
|
||||
},
|
||||
views:{
|
||||
'host-summary': {
|
||||
controller: 'HostSummaryController',
|
||||
templateUrl: templateUrl('job-detail/host-summary/host-summary'),
|
||||
}
|
||||
}
|
||||
};
|
||||
15
awx/ui/client/src/job-detail/host-summary/main.js
Normal file
15
awx/ui/client/src/job-detail/host-summary/main.js
Normal file
@ -0,0 +1,15 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './host-summary.route';
|
||||
import controller from './host-summary.controller';
|
||||
|
||||
export default
|
||||
angular.module('jobDetail.hostSummary', [])
|
||||
.controller('HostSummaryController', controller)
|
||||
.run(['$stateExtender', function($stateExtender){
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
@ -413,7 +413,8 @@ export default
|
||||
var params = {
|
||||
order_by: 'id'
|
||||
};
|
||||
JobDetailService.getJobPlays(scope.job.id, params)
|
||||
if (scope.job.summary_fields.unified_job_template.unified_job_type == 'job'){
|
||||
JobDetailService.getJobPlays(scope.job.id, params)
|
||||
.success( function(data) {
|
||||
scope.next_plays = data.next;
|
||||
if (data.results.length > 0) {
|
||||
@ -498,6 +499,7 @@ export default
|
||||
}
|
||||
scope.$emit('LoadTasks', events_url);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -513,9 +515,10 @@ export default
|
||||
scope.hostResultsLoading = true;
|
||||
|
||||
// Load the job record
|
||||
JobDetailService.getJob(job_id)
|
||||
.success(function(data) {
|
||||
var i;
|
||||
JobDetailService.getJob({id: job_id})
|
||||
.success(function(res) {
|
||||
var i,
|
||||
data = res.results[0];
|
||||
scope.job = data;
|
||||
scope.job_template_name = data.name;
|
||||
scope.project_name = (data.summary_fields.project) ? data.summary_fields.project.name : '';
|
||||
|
||||
@ -157,7 +157,7 @@
|
||||
<!--- end of results-->
|
||||
|
||||
<!--beginning of details-->
|
||||
<div id="job-detail-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen">
|
||||
<div id="job-detail-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen && job.summary_fields.unified_job_template.unified_job_type == 'job'">
|
||||
<div class="JobDetail-panelHeader">
|
||||
<div class="JobDetail-expandContainer">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessDetail" href="" ng-click="toggleLessDetail()">
|
||||
@ -376,21 +376,21 @@
|
||||
<div class="JobDetail-rightSide">
|
||||
|
||||
<!--beginning of events summary-->
|
||||
<div id="events-summary-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen">
|
||||
<div id="events-summary-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen && job.summary_fields.unified_job_template.unified_job_type == 'job'">
|
||||
<div class="JobDetail-panelHeader">
|
||||
<div class="JobDetail-expandContainer">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessEvents" href="" ng-click="toggleLessEvents()">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessEvents" ui-sref="jobDetail.host-summary" ng-click="toggleLessEvents()">
|
||||
EVENT SUMMARY<i class="JobDetail-expandArrow fa fa-caret-left"></i>
|
||||
</a>
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessEvents" href="" ng-click="toggleLessEvents()">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessEvents" ui-sref="jobDetail" ng-click="toggleLessEvents()">
|
||||
EVENT SUMMARY<i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Host Summary view -->
|
||||
<div id="events-summary" ng-hide="lessEvents">
|
||||
<div ui-view="host-summary@jobDetail"></div>
|
||||
<div id="events-summary" ng-hide="lessEvents">
|
||||
<div ui-view="host-summary"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end of events summary-->
|
||||
|
||||
@ -38,19 +38,9 @@ export default {
|
||||
endpoint: "jobs"
|
||||
});
|
||||
job_socket.init();
|
||||
// returns should really be providing $rootScope.job_socket
|
||||
// otherwise, we have to inject the entire $rootScope into the controller
|
||||
return job_socket;
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
'': {
|
||||
templateUrl: templateUrl('job-detail/job-detail'),
|
||||
controller: 'JobDetailController',
|
||||
},
|
||||
'host-summary@jobDetail': {
|
||||
templateUrl: templateUrl('job-detail/host-summary/host-summary'),
|
||||
controller: HostSummaryController
|
||||
}
|
||||
}
|
||||
templateUrl: templateUrl('job-detail/job-detail'),
|
||||
controller: 'JobDetailController'
|
||||
};
|
||||
|
||||
@ -2,13 +2,10 @@ export default
|
||||
['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', function($rootScope, Rest, GetBasePath, ProcessErrors){
|
||||
return {
|
||||
|
||||
/*
|
||||
* For ES6
|
||||
* it might be useful to set some default params here, e.g.
|
||||
* getJobHostSummaries: function(id, page_size=200, order='host_name'){}
|
||||
* without ES6, we'd have to supply defaults like this:
|
||||
* this.page_size = params.page_size ? params.page_size : 200;
|
||||
*/
|
||||
stringifyParams: function(params){
|
||||
return _.reduce(params, (result, value, key) => {
|
||||
return result + key + '=' + value + '&'}, '');
|
||||
},
|
||||
|
||||
// the the API passes through Ansible's event_data response
|
||||
// we need to massage away the verbose and redundant properties
|
||||
@ -129,12 +126,7 @@ export default
|
||||
// ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter
|
||||
getRelatedJobEvents: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_events/?';
|
||||
Object.keys(params).forEach(function(key, index) {
|
||||
// the API is tolerant of extra ampersands
|
||||
// ?&event=playbook_on_start == ?event=playbook_on_stats
|
||||
url = url + '&' + key + '=' + params[key];
|
||||
});
|
||||
url = url + id + '/job_events/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
@ -162,11 +154,7 @@ export default
|
||||
// e.g. ?page_size=200&order=host_name
|
||||
getJobHostSummaries: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_host_summaries/?';
|
||||
Object.keys(params).forEach(function(key, index) {
|
||||
// the API is tolerant of extra ampersands
|
||||
url = url + '&' + key + '=' + params[key];
|
||||
});
|
||||
url = url + id + '/job_host_summaries/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
@ -181,11 +169,7 @@ export default
|
||||
// e.g. ?page_size=200
|
||||
getJobPlays: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_plays/?';
|
||||
Object.keys(params).forEach(function(key, index) {
|
||||
// the API is tolerant of extra ampersands
|
||||
url = url + '&' + key + '=' + params[key];
|
||||
});
|
||||
url = url + id + '/job_plays/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
@ -198,11 +182,7 @@ export default
|
||||
},
|
||||
getJobTasks: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_tasks/?';
|
||||
Object.keys(params).forEach(function(key, index) {
|
||||
// the API is tolerant of extra ampersands
|
||||
url = url + '&' + key + '=' + params[key];
|
||||
});
|
||||
url = url + id + '/job_tasks/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
@ -213,9 +193,8 @@ export default
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
getJob: function(id){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id;
|
||||
getJob: function(params){
|
||||
var url = GetBasePath('unified_jobs') + '?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
|
||||
@ -9,11 +9,13 @@ import controller from './job-detail.controller';
|
||||
import service from './job-detail.service';
|
||||
import hostEvents from './host-events/main';
|
||||
import hostEvent from './host-event/main';
|
||||
import hostSummary from './host-summary/main';
|
||||
|
||||
export default
|
||||
angular.module('jobDetail', [
|
||||
hostEvents.name,
|
||||
hostEvent.name
|
||||
hostEvent.name,
|
||||
hostSummary.name
|
||||
])
|
||||
.controller('JobDetailController', controller)
|
||||
.service('JobDetailService', service)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user