diff --git a/awx/ui/client/src/job-results/event-queue.service.js b/awx/ui/client/src/job-results/event-queue.service.js index 3737de2258..02c99ff9a5 100644 --- a/awx/ui/client/src/job-results/event-queue.service.js +++ b/awx/ui/client/src/job-results/event-queue.service.js @@ -4,72 +4,14 @@ * All Rights Reserved *************************************************/ -export default ['jobResultsService', 'parseStdoutService', '$q', function(jobResultsService, parseStdoutService, $q){ +export default ['jobResultsService', 'parseStdoutService', function(jobResultsService, parseStdoutService){ var val = {}; val = { populateDefers: {}, queue: {}, - // Get the count of the last event - getPreviousCount: function(counter, type) { - var countAttr; - - if (type === 'play') { - countAttr = 'playCount'; - } else if (type === 'task') { - countAttr = 'taskCount'; - } else { - countAttr = 'count'; - } - - var previousCount = $q.defer(); - - // iteratively find the last count - var findCount = function(counter) { - if (counter === 0) { - // if counter is 0, no count has been initialized - // initialize one! - - if (countAttr === 'count') { - previousCount.resolve({ - ok: 0, - skipped: 0, - unreachable: 0, - failures: 0, - changed: 0 - }); - } else { - previousCount.resolve(0); - } - - } else if (val.queue[counter] && val.queue[counter][countAttr] !== undefined) { - // this event has a count, resolve! - previousCount.resolve(_.clone(val.queue[counter][countAttr])); - } else { - // this event doesn't have a count, decrement to the - // previous event and check it - findCount(counter - 1); - } - }; - - if (val.queue[counter - 1]) { - // if the previous event has been resolved, start the iterative - // get previous count process - findCount(counter - 1); - } else if (val.populateDefers[counter - 1]){ - // if the previous event has not been resolved, wait for it to - // be and then start the iterative get previous count process - val.populateDefers[counter - 1].promise.then(function() { - findCount(counter - 1); - }); - } - - return previousCount.promise; - }, // munge the raw event from the backend into the event_queue's format munge: function(event) { - var mungedEventDefer = $q.defer(); - // basic data needed in the munged event var mungedEvent = { counter: event.counter, @@ -84,64 +26,15 @@ export default ['jobResultsService', 'parseStdoutService', '$q', function(jobRes // updates to it if (event.stdout) { mungedEvent.stdout = parseStdoutService.parseStdout(event); + mungedEvent.start_line = event.start_line + 1; mungedEvent.changes.push('stdout'); } // for different types of events, you need different types of data if (event.event_name === 'playbook_on_start') { - mungedEvent.count = { - ok: 0, - skipped: 0, - unreachable: 0, - failures: 0, - changed: 0 - }; mungedEvent.startTime = event.modified; - mungedEvent.changes.push('count'); mungedEvent.changes.push('startTime'); - } else if (event.event_name === 'playbook_on_play_start') { - val.getPreviousCount(mungedEvent.counter, "play") - .then(count => { - mungedEvent.playCount = count + 1; - mungedEvent.changes.push('playCount'); - }); - } else if (event.event_name === 'playbook_on_task_start') { - val.getPreviousCount(mungedEvent.counter, "task") - .then(count => { - mungedEvent.taskCount = count + 1; - mungedEvent.changes.push('taskCount'); - }); - } else if (event.event_name === 'runner_on_ok' || - event.event_name === 'runner_on_async_ok') { - val.getPreviousCount(mungedEvent.counter) - .then(count => { - mungedEvent.count = count; - mungedEvent.count.ok++; - mungedEvent.changes.push('count'); - }); - } else if (event.event_name === 'runner_on_skipped') { - val.getPreviousCount(mungedEvent.counter) - .then(count => { - mungedEvent.count = count; - mungedEvent.count.skipped++; - mungedEvent.changes.push('count'); - }); - } else if (event.event_name === 'runner_on_unreachable') { - val.getPreviousCount(mungedEvent.counter) - .then(count => { - mungedEvent.count = count; - mungedEvent.count.unreachable++; - mungedEvent.changes.push('count'); - }); - } else if (event.event_name === 'runner_on_error' || - event.event_name === 'runner_on_async_failed') { - val.getPreviousCount(mungedEvent.counter) - .then(count => { - mungedEvent.count = count; - mungedEvent.count.failed++; - mungedEvent.changes.push('count'); - }); - } else if (event.event_name === 'playbook_on_stats') { + } if (event.event_name === 'playbook_on_stats') { // get the data for populating the host status bar mungedEvent.count = jobResultsService .getCountsFromStatsEvent(event.event_data); @@ -150,10 +43,7 @@ export default ['jobResultsService', 'parseStdoutService', '$q', function(jobRes mungedEvent.changes.push('countFinished'); mungedEvent.changes.push('finishedTime'); } - - mungedEventDefer.resolve(mungedEvent); - - return mungedEventDefer.promise; + return mungedEvent; }, // reinitializes the event queue value for the job results page initialize: function() { @@ -162,88 +52,18 @@ export default ['jobResultsService', 'parseStdoutService', '$q', function(jobRes }, // populates the event queue populate: function(event) { - // if a defer hasn't been set up for the event, - // set one up now - if (!val.populateDefers[event.counter]) { - val.populateDefers[event.counter] = $q.defer(); - } + val.queue[event.counter] = val.munge(event); - // make sure not to send duplicate events over to the - // controller - if (val.queue[event.counter] && - val.queue[event.counter].processed) { - val.populateDefers.reject("duplicate event: " + - event); - } - - if (!val.queue[event.counter]) { - var resolvePopulation = function(event) { - // to resolve, put the event on the queue and - // then resolve the deferred value - val.queue[event.counter] = event; - val.populateDefers[event.counter].resolve(event); - }; - - if (event.counter === 1) { - // for the first event, go ahead and munge and - // resolve - val.munge(event).then(event => { - resolvePopulation(event); - }); - } else { - // for all other events, you have to do some things - // to keep the event processing in the UI synchronous - - if (!val.populateDefers[event.counter - 1]) { - // first, if the previous event doesn't have - // a defer set up (this happens when websocket - // events are coming in and you need to make - // rest calls to catch up), go ahead and set a - // defer for the previous event - val.populateDefers[event.counter - 1] = $q.defer(); - } - - // you can start the munging process... - val.munge(event).then(event => { - // ...but wait until the previous event has - // been resolved before resolving this one and - // doing stuff in the ui (that's why we - // needed that previous conditional). - val.populateDefers[event.counter - 1].promise - .then(() => { - resolvePopulation(event); - }); - }); - } + if (!val.queue[event.counter].processed) { + return val.munge(event); } else { - // don't repopulate the event if it's already been added - // and munged either by rest or by websocket event - val.populateDefers[event.counter] - .resolve(val.queue[event.counter]); + return {}; } - - return val.populateDefers[event.counter].promise; }, // the event has been processed in the view and should be marked as // completed in the queue markProcessed: function(event) { - var process = function(event) { - // the event has now done it's work in the UI, record - // that! - val.queue[event.counter].processed = true; - }; - - if (!val.queue[event.counter]) { - // sometimes, the process is called in the controller and - // the event queue hasn't caught up and actually added - // the event to the queue yet. Wait until that happens - val.populateDefers[event.counter].promise - .finally(function() { - process(event); - }); - } else { - process(event); - } + val.queue[event.counter].processed = true; } }; diff --git a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.partial.html b/awx/ui/client/src/job-results/host-status-bar/host-status-bar.partial.html index 24a9170bcd..2d860cc2e6 100644 --- a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.partial.html +++ b/awx/ui/client/src/job-results/host-status-bar/host-status-bar.partial.html @@ -20,7 +20,7 @@ aw-tool-tip="{{skippedCountTip}}" data-tip-watch="skippedCountTip">
diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less index cc34d11c2f..408e4cbb32 100644 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less +++ b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less @@ -3,13 +3,16 @@ @breakpoint-md: 1200px; .JobResultsStdOut { - height: ~"calc(100% - 70px)"; + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + align-items: stretch; } .JobResultsStdOut-toolbar { + flex: initial; display: flex; - height: 38px; - margin-top: 15px; border: 1px solid @default-list-header-bg; border-bottom: 0px; border-radius: 5px; @@ -28,7 +31,7 @@ display: flex; justify-content: space-between; width: 70px; - padding-bottom: 0px; + padding-bottom: 10px; padding-left: 8px; padding-right: 8px; padding-top: 10px; @@ -106,21 +109,18 @@ } .JobResultsStdOut-stdoutContainer { - height: ~"calc(100% - 48px)"; - background-color: @default-no-items-bord; + flex: 1; + position: relative; + background-color: #F6F6F6; overflow-y: scroll; overflow-x: hidden; } .JobResultsStdOut-numberColumnPreload { background-color: @default-list-header-bg; + position: absolute; + height: 100%; width: 70px; - position: fixed; - top: 148px; - bottom: 20px; - margin-top: 65px; - margin-bottom: 65px; - } .JobResultsStdOut-aLineOfStdOut { @@ -171,6 +171,10 @@ width:100%; } +.JobResultsStdOut-stdoutColumn { + cursor: pointer; +} + .JobResultsStdOut-aLineOfStdOut:hover, .JobResultsStdOut-aLineOfStdOut:hover .JobResultsStdOut-lineNumberColumn { background-color: @default-bg; @@ -197,6 +201,7 @@ .JobResultsStdOut-followAnchor { height: 20px; width: 100%; + border-left: 70px solid @default-list-header-bg; } .JobResultsStdOut-toTop { diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js index 15d3e5e90c..0d1674b267 100644 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js +++ b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js @@ -12,7 +12,7 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', templateUrl: templateUrl('job-results/job-results-stdout/job-results-stdout'), restrict: 'E', link: function(scope, element) { - + scope.stdoutContainerAvailable.resolve("container available"); // utility function used to find the top visible line and // parent header in the pane // diff --git a/awx/ui/client/src/job-results/job-results.block.less b/awx/ui/client/src/job-results/job-results.block.less index 9165ac4d4c..2f24e98cbc 100644 --- a/awx/ui/client/src/job-results/job-results.block.less +++ b/awx/ui/client/src/job-results/job-results.block.less @@ -149,3 +149,30 @@ border-radius: 5px; color: @default-interface-txt; } + +.JobResults-panelRight { + display: flex; + flex-direction: column; +} + +.StandardOut-panelHeader { + flex: initial; +} + +.StandardOut-panelHeader--jobIsRunning { + margin-bottom: 20px; +} + +host-status-bar { + flex: initial; + margin-bottom: 20px; +} + +smart-search { + flex: initial; +} + +job-results-standard-out { + flex: 1; + display: flex +} 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 dfef8fca10..1c98dd3895 100644 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ b/awx/ui/client/src/job-results/job-results.controller.js @@ -1,4 +1,20 @@ -export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', 'eventQueue', '$compile', '$log', function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, $log) { +export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', 'eventQueue', '$compile', '$log', 'Dataset', '$q', 'Rest', '$state', 'QuerySet', function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, $log, Dataset, $q, Rest, $state, QuerySet) { + // used for tag search + $scope.job_event_dataset = Dataset.data; + + // used for tag search + $scope.list = { + basePath: jobData.related.job_events, + defaultSearchParams: function(term){ + return { + or__stdout__icontains: term, + }; + }, + }; + + // used for tag search + $scope.job_events = $scope.job_event_dataset.results; + var getTowerLinks = function() { var getTowerLink = function(key) { if ($scope.job.related[key]) { @@ -87,6 +103,7 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count' $scope.relaunchJob = function() { jobResultsService.relaunchJob($scope); + $state.reload(); }; $scope.lessLabels = false; @@ -127,91 +144,177 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count' // Flow is event queue munging in the service -> $scope setting in here var processEvent = function(event) { // put the event in the queue - eventQueue.populate(event).then(mungedEvent => { - // make changes to ui based on the event returned from the queue - if (mungedEvent.changes) { - mungedEvent.changes.forEach(change => { - // we've got a change we need to make to the UI! - // update the necessary scope and make the change - if (change === 'startTime' && !$scope.job.start) { - $scope.job.start = mungedEvent.startTime; - } + var mungedEvent = eventQueue.populate(event); - if (change === 'count' && !$scope.countFinished) { - // for all events that affect the host count, - // update the status bar as well as the host - // count badge - $scope.count = mungedEvent.count; - $scope.hostCount = getTotalHostCount(mungedEvent - .count); - } + // make changes to ui based on the event returned from the queue + if (mungedEvent.changes) { + mungedEvent.changes.forEach(change => { + // we've got a change we need to make to the UI! + // update the necessary scope and make the change + if (change === 'startTime' && !$scope.job.start) { + $scope.job.start = mungedEvent.startTime; + } - if (change === 'playCount') { - $scope.playCount = mungedEvent.playCount; - } + if (change === 'count' && !$scope.countFinished) { + // for all events that affect the host count, + // update the status bar as well as the host + // count badge + $scope.count = mungedEvent.count; + $scope.hostCount = getTotalHostCount(mungedEvent + .count); + } - if (change === 'taskCount') { - $scope.taskCount = mungedEvent.taskCount; - } + if (change === 'finishedTime' && !$scope.job.finished) { + $scope.job.finished = mungedEvent.finishedTime; + $scope.jobFinished = true; + $scope.followTooltip = "Jump to last line of standard out."; + } - if (change === 'finishedTime' && !$scope.job.finished) { - $scope.job.finished = mungedEvent.finishedTime; - $scope.jobFinished = true; - $scope.followTooltip = "Jump to last line of standard out."; - } + if (change === 'countFinished') { + // the playbook_on_stats event actually lets + // us know that we don't need to iteratively + // look at event to update the host counts + // any more. + $scope.countFinished = true; + } - if (change === 'countFinished') { - // the playbook_on_stats event actually lets - // us know that we don't need to iteratively - // look at event to update the host counts - // any more. - $scope.countFinished = true; - } + if(change === 'stdout'){ + // put stdout elements in stdout container - if(change === 'stdout'){ - // put stdout elements in stdout container + // this scopes the event to that particular + // block of stdout. + // If you need to see the event a particular + // stdout block is from, you can: + // angular.element($0).scope().event + $scope.events[mungedEvent.counter] = $scope.$new(); + $scope.events[mungedEvent.counter] + .event = mungedEvent; - // this scopes the event to that particular - // block of stdout. - // If you need to see the event a particular - // stdout block is from, you can: - // angular.element($0).scope().event - $scope.events[mungedEvent.counter] = $scope.$new(); - $scope.events[mungedEvent.counter] - .event = mungedEvent; + if (mungedEvent.stdout.indexOf("not_skeleton") > -1) { + // put non-duplicate lines into the standard + // out pane where they should go (within the + // right header section, before the next line + // as indicated by start_line) + window.$ = $; + var putIn; + var classList = $("div", + "