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 3e052def56..2af29d5b05 100644
--- a/awx/ui/client/src/job-results/job-results.controller.js
+++ b/awx/ui/client/src/job-results/job-results.controller.js
@@ -297,6 +297,26 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
}
if(change === 'stdout'){
+ var appendToBottom = function(mungedEvent){
+ // if we get here then the event type was either a
+ // header line, recap line, or one of the additional
+ // event types, so we append it to the bottom.
+ // These are the event types for captured
+ // stdout not directly related to playbook or runner
+ // events:
+ // (0, 'debug', _('Debug'), False),
+ // (0, 'verbose', _('Verbose'), False),
+ // (0, 'deprecated', _('Deprecated'), False),
+ // (0, 'warning', _('Warning'), False),
+ // (0, 'system_warning', _('System Warning'), False),
+ // (0, 'error', _('Error'), True),
+ angular
+ .element(".JobResultsStdOut-stdoutContainer")
+ .append($compile(mungedEvent
+ .stdout)($scope.events[mungedEvent
+ .counter]));
+ };
+
if (!$scope.events[mungedEvent.counter]) {
// line hasn't been put in the pane yet
@@ -314,13 +334,56 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
.after($compile(mungedEvent
.stdout)($scope.events[mungedEvent
.counter]));
+ } else if (mungedEvent.stdout.indexOf("not_skeleton") > -1) {
+ var putIn;
+ var classList = $("div",
+ "
"+mungedEvent.stdout+"
")
+ .attr("class").split(" ");
+ if (classList
+ .filter(v => v.indexOf("task_") > -1)
+ .length) {
+ putIn = classList
+ .filter(v => v.indexOf("task_") > -1)[0];
+ } else if(classList
+ .filter(v => v.indexOf("play_") > -1)
+ .length) {
+ putIn = classList
+ .filter(v => v.indexOf("play_") > -1)[0];
+ }
+
+ var putAfter;
+ var isDup = false;
+ $(".header_" + putIn + ",." + putIn)
+ .each((i, v) => {
+ if (angular.element(v).scope()
+ .event.start_line < mungedEvent
+ .start_line) {
+ putAfter = v;
+ } else if (angular.element(v).scope()
+ .event.start_line === mungedEvent
+ .start_line) {
+ isDup = true;
+ return false;
+ } else if (angular.element(v).scope()
+ .event.start_line > mungedEvent
+ .start_line) {
+ return false;
+ } else {
+ appendToBottom(mungedEvent);
+ }
+ });
+
+ if (!isDup) {
+ $(putAfter).after($compile(mungedEvent
+ .stdout)($scope.events[mungedEvent
+ .counter]));
+ }
+
+
+ classList = null;
+ putIn = null;
} else {
- // if not, put it at the bottom
- angular
- .element(".JobResultsStdOut-stdoutContainer")
- .append($compile(mungedEvent
- .stdout)($scope.events[mungedEvent
- .counter]));
+ appendToBottom(mungedEvent);
}
// delete ref to the elem because it might leak scope
@@ -508,10 +571,24 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
}
}));
+ var buffer = [];
+ var processBuffer = function() {
+ buffer.forEach((event, i) => {
+ processEvent(event);
+ buffer.splice(i, 1);
+ });
+ };
+
+ var bufferInterval;
// Processing of job_events messages from the websocket
toDestroy.push($scope.$on(`ws-job_events-${$scope.job.id}`, function(e, data) {
+ if (!bufferInterval) {
+ bufferInterval = setInterval(function(){
+ processBuffer();
+ }, 500);
+ }
// use the lowest counter coming over the socket to retrigger pull data
// to only be for stuff lower than that id
@@ -538,6 +615,7 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
} else if (data.event_name === "playbook_on_task_start") {
$scope.taskCount++;
}
+ buffer.push(data);
processEvent(data);
});
}));
@@ -553,6 +631,9 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
data.status === "error" ||
data.status === "canceled") {
clearInterval(elapsedInterval);
+ if (bufferInterval) {
+ clearInterval(bufferInterval);
+ }
// When the fob is finished retrieve the job data to
// correct anything that was out of sync from the job run
jobResultsService.getJobData($scope.job.id).then(function(data){
@@ -584,6 +665,9 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
});
$scope.events = {};
clearInterval(elapsedInterval);
+ if (bufferInterval) {
+ clearInterval(bufferInterval);
+ }
toDestroy.forEach(closureFunc => closureFunc());
});
}];
diff --git a/awx/ui/client/src/job-results/job-results.service.js b/awx/ui/client/src/job-results/job-results.service.js
index 6fbeefde13..7c3d36aab9 100644
--- a/awx/ui/client/src/job-results/job-results.service.js
+++ b/awx/ui/client/src/job-results/job-results.service.js
@@ -18,19 +18,11 @@ function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybo
// and their status data
Object.keys(event_data).forEach(key => {
// failed passes boolean not integer
- if (key === "failed") {
- // array of hosts from failed type
- 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 {
+ if (key === "changed" ||
+ key === "dark" ||
+ key === "failures" ||
+ key === "ok" ||
+ key === "skipped") {
// array of hosts from each type ("changed", "dark", etc.)
hostsArr = Object.keys(event_data[key]);
hostsArr.forEach(host => {
@@ -61,7 +53,7 @@ function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybo
return o.dark > 0;
}),
failures : _.filter(hosts, function(o){
- return o.failed === true;
+ return o.failures > 0;
}),
changed : _.filter(hosts, function(o){
return o.changed > 0;
diff --git a/awx/ui/client/src/job-results/parse-stdout.service.js b/awx/ui/client/src/job-results/parse-stdout.service.js
index 8630c0fa07..8c7e91f755 100644
--- a/awx/ui/client/src/job-results/parse-stdout.service.js
+++ b/awx/ui/client/src/job-results/parse-stdout.service.js
@@ -202,6 +202,11 @@ export default ['$log', 'moment', function($log, moment){
let lineNums = _.range(event.start_line + 1,
event.end_line + 1);
+ // hack around no-carriage return issues
+ if (!lineNums.length) {
+ lineNums = [event.start_line + 1];
+ }
+
let lines = event.stdout
.replace("\t", " ")
.split("\r\n");
@@ -214,6 +219,11 @@ export default ['$log', 'moment', function($log, moment){
}
}
+ // hack around no-carriage return issues
+ if (lineNums.length === lines.length) {
+ return _.zip(lineNums, lines);
+ }
+
return _.zip(lineNums, lines).slice(0, -1);
},
// public function that provides the parsed stdout line, given a