diff --git a/awx/ui/client/legacy-styles/ansible-ui.less b/awx/ui/client/legacy-styles/ansible-ui.less
index ef19b69111..a511ffa33f 100644
--- a/awx/ui/client/legacy-styles/ansible-ui.less
+++ b/awx/ui/client/legacy-styles/ansible-ui.less
@@ -2234,3 +2234,8 @@ a:hover {
padding-left: 2px;
width: 14px;
}
+
+button[disabled],
+html input[disabled] {
+ cursor: not-allowed;
+}
diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html
index 63e41b8fa8..b0b3d85f10 100644
--- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html
+++ b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html
@@ -35,11 +35,14 @@
diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js
index 92d311a204..0215c816fb 100644
--- a/awx/ui/client/src/job-results/job-results.controller.js
+++ b/awx/ui/client/src/job-results/job-results.controller.js
@@ -1,5 +1,5 @@
-export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', 'eventQueue', '$compile', '$log', 'Dataset', '$q', 'Rest', '$state', 'QuerySet', '$rootScope', 'moment', 'i18n',
-function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, $log, Dataset, $q, Rest, $state, QuerySet, $rootScope, moment, i18n) {
+export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', 'eventQueue', '$compile', '$log', 'Dataset', '$q', 'Rest', '$state', 'QuerySet', '$rootScope', 'moment', '$stateParams', 'i18n',
+function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, $log, Dataset, $q, Rest, $state, QuerySet, $rootScope, moment, $stateParams, i18n) {
var toDestroy = [];
var cancelRequests = false;
@@ -9,6 +9,25 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
// this allows you to manage the timing of rest-call based events as
// filters are updated. see processPage for more info
var currentContext = 1;
+ $scope.firstCounterFromSocket = -1;
+
+ // if the user enters the page mid-run, reset the search to include a param
+ // to only grab events less than the first counter from the websocket events
+ toDestroy.push($scope.$watch('firstCounterFromSocket', function(counter) {
+ if (counter > -1) {
+ // make it so that the search include a counter less than the
+ // first counter from the socket
+ let params = _.cloneDeep($stateParams.job_event_search);
+ params.counter__lte = "" + counter;
+
+ Dataset = QuerySet.search(jobData.related.job_events,
+ params);
+
+ Dataset.then(function(actualDataset) {
+ $scope.job_event_dataset = actualDataset.data;
+ });
+ }
+ }));
// used for tag search
$scope.job_event_dataset = Dataset.data;
@@ -424,57 +443,87 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
// grab non-header recap lines
toDestroy.push($scope.$watch('job_event_dataset', function(val) {
- eventQueue.initialize();
+ if (val) {
+ eventQueue.initialize();
- Object.keys($scope.events)
- .forEach(v => {
- // dont destroy scope events for skeleton lines
- let name = $scope.events[v].event.name;
+ Object.keys($scope.events)
+ .forEach(v => {
+ // dont destroy scope events for skeleton lines
+ let name = $scope.events[v].event.name;
- if (!(name === "playbook_on_play_start" ||
- name === "playbook_on_task_start" ||
- name === "playbook_on_stats")) {
- $scope.events[v].$destroy();
- $scope.events[v] = null;
- delete $scope.events[v];
+ if (!(name === "playbook_on_play_start" ||
+ name === "playbook_on_task_start" ||
+ name === "playbook_on_stats")) {
+ $scope.events[v].$destroy();
+ $scope.events[v] = null;
+ delete $scope.events[v];
+ }
+ });
+
+ // pause websocket events from coming in to the pane
+ $scope.gotPreviouslyRanEvents = $q.defer();
+ currentContext += 1;
+
+ let context = currentContext;
+
+ $( ".JobResultsStdOut-aLineOfStdOut.not_skeleton" ).remove();
+ $scope.hasSkeleton.promise.then(() => {
+ if (val.count > parseInt(val.maxEvents)) {
+ $(".header_task").hide();
+ $(".header_play").hide();
+ $scope.standardOutTooltip = '
';
+
+ if ($scope.job_status === "successful" ||
+ $scope.job_status === "failed" ||
+ $scope.job_status === "error" ||
+ $scope.job_status === "canceled") {
+ $scope.tooManyEvents = true;
+ $scope.tooManyPastEvents = false;
+ } else {
+ $scope.tooManyPastEvents = true;
+ $scope.tooManyEvents = false;
+ $scope.gotPreviouslyRanEvents.resolve("");
+ }
+ } else {
+ $(".header_task").show();
+ $(".header_play").show();
+ $scope.tooManyEvents = false;
+ $scope.tooManyPastEvents = false;
+ processPage(val, context);
}
});
-
- // pause websocket events from coming in to the pane
- $scope.gotPreviouslyRanEvents = $q.defer();
- currentContext += 1;
-
- let context = currentContext;
-
- $( ".JobResultsStdOut-aLineOfStdOut.not_skeleton" ).remove();
- $scope.hasSkeleton.promise.then(() => {
- if (val.count > parseInt(val.maxEvents)) {
- $(".header_task").hide();
- $(".header_play").hide();
- $scope.tooManyEvents = true;
- $scope.standardOutTooltip = '
';
- } else {
- $(".header_task").show();
- $(".header_play").show();
- $scope.tooManyEvents = false;
- processPage(val, context);
- }
- });
+ }
}));
// Processing of job_events messages from the websocket
toDestroy.push($scope.$on(`ws-job_events-${$scope.job.id}`, function(e, data) {
+
+ // use the lowest counter coming over the socket to retrigger pull data
+ // to only be for stuff lower than that id
+ //
+ // only do this for entering the jobs page mid-run (thus the
+ // data.counter is 1 conditional
+ if (data.counter === 1) {
+ $scope.firstCounterFromSocket = -2;
+ }
+
+ if ($scope.firstCounterFromSocket !== -2 &&
+ $scope.firstCounterFromSocket === -1 ||
+ data.counter < $scope.firstCounterFromSocket) {
+ $scope.firstCounterFromSocket = data.counter;
+ }
+
$q.all([$scope.gotPreviouslyRanEvents.promise,
$scope.hasSkeleton.promise]).then(() => {
// put the line in the
diff --git a/awx/ui/client/src/job-results/job-results.partial.html b/awx/ui/client/src/job-results/job-results.partial.html
index 86d7430d21..24b2dce775 100644
--- a/awx/ui/client/src/job-results/job-results.partial.html
+++ b/awx/ui/client/src/job-results/job-results.partial.html
@@ -506,7 +506,9 @@
list="list"
collection="job_events"
dataset="job_event_dataset"
- search-tags="searchTags">
+ search-tags="searchTags"
+ disable-search="job_status == 'running' ||
+ job_status=='pending'">
diff --git a/awx/ui/client/src/job-results/job-results.route.js b/awx/ui/client/src/job-results/job-results.route.js
index ec47cc7757..063804320e 100644
--- a/awx/ui/client/src/job-results/job-results.route.js
+++ b/awx/ui/client/src/job-results/job-results.route.js
@@ -6,6 +6,12 @@
import {templateUrl} from '../shared/template-url/template-url.factory';
+const defaultParams = {
+ page_size: "200",
+ order_by: 'start_line',
+ not__event__in: 'playbook_on_start,playbook_on_play_start,playbook_on_task_start,playbook_on_stats'
+}
+
export default {
name: 'jobDetail',
url: '/jobs/{id: int}',
@@ -24,11 +30,7 @@ export default {
},
params: {
job_event_search: {
- value: {
- page_size: 100,
- order_by: 'id',
- not__event__in: 'playbook_on_start,playbook_on_play_start,playbook_on_task_start,playbook_on_stats'
- },
+ value: defaultParams,
dynamic: true,
squash: ''
}
@@ -56,7 +58,7 @@ export default {
// flashing as rest data comes in. If the job is finished and
// there's a playbook_on_stats event, go ahead and resolve the count
// so you don't get that flashing!
- count: ['jobData', 'jobResultsService', 'Rest', '$q', function(jobData, jobResultsService, Rest, $q) {
+ count: ['jobData', 'jobResultsService', 'Rest', '$q', '$stateParams', '$state', function(jobData, jobResultsService, Rest, $q, $stateParams, $state) {
var defer = $q.defer();
if (jobData.finished) {
// if the job is finished, grab the playbook_on_stats
@@ -92,6 +94,15 @@ export default {
}, countFinished: false});
});
} else {
+ // make sure to not include any extra
+ // search params for a running job (because we can't filter
+ // incoming job events)
+ if (!_.isEqual($stateParams.job_event_search, defaultParams)) {
+ let params = _.cloneDeep($stateParams);
+ params.job_event_search = defaultParams;
+ $state.go('.', params, { reload: true });
+ }
+
// job isn't finished so just send an empty count and read
// from events
defer.resolve({val: {
diff --git a/awx/ui/client/src/shared/smart-search/smart-search.directive.js b/awx/ui/client/src/shared/smart-search/smart-search.directive.js
index 4cf67ed51f..73d3eafa89 100644
--- a/awx/ui/client/src/shared/smart-search/smart-search.directive.js
+++ b/awx/ui/client/src/shared/smart-search/smart-search.directive.js
@@ -15,6 +15,7 @@ export default ['templateUrl',
dataset: '=',
collection: '=',
searchTags: '=',
+ disableSearch: '='
},
controller: 'SmartSearchController',
templateUrl: templateUrl('shared/smart-search/smart-search')
diff --git a/awx/ui/client/src/shared/smart-search/smart-search.partial.html b/awx/ui/client/src/shared/smart-search/smart-search.partial.html
index 57a3bfb52c..a7a8f9caa2 100644
--- a/awx/ui/client/src/shared/smart-search/smart-search.partial.html
+++ b/awx/ui/client/src/shared/smart-search/smart-search.partial.html
@@ -4,7 +4,8 @@