From 30c472c4991e237e1edaf97f4aa0f21ae6fd0981 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 15 Nov 2017 15:44:20 -0500 Subject: [PATCH] Add basic event output in sandbox --- awx/ui/client/features/jobs/_index.less | 22 ++-- .../features/output/index.controller.js | 114 ++++++++++++++---- awx/ui/client/features/output/index.js | 11 +- awx/ui/package.json | 2 + 4 files changed, 111 insertions(+), 38 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 9144f3c93c..50597f1b1a 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -1,12 +1,18 @@ .at-Stdout { - tr { - & > td:first-child { - padding-right: 5px; - border-right: 1px solid gray; - } + &-expand { + padding-right: 10px; + } - & > td:last-child { - padding-left: 5px; - } + &-lineNumber { + padding-right: 5px; + border-right: 1px solid gray; + } + + &-content { + padding-left: 5px; + } + + &-timestamp { + padding-left: 20px; } } diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 71c4bcf408..d94971ce7f 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -1,40 +1,102 @@ import Ansi from 'ansi-to-html'; import hasAnsi from 'has-ansi'; +let ansi; + +const EVENT_START_TASK = 'playbook_on_task_start'; +const EVENT_START_PLAY = 'playbook_on_play_start'; +const EVENT_STATS_PLAY = 'playbook_on_stats'; + +const EVENT_GROUPS = [ + EVENT_START_TASK, + EVENT_START_PLAY +]; + +const TIME_EVENTS = [ + EVENT_START_TASK, + EVENT_START_PLAY, + EVENT_STATS_PLAY +]; + function JobsIndexController (job, $sce) { const vm = this || {}; - const results = job.get('related.job_events.results'); - const ansi = new Ansi({}); + const events = job.get('related.job_events.results'); - /* - * const colors = []; - * - * for (let i = 0; i < 255; i++) { - * colors.push('#ababab'); - * } - * - */ + ansi = new Ansi(); - let html = ''; - results.forEach((line, i) => { - if (!line.stdout) { - return; - } - - let output; - - if (hasAnsi(line.stdout)) { - output = ansi.toHtml(line.stdout); - } else { - output = line.stdout; // .replace(/(\n|\r)/g, ''); - } - - html += `${i}${output}`; - }); + const html = parseEvents(events); vm.html = $sce.trustAsHtml(html); } +function parseEvents (events) { + events.sort((a, b) => a.start_line > b.start_line); + + return events.reduce((html, event) => `${html}${parseLine(event)}`, ''); +} + +function parseLine (event) { + if (!event || !event.stdout) { + return ''; + } + + const { stdout } = event; + const lines = stdout.split('\r\n'); + + let ln = event.start_line; + + return lines.reduce((html, line, i) => { + ln++; + + const time = getTime(event, i); + const group = getGroup(event, i); + + return `${html}${createRow(ln, line, time, group)}`; + }, ''); +} + +function createRow (ln, content, time, group) { + content = hasAnsi(content) ? ansi.toHtml(content) : content; + + let expand = ''; + if (group.parent) { + expand = ''; + } + + return ` + + ${expand} + ${ln} + ${content} + ${time} + `; +} + +function getGroup (event, i) { + const group = {}; + + if (EVENT_GROUPS.includes(event.event) && i === 1) { + group.parent = true; + group.classList = `parent parent-${event.event_level}`; + } else { + group.classList = ''; + } + + group.level = event.event_level; + + return group; +} + +function getTime (event, i) { + if (!TIME_EVENTS.includes(event.event) || i !== 1) { + return ''; + } + + const date = new Date(event.created); + + return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; +} + JobsIndexController.$inject = ['job', '$sce']; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index c5899b7964..558a1e4714 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -8,7 +8,7 @@ const MODULE_NAME = 'at.features.output'; function JobsRun ($stateExtender, strings) { $stateExtender.addState({ name: 'jobz', - route: '/jobz', + route: '/jobz/:id', ncyBreadcrumb: { label: strings.get('state.TITLE') }, @@ -24,9 +24,12 @@ function JobsRun ($stateExtender, strings) { } }, resolve: { - job: ['JobsModel', Jobs => new Jobs('get', 1002) - .then(job => job.extend('job_events')) - ] + job: ['JobsModel', '$stateParams', (Jobs, $stateParams) => { + const { id } = $stateParams; + + return new Jobs('get', id) + .then(job => job.extend('job_events')); + }] } }); } diff --git a/awx/ui/package.json b/awx/ui/package.json index 2d9f32f488..5f8ff0ad5c 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -107,12 +107,14 @@ "angular-sanitize": "~1.6.6", "angular-scheduler": "git+https://git@github.com/ansible/angular-scheduler#v0.3.2", "angular-tz-extensions": "git+https://git@github.com/ansible/angular-tz-extensions#v0.5.2", + "ansi-to-html": "^0.6.3", "babel-polyfill": "^6.26.0", "bootstrap": "^3.3.7", "bootstrap-datepicker": "^1.7.1", "codemirror": "^5.17.0", "components-font-awesome": "^4.6.1", "d3": "~3.3.13", + "has-ansi": "^3.0.0", "javascript-detect-element-resize": "^0.5.3", "jquery": "~2.2.4", "jquery-ui": "^1.12.1",