mirror of
https://github.com/ansible/awx.git
synced 2026-01-13 11:00:03 -03:30
Fix assorted Job Details bugs
* fix strict equality check that broke host summary event signals, fix hosts value provided to host-event modal, add all jobDetail.* routes to event emitter used by stdout, resolves #1733 * fix over-broad clear on intervals driving job details / stdout updates #1799 * fixes assorted job details audit issues * feedback on PR #1799
This commit is contained in:
parent
ea25742ea5
commit
1af0a172d3
@ -670,15 +670,15 @@ var tower = angular.module('Tower', [
|
||||
// accessing.
|
||||
if ($state.is('jobs')) {
|
||||
$rootScope.$emit('JobStatusChange-jobs', data);
|
||||
} else if ($state.is('jobDetail') ||
|
||||
} else if ($state.includes('jobDetail') ||
|
||||
$state.is('adHocJobStdout') ||
|
||||
$state.is('inventorySyncStdout') ||
|
||||
$state.is('managementJobStdout') ||
|
||||
$state.is('scmUpdateStdout')) {
|
||||
$state.is('scmUpdateStdout')){
|
||||
|
||||
$log.debug("sending status to standard out");
|
||||
$rootScope.$emit('JobStatusChange-jobStdout', data);
|
||||
} if ($state.is('jobDetail')) {
|
||||
} if ($state.includes('jobDetail')) {
|
||||
$rootScope.$emit('JobStatusChange-jobDetails', data);
|
||||
} else if ($state.is('dashboard')) {
|
||||
$rootScope.$emit('JobStatusChange-home', data);
|
||||
@ -739,10 +739,19 @@ var tower = angular.module('Tower', [
|
||||
}
|
||||
|
||||
// remove any lingering intervals
|
||||
if ($rootScope.jobDetailInterval) {
|
||||
// except on jobDetails.* states
|
||||
var jobDetailStates = [
|
||||
'jobDetail',
|
||||
'jobDetail.host-summary',
|
||||
'jobDetail.host-event.details',
|
||||
'jobDetail.host-event.json',
|
||||
'jobDetail.host-events',
|
||||
'jobDetail.host-event.stdout'
|
||||
];
|
||||
if ($rootScope.jobDetailInterval && !_.includes(jobDetailStates, next.name) ) {
|
||||
window.clearInterval($rootScope.jobDetailInterval);
|
||||
}
|
||||
if ($rootScope.jobStdOutInterval) {
|
||||
if ($rootScope.jobStdOutInterval && !_.includes(jobDetailStates, next.name) ) {
|
||||
window.clearInterval($rootScope.jobStdOutInterval);
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
|
||||
|
||||
export default
|
||||
['$stateParams', '$scope', '$state', 'Wait', 'JobDetailService', 'event', 'CodeMirror',
|
||||
function($stateParams, $scope, $state, Wait, JobDetailService, event, CodeMirror){
|
||||
['$stateParams', '$scope', '$state', 'Wait', 'JobDetailService', 'hostEvent',
|
||||
function($stateParams, $scope, $state, Wait, JobDetailService, hostEvent){
|
||||
|
||||
$scope.processEventStatus = JobDetailService.processEventStatus;
|
||||
$scope.hostResults = [];
|
||||
@ -56,7 +56,7 @@
|
||||
};
|
||||
|
||||
var init = function(){
|
||||
$scope.event = event;
|
||||
$scope.event = hostEvent;
|
||||
JobDetailService.getJobEventChildren($stateParams.taskId).success(function(res){
|
||||
$scope.hostResults = res.results;
|
||||
});
|
||||
|
||||
@ -12,10 +12,10 @@ var hostEventModal = {
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-detail/host-event/host-event-modal'),
|
||||
resolve: {
|
||||
event: ['JobDetailService','$stateParams', function(JobDetailService, $stateParams) {
|
||||
hostEvent: ['JobDetailService','$stateParams', function(JobDetailService, $stateParams) {
|
||||
return JobDetailService.getRelatedJobEvents($stateParams.id, {
|
||||
id: $stateParams.eventId
|
||||
}).success(function(res){ return res.results[0];});
|
||||
}).then(function(res){ return res.data.results[0];});
|
||||
}]
|
||||
},
|
||||
onExit: function(){
|
||||
|
||||
@ -72,8 +72,7 @@
|
||||
if (filter === 'ok'){
|
||||
return JobDetailService.getRelatedJobEvents($stateParams.id, {
|
||||
host_name: $stateParams.hostName,
|
||||
or__field__event: 'runner_on_ok',
|
||||
changed: false
|
||||
event__startswith: 'runner_on_ok'
|
||||
})
|
||||
.success(function(res){
|
||||
$scope.results = res.results;
|
||||
@ -93,7 +92,9 @@
|
||||
if (filter === 'failed'){
|
||||
return JobDetailService.getRelatedJobEvents($stateParams.id, {
|
||||
host_name: $stateParams.hostName,
|
||||
failed: true})
|
||||
failed: true,
|
||||
event__startswith: 'runner_on_failed'
|
||||
})
|
||||
.success(function(res){
|
||||
$scope.results = res.results;
|
||||
Wait('stop');
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
text-anchor: end !important;
|
||||
}
|
||||
.HostSummary-graph--changed{
|
||||
text-anchor: end !important;
|
||||
text-anchor: start !important;
|
||||
}
|
||||
.HostSUmmary-graph--unreachable{}
|
||||
.HostSummary-loading{
|
||||
|
||||
@ -53,13 +53,13 @@
|
||||
// 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){
|
||||
if (parseInt($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){
|
||||
if (parseInt($stateParams.id) === data.unified_job_id){
|
||||
$scope.status = data.status;
|
||||
}
|
||||
});
|
||||
@ -78,11 +78,6 @@
|
||||
return n !== 0 ? n + dict[n] + status : dict[n] + status;
|
||||
}
|
||||
};
|
||||
/*
|
||||
_.forIn(count, function(value, key){
|
||||
text[key] = grammar(value.length, key);
|
||||
});
|
||||
*/
|
||||
return grammar(n, status);
|
||||
};
|
||||
$scope.getNextPage = function(){
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
<div id="hosts-summary-section" class="section">
|
||||
<div class="JobDetail-instructions"><span class="badge">4</span> Please select a host below to view a summary of all associated tasks.</div>
|
||||
<div class="JobDetail-searchHeaderRow" ng-hide="hosts.length == 0">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
@ -43,17 +44,17 @@
|
||||
<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>
|
||||
<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.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'">
|
||||
<td colspan="5" class="col-lg-12 HostSummary-loading">Waiting...</td>
|
||||
<tr ng-show="status == 'pending'">
|
||||
<td colspan="5" class="col-lg-12 HostSummary-loading">Initiating job run.</td>
|
||||
</tr>
|
||||
<tr ng-show="hosts.length === 0 && status === 'running' ">
|
||||
<td colspan="5" class="col-lg-12 HostSummary-loading">Loading...</td>
|
||||
<tr ng-show="status == 'running'">
|
||||
<td colspan="5" class="col-lg-12 HostSummary-loading">Job is running. Summary will be available on completion.</td>
|
||||
</tr>
|
||||
<tr ng-show="status === 'failed' || status === 'successful' && hosts.length === 0 ">
|
||||
<tr ng-show="(status === 'failed' || status === 'successful') && hosts.length === 0 ">
|
||||
<td colspan="2" class="col-lg-12 HostSummary-loading">No matching hosts</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -65,7 +66,7 @@
|
||||
</div>
|
||||
|
||||
</div><!-- section -->
|
||||
|
||||
<div class="JobDetail-panelHeaderText">Host Status Summary</div>
|
||||
<div id="graph-section" class="JobDetail-graphSection">
|
||||
<svg></svg>
|
||||
</div>
|
||||
|
||||
@ -8,16 +8,21 @@ import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'jobDetail.host-summary',
|
||||
url: '/event-summary',
|
||||
/*
|
||||
resolve: {
|
||||
jobSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
|
||||
var job_socket = Socket({
|
||||
scope: $rootScope,
|
||||
endpoint: "jobs"
|
||||
});
|
||||
job_socket.init();
|
||||
return job_socket;
|
||||
var job_socket = Socket({
|
||||
scope: $rootScope,
|
||||
endpoint: "jobs"
|
||||
});
|
||||
if (!$rootScope.event_socket){
|
||||
job_socket.init();
|
||||
}
|
||||
return job_socket;
|
||||
}]
|
||||
},
|
||||
*/
|
||||
views:{
|
||||
'host-summary': {
|
||||
controller: 'HostSummaryController',
|
||||
|
||||
@ -7,6 +7,13 @@
|
||||
@breakpoint-md: 1200px;
|
||||
@breakpoint-sm: 420px;
|
||||
|
||||
.JobDetail-tasks.section{
|
||||
margin-top:40px;
|
||||
}
|
||||
.JobDetail-instructions{
|
||||
color: @default-interface-txt;
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
.JobDetail{
|
||||
.OnePlusOne-container(100%, @breakpoint-md);
|
||||
}
|
||||
@ -115,7 +122,7 @@
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
height: 50px;
|
||||
margin-top: 25px;
|
||||
margin-top: 20px;
|
||||
@media screen and(max-width: @breakpoint-sm){
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
export default
|
||||
[ '$location', '$rootScope', '$filter', '$scope', '$compile',
|
||||
'$stateParams', '$log', 'ClearScope', 'GetBasePath', 'Wait',
|
||||
'ProcessErrors', 'SelectPlay', 'SelectTask', 'Socket', 'GetElapsed',
|
||||
'ProcessErrors', 'SelectPlay', 'SelectTask', 'GetElapsed',
|
||||
'JobIsFinished', 'SetTaskStyles', 'DigestEvent', 'UpdateDOM', 'DeleteJob', 'PlaybookRun',
|
||||
'LoadPlays', 'LoadTasks', 'HostsEdit',
|
||||
'ParseVariableString', 'GetChoices', 'fieldChoices', 'fieldLabels',
|
||||
@ -21,7 +21,7 @@ export default
|
||||
function(
|
||||
$location, $rootScope, $filter, $scope, $compile, $stateParams,
|
||||
$log, ClearScope, GetBasePath, Wait, ProcessErrors,
|
||||
SelectPlay, SelectTask, Socket, GetElapsed,
|
||||
SelectPlay, SelectTask, GetElapsed,
|
||||
JobIsFinished,
|
||||
SetTaskStyles, DigestEvent, UpdateDOM, DeleteJob,
|
||||
PlaybookRun, LoadPlays, LoadTasks,
|
||||
@ -680,7 +680,7 @@ export default
|
||||
// there's a bunch of white space at the bottom, let's use it
|
||||
$('#plays-table-detail').height(80 + (height * 0.10));
|
||||
$('#tasks-table-detail').height(120 + (height * 0.20));
|
||||
$('#hosts-table-detail').height(150 + (height * 0.70));
|
||||
$('#hosts-table-detail').height(150 + (height * 0.10));
|
||||
}
|
||||
scope.$emit('RefreshCompleted');
|
||||
};
|
||||
@ -737,7 +737,7 @@ export default
|
||||
return true;
|
||||
};
|
||||
|
||||
scope.toggleLessEvents = function() {
|
||||
scope.toggleLessEvents = function(state) {
|
||||
if (!scope.lessEvents) {
|
||||
$('#events-summary').slideUp(200);
|
||||
scope.lessEvents = true;
|
||||
|
||||
@ -171,6 +171,7 @@
|
||||
|
||||
<div id="job-detail-details">
|
||||
<div id="play-section">
|
||||
<div class="JobDetail-instructions"><span class="badge">1</span> Please select from a play below to view its associated tasks.</div>
|
||||
<div class="JobDetail-searchHeaderRow">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
@ -228,7 +229,8 @@
|
||||
</div>
|
||||
<!-- end of plays section of details-->
|
||||
|
||||
<div id="task-section" class="section" >
|
||||
<div id="task-section" class="section JobDetail-tasks" >
|
||||
<div class="JobDetail-instructions"><span class="badge">2</span> Please select a task below to view its associated hosts</div>
|
||||
<div class="JobDetail-searchHeaderRow">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
@ -312,6 +314,7 @@
|
||||
<!--end of tasks section of details-->
|
||||
|
||||
<div id="task-hosts-section" class="section">
|
||||
<div class="JobDetail-instructions"><span class="badge">3</span> Please select a host below to view associated task details.</div>
|
||||
<div class="JobDetail-searchHeaderRow">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
@ -368,19 +371,12 @@
|
||||
</div>
|
||||
<!--end of details-->
|
||||
|
||||
|
||||
</div>
|
||||
<!--end of job-detail-container (left side)-->
|
||||
|
||||
<!--beginning of stdout-->
|
||||
<div class="JobDetail-rightSide">
|
||||
|
||||
<!--beginning of events summary-->
|
||||
<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" ui-sref="jobDetail.host-summary" ng-click="toggleLessEvents()">
|
||||
EVENT SUMMARY<i class="JobDetail-expandArrow fa fa-caret-left"></i>
|
||||
EVENT SUMMARY<i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
</a>
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessEvents" ui-sref="jobDetail" ng-click="toggleLessEvents()">
|
||||
EVENT SUMMARY<i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
@ -396,6 +392,11 @@
|
||||
<!-- end of events summary-->
|
||||
|
||||
|
||||
</div>
|
||||
<!--end of job-detail-container (left side)-->
|
||||
|
||||
<!--beginning of stdout-->
|
||||
<div class="JobDetail-rightSide">
|
||||
<div class="JobDetail-stdoutPanel Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText">STANDARD OUT</div>
|
||||
@ -403,7 +404,7 @@
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="Toggle Output" data-placement="top" ng-class="{'StandardOut-actionButton--active': stdoutFullScreen}" ng-click="toggleStdoutFullscreen()">
|
||||
<i class="fa fa-arrows-alt"></i>
|
||||
</button>
|
||||
<a href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}">
|
||||
<a ng-show="job_status.status === ('failed' || 'successful')" href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="Download Output" data-placement="top">
|
||||
<i class="fa fa-download"></i>
|
||||
</button>
|
||||
|
||||
@ -35,10 +35,6 @@ angular.module('ApiLoader', ['Utilities'])
|
||||
.success(function (data) {
|
||||
data.base = base;
|
||||
$rootScope.defaultUrls = data;
|
||||
// tiny hack to side-step api/v1/job_events not being a visible endpoint @ GET api/v1/
|
||||
if (!$rootScope.defaultUrls.job_events){
|
||||
$rootScope.defaultUrls.job_events = '/api/v1/job_events/';
|
||||
}
|
||||
Store('api', data);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
|
||||
@ -22,14 +22,14 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce
|
||||
// Open up a socket for events depending on the type of job
|
||||
function openSockets() {
|
||||
if ($state.current.name === 'jobDetail') {
|
||||
$log.debug("socket watching on job_events-" + job_id);
|
||||
$rootScope.event_socket.on("job_events-" + job_id, function() {
|
||||
$log.debug("socket fired on job_events-" + job_id);
|
||||
if (api_complete) {
|
||||
event_queue++;
|
||||
}
|
||||
});
|
||||
}
|
||||
$log.debug("socket watching on job_events-" + job_id);
|
||||
$rootScope.event_socket.on("job_events-" + job_id, function() {
|
||||
$log.debug("socket fired on job_events-" + job_id);
|
||||
if (api_complete) {
|
||||
event_queue++;
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($state.current.name === 'adHocJobStdout') {
|
||||
$log.debug("socket watching on ad_hoc_command_events-" + job_id);
|
||||
$rootScope.adhoc_event_socket.on("ad_hoc_command_events-" + job_id, function() {
|
||||
@ -131,8 +131,6 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce
|
||||
start: (data.range.start < 0) ? 0 : data.range.start,
|
||||
end: data.range.end
|
||||
});
|
||||
//console.log('loaded start: ' + data.range.start + ' end: ' + data.range.end);
|
||||
//console.log(data.content);
|
||||
if ($scope.should_apply_live_events) {
|
||||
// if user has not disabled live event view by scrolling upward, then scroll down to the new content
|
||||
current_range = data.range;
|
||||
|
||||
@ -3,6 +3,11 @@
|
||||
|
||||
/** @define StandardOut */
|
||||
|
||||
.StandardOut-preContent{
|
||||
font-size: 12px;
|
||||
padding: 0 20px 0 20px;
|
||||
font-family: Menlo,Monaco,Consolas,"Courier New",monospace;
|
||||
}
|
||||
.StandardOut-container {
|
||||
.OnePlusTwo-container;
|
||||
}
|
||||
@ -24,8 +29,8 @@
|
||||
min-height: 200px;
|
||||
background-color: @default-secondary-bg;
|
||||
border-radius: 5px;
|
||||
height: ~"calc(100% - 74px)";
|
||||
overflow: scroll;
|
||||
max-height: 1600px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.StandardOut-details {
|
||||
@ -81,7 +86,7 @@
|
||||
font-size: 16px;
|
||||
height: 30px;
|
||||
min-width: 30px;
|
||||
color: @default-data-txt;
|
||||
color: @list-action-icon;
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user