From 2d642c58d7a8b111e329ce992f5aee55dbe72229 Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Thu, 26 Jun 2014 04:40:09 -0400 Subject: [PATCH] Job detail page Added dialog for viewing host results and a bunch of formatting. Linked host summary to job_host_summaries page (yes, that still exists). Host results now shows 'skipped' hosts with a blue icon rather than green. --- awx/ui/static/js/controllers/JobDetail.js | 13 +- awx/ui/static/js/helpers/JobDetail.js | 160 +++++++++++++++++++++- awx/ui/static/less/ansible-ui.less | 9 +- awx/ui/static/less/job-details.less | 54 ++++++++ awx/ui/static/partials/job_detail.html | 11 +- 5 files changed, 232 insertions(+), 15 deletions(-) diff --git a/awx/ui/static/js/controllers/JobDetail.js b/awx/ui/static/js/controllers/JobDetail.js index ca1e5e0cb3..c98706f5fe 100644 --- a/awx/ui/static/js/controllers/JobDetail.js +++ b/awx/ui/static/js/controllers/JobDetail.js @@ -9,7 +9,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, ClearScope, Breadcrumbs, LoadBreadCrumbs, GetBasePath, Wait, Rest, ProcessErrors, SelectPlay, SelectTask, Socket, GetElapsed, FilterAllByHostName, DrawGraph, LoadHostSummary, ReloadHostSummaryList, - JobIsFinished, SetTaskStyles, DigestEvent, UpdateDOM) { + JobIsFinished, SetTaskStyles, DigestEvent, UpdateDOM, ViewHostResults) { ClearScope(); @@ -182,7 +182,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, event = data.results[idx]; task.hostResults[event.id] = { id: event.id, - status: ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ), + status: (event.event === "runner_on_skipped") ? 'skipped' : (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful', host_id: event.host, task_id: event.parent, name: event.event_data.host, @@ -1044,13 +1044,16 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, }); }; - scope.viewEvent = function(event_id) { - $log.debug(event_id); + scope.viewHostResults = function(id) { + ViewHostResults({ + scope: scope, + id: id + }); }; } JobDetailController.$inject = [ '$rootScope', '$scope', '$compile', '$routeParams', '$log', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'GetBasePath', 'Wait', 'Rest', 'ProcessErrors', 'SelectPlay', 'SelectTask', 'Socket', 'GetElapsed', 'FilterAllByHostName', 'DrawGraph', - 'LoadHostSummary', 'ReloadHostSummaryList', 'JobIsFinished', 'SetTaskStyles', 'DigestEvent', 'UpdateDOM' + 'LoadHostSummary', 'ReloadHostSummaryList', 'JobIsFinished', 'SetTaskStyles', 'DigestEvent', 'UpdateDOM', 'ViewHostResults' ]; diff --git a/awx/ui/static/js/helpers/JobDetail.js b/awx/ui/static/js/helpers/JobDetail.js index 4dcb9d046a..9a9b429799 100644 --- a/awx/ui/static/js/helpers/JobDetail.js +++ b/awx/ui/static/js/helpers/JobDetail.js @@ -37,7 +37,7 @@ 'use strict'; -angular.module('JobDetailHelper', ['Utilities', 'RestServices']) +angular.module('JobDetailHelper', ['Utilities', 'RestServices', 'ModalDialog']) .factory('DigestEvent', ['$rootScope', '$log', 'UpdatePlayStatus', 'UpdateHostStatus', 'AddHostResult', 'GetElapsed', 'UpdateTaskStatus', 'DrawGraph', 'LoadHostSummary', 'JobIsFinished', 'AddNewTask', @@ -716,7 +716,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge data.results.forEach(function(event) { scope.hostResults.push({ id: event.id, - status: ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ), + status: (event.event === "runner_on_skipped") ? 'skipped' : (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful', host_id: event.host, task_id: event.parent, name: event.event_data.host, @@ -1089,4 +1089,160 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge msg: 'Call to ' + url + '. GET returned: ' + status }); }); }; +}]) + +.factory('ViewHostResults', ['$log', 'CreateDialog', 'Rest', 'ProcessErrors', 'Wait', function($log, CreateDialog, Rest, ProcessErrors, Wait) { + return function(params) { + var scope = params.scope, + my_scope = params.scope.$new(), + id = params.id, + url; + + function parseJSON(obj) { + var html="", keys; + if (typeof obj === "object") { + html += "\n"; + html += "\n"; + keys = Object.keys(obj).sort(); + keys.forEach(function(key) { + if (typeof obj[key] === "boolean" || typeof obj[key] === "number" || typeof obj[key] === "string") { + html += "\n"; + } + else if (obj[key] === null || obj[key] === undefined) { + // html += "\n"; + } + else if (typeof obj[key] === "object" && Array.isArray(obj[key])) { + html += "\n"; + } + else if (typeof obj[key] === "object") { + html += "\n"; + } + }); + html += "\n"; + html += "
" + key + ":"; + html += (key === "status") ? " " + obj[key] : obj[key]; + html += "
" + key + ":null
" + key + ":"; + obj[key].forEach(function(row) { + html += "

" + row + "

"; + }); + html += "
" + key + ":\n" + parseJSON(obj[key]) + "
\n"; + } + return html; + } + + /*function parseJSON(obj) { + var html="", key; + html += "\n"; + return html; + }*/ + + /*function parseJSON(obj) { + var html="", key; + for(key in obj) { + html += "
\n"; + if (typeof obj[key] === "boolean" || typeof obj[key] === "number" || typeof obj[key] === "string") { + html += "
" + key + ":
" + obj[key] + "
\n"; + } + if (obj[key] === null || obj[key] === undefined) { + html += "
" + key + ":
null
\n"; + } + if (typeof obj[key] === "object") { + html += "
" + key + ":
" + parseJSON(obj[key]) + "
\n"; + } + html += "
\n"; + } + return html; + }*/ + + if (my_scope.removeDataReady) { + my_scope.removeDataReady(); + } + my_scope.removeDataReady = my_scope.$on('DataReady', function(e, event_data, host) { + //var html = "
\n"; + //html += "

" + host.name + "

\n"; + //html += (host.description && host.description !== "imported") ? "
" + host.description + "
" : ""; + //html += "

Event " + id + " details:

\n"; + //html += "
\n"; + var html = "
\n"; + event_data.host = host.name; + html += parseJSON(event_data); + html += "
\n"; + html += "
\n"; + + $('#event-viewer-dialog').empty().html(html); + + CreateDialog({ + scope: my_scope, + width: 600, + height: 550, + minWidth: 450, + callback: 'ModalReady', + id: 'event-viewer-dialog', + title: 'Host Results', + onOpen: function() { + $('#dialog-ok-button').focus(); + } + }); + }); + + if (my_scope.removeModalReady) { + my_scope.removeModalReady(); + } + my_scope.removeModalReady = my_scope.$on('ModalReady', function() { + Wait('stop'); + $('#event-viewer-dialog').dialog('open'); + }); + + url = scope.job.related.job_events + "?id=" + id; + Wait('start'); + Rest.setUrl(url); + Rest.get() + .success( function(data) { + var key; + Wait('stop'); + if (data.results.length > 0 && data.results[0].event_data.res) { + for (key in data.results[0].event_data) { + if (key !== "res") { + data.results[0].event_data.res[key] = data.results[0].event_data[key]; + } + } + if (data.results[0].event_data.res.ansible_facts) { + delete data.results[0].event_data.res.ansible_facts; + } + data.results[0].event_data.res.status = (data.results[0].event === "runner_on_skipped") ? 'skipped' : (data.results[0].failed) ? 'failed' : + (data.results[0].changed) ? 'changed' : 'successful'; + my_scope.$emit('DataReady', data.results[0].event_data.res, data.results[0].summary_fields.host, data.results[0].id); + } + else { + data.results[0].event_data.status = (data.results[0].event === "runner_on_skipped") ? 'skipped' : (data.results[0].failed) ? 'failed' : + (data.results[0].changed) ? 'changed' : 'successful'; + my_scope.$emit('DataReady', data.results[0].event_data, data.results[0].summary_fields.host, data.results[0].id); + } + }) + .error(function(data, status) { + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Call to ' + url + '. GET returned: ' + status }); + }); + + scope.modalOK = function() { + $('#event-viewer-dialog').dialog('close'); + my_scope.$destroy(); + }; + }; }]); \ No newline at end of file diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index d00d78101d..eb2ba148d7 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -975,7 +975,8 @@ input[type="checkbox"].checkbox-no-label { .icon-job-success:before, .icon-job-successful:before, .icon-job-changed:before, - .icon-job-ok:before { + .icon-job-ok:before, + .icon-job-skipped:before { content: "\f111"; } @@ -991,7 +992,6 @@ input[type="checkbox"].checkbox-no-label { .icon-job-waiting:before, .icon-job-new:before, .icon-job-none:before, - .icon-job-skipped:before, .icon-job-no-matching-hosts:before { content: "\f10c"; } @@ -1004,6 +1004,10 @@ input[type="checkbox"].checkbox-no-label { color: @green; } + .icon-job-skipped { + color: @skipped; + } + .icon-job-running { .pulsate(); } @@ -1025,7 +1029,6 @@ input[type="checkbox"].checkbox-no-label { .icon-job-pending, .icon-job-waiting, .icon-job-new, - .icon-job-skipped, .icon-job-no-matching-hosts { color: @grey; opacity: 0.45; diff --git a/awx/ui/static/less/job-details.less b/awx/ui/static/less/job-details.less index abdec4de73..fbec2bd10e 100644 --- a/awx/ui/static/less/job-details.less +++ b/awx/ui/static/less/job-details.less @@ -387,6 +387,60 @@ svg text.percent{ font-weight: bold; } +#event-viewer-dialog { + padding-bottom: 5px; + padding-top: 10px; + padding-left: 5px; + padding-right: 5px; + overflow: hidden; + .results { + width: 100%; + height: 100%; + overflow: auto; + } + .spacer { + height: 60px; + } + table { + border-collapse: collapse; + width: 100%; + border-bottom: 1px solid @well-border; + } + tr { + border-top: 1px solid @well-border; + /*border-bottom: 1px solid @well-border;*/ + } + tr:first-child { + border-top: none; + } + .key { + vertical-align: top; + padding: 3px; + font-weight: 600; + } + .value { + padding: 3px; + i { + font-size: 12px; + } + } + .nested-table { + border: none; + padding: 0; + table { + border-top: none; + border-bottom: none; + width: auto; + tr:first-child { + border-top: none; + } + .key { + font-weight: 400; + } + } + } +} + @media (max-width: 767px) { #job-detail-container { #job-status-form { diff --git a/awx/ui/static/partials/job_detail.html b/awx/ui/static/partials/job_detail.html index 9999715852..66a837c91c 100644 --- a/awx/ui/static/partials/job_detail.html +++ b/awx/ui/static/partials/job_detail.html @@ -151,10 +151,9 @@
-
- +
{{ result.msg }} @@ -226,8 +225,8 @@
- + +