diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index bd2c18c14d..2fb6239c7e 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -38,6 +38,16 @@ } } + &-menuIcon--md { + font-size: 14px; + padding: 10px; + cursor: pointer; + + &:hover { + color: @at-blue; + } + } + &-menuIcon--lg { font-size: 22px; line-height: 12px; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 0080af3271..7d2acdc8ea 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -416,6 +416,22 @@ function reloadState (params) { return $state.transitionTo($state.current, params, { inherit: false, location: 'replace' }); } +function clear () { + stopListening(); + render.clear(); + + followOnce = true; + lockFollow = false; + lockFrames = false; + + bufferInit(); + status.init(resource); + slide.init(render, resource.events, scroll); + status.subscribe(data => { vm.status = data.status; }); + + startListening(); +} + function OutputIndexController ( _$compile_, _$q_, @@ -432,7 +448,8 @@ function OutputIndexController ( strings, $stateParams, ) { - const { isPanelExpanded } = $stateParams; + const { isPanelExpanded, _debug } = $stateParams; + const isProcessingFinished = !_debug && _resource_.model.get('event_processing_finished'); $compile = _$compile_; $q = _$q_; @@ -444,7 +461,7 @@ function OutputIndexController ( render = _render_; status = _status_; stream = _stream_; - slide = resource.model.get('event_processing_finished') ? _page_ : _slide_; + slide = isProcessingFinished ? _page_ : _slide_; vm = this || {}; @@ -458,7 +475,7 @@ function OutputIndexController ( vm.togglePanelExpand = togglePanelExpand; // Stdout Navigation - vm.menu = { last: menuLast, first, down, up }; + vm.menu = { last: menuLast, first, down, up, clear }; vm.isMenuExpanded = true; vm.isFollowing = false; vm.toggleMenuExpand = toggleMenuExpand; @@ -466,6 +483,7 @@ function OutputIndexController ( vm.showHostDetails = showHostDetails; vm.toggleLineEnabled = resource.model.get('type') === 'job'; vm.followTooltip = vm.strings.get('tooltips.MENU_LAST'); + vm.debug = _debug; render.requestAnimationFrame(() => { bufferInit(); @@ -523,7 +541,7 @@ function OutputIndexController ( } }); - if (resource.model.get('event_processing_finished')) { + if (isProcessingFinished) { followOnce = false; lockFollow = true; lockFrames = true; @@ -537,6 +555,10 @@ function OutputIndexController ( startListening(); } + if (_debug) { + return render.clear(); + } + return last(); }); } diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index ce016c1f19..0109877d9b 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -82,7 +82,7 @@ function resolveResource ( order_by: OUTPUT_ORDER_BY, }; - if (job_event_search) { // eslint-disable-line camelcase + if (job_event_search) { const query = qs.encodeQuerysetObject(qs.decodeArr(job_event_search)); Object.assign(params, query); } @@ -173,7 +173,7 @@ function JobsRun ($stateRegistry, $filter, strings) { const sanitize = $filter('sanitize'); const state = { - url: '/:type/:id?job_event_search', + url: '/:type/:id?job_event_search?_debug', name: 'output', parent, ncyBreadcrumb, diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 738b7a0ae5..d6fae167f5 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -47,6 +47,9 @@ +
+ +
diff --git a/docs/job_events.md b/docs/job_events.md index 60f5ad7afd..2f4c35ae72 100644 --- a/docs/job_events.md +++ b/docs/job_events.md @@ -95,6 +95,9 @@ playbook_on_play_start-install runner_on_ok_hostA (install_tower) ``` +## Testing +A management command for event replay exists for replaying jobs at varying speeds and other parameters. Run `awx-manage replay_job_events --help` for additional usage information. To prepare the UI for event replay, load the page for a finished job and then append `_debug` as a parameter to the url. + ## Code References * More comprehensive list of Job Events and the hierarchy they form https://github.com/ansible/awx/blob/devel/awx/main/models/jobs.py#L870 * Exhaustive list of Job Events in Tower https://github.com/ansible/awx/blob/devel/awx/main/models/jobs.py#L900