diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index cd0c710470..0a8801d4b7 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -98,6 +98,7 @@ angular.module('Tower', [ 'LogViewerStatusDefinition', 'LogViewerHelper', 'LogViewerOptionsDefinition', + 'EventViewerHelper', 'JobDetailHelper', 'SocketIO' ]) diff --git a/awx/ui/static/js/controllers/JobDetail.js b/awx/ui/static/js/controllers/JobDetail.js index ceed30f3b3..1bf027c587 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, ViewHostResults) { + JobIsFinished, SetTaskStyles, DigestEvent, UpdateDOM, EventViewer) { ClearScope(); @@ -1155,9 +1155,10 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, }; scope.viewHostResults = function(id) { - ViewHostResults({ + EventViewer({ scope: scope, - id: id + url: scope.job.related.job_events + '?id=' + id, + title: 'Host Result' }); }; @@ -1165,5 +1166,5 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, 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', 'ViewHostResults' + 'LoadHostSummary', 'ReloadHostSummaryList', 'JobIsFinished', 'SetTaskStyles', 'DigestEvent', 'UpdateDOM', 'EventViewer' ]; diff --git a/awx/ui/static/js/helpers/EventViewer.js b/awx/ui/static/js/helpers/EventViewer.js new file mode 100644 index 0000000000..06a356c84c --- /dev/null +++ b/awx/ui/static/js/helpers/EventViewer.js @@ -0,0 +1,298 @@ +/********************************************* + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * LogViewer.js + * + */ + +'use strict'; + +angular.module('EventViewerHelper', ['ModalDialog', 'Utilities']) + + .factory('EventViewer', ['$compile', 'CreateDialog', 'GetEvent', 'Wait', 'AddTable', 'GetBasePath', 'LookUpName', 'Empty', 'AddPreFormattedText', + function($compile, CreateDialog, GetEvent, Wait, AddTable, GetBasePath, LookUpName, Empty, AddPreFormattedText) { + return function(params) { + var parent_scope = params.scope, + url = params.url, + title = params.title, //optional + scope = parent_scope.$new(true); + + if (scope.removeModalReady) { + scope.removeModalReady(); + } + scope.removeModalReady = scope.$on('ModalReady', function() { + Wait('stop'); + $('#eventviewer-modal-dialog').dialog('open'); + }); + + if (scope.removeJobReady) { + scope.removeJobReady(); + } + scope.removeEventReady = scope.$on('EventReady', function(e, data) { + var elem; + + $('#status-form-container').empty(); + $('#stdout-form-container').empty(); + $('#stderr-form-container').empty(); + $('#traceback-form-container').empty(); + $('#eventview-tabs li:eq(1)').hide(); + $('#eventview-tabs li:eq(2)').hide(); + $('#eventview-tabs li:eq(3)').hide(); + + AddTable({ scope: scope, id: 'status-form-container', event: data }); + + if (data.stdout) { + $('#eventview-tabs li:eq(1)').show(); + AddPreFormattedText({ + id: 'stdout-form-container', + val: data.stdout + }); + } + + if (data.stderr) { + $('#eventview-tabs li:eq(2)').show(); + AddPreFormattedText({ + id: 'stderr-form-container', + val: data.stderr + }); + } + + if (data.traceback) { + $('#eventview-tabs li:eq(3)').show(); + AddPreFormattedText({ + id: 'traceback-form-container', + val: data.traceback + }); + } + + elem = angular.element(document.getElementById('eventviewer-modal-dialog')); + $compile(elem)(scope); + + CreateDialog({ + scope: scope, + width: 675, + height: 600, + minWidth: 450, + callback: 'ModalReady', + id: 'eventviewer-modal-dialog', + // onResizeStop: resizeText, + title: ( (title) ? title : 'Event Details' ), + onOpen: function() { + $('#eventview-tabs a:first').tab('show'); + $('#dialog-ok-button').focus(); + } + }); + }); + + GetEvent({ + url: url, + scope: scope + }); + + scope.modalOK = function() { + $('#eventviewer-modal-dialog').dialog('close'); + scope.$destroy(); + }; + }; + }]) + + .factory('GetEvent', ['Wait', 'Rest', 'ProcessErrors', function(Wait, Rest, ProcessErrors) { + return function(params) { + var url = params.url, + scope = params.scope; + + function getStatus(data) { + return (data.results[0].event === "runner_on_unreachable") ? "unreachable" : (data.results[0].event === "runner_on_skipped") ? 'skipped' : (data.results[0].failed) ? 'failed' : + (data.results[0].changed) ? 'changed' : 'successful'; + } + + Wait('start'); + Rest.setUrl(url); + Rest.get() + .success( function(data) { + var key, event_data = {}; + 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) { + // don't show fact gathering results + delete data.results[0].event_data.res.ansible_facts; + } + data.results[0].event_data.res.status = getStatus(data); + event_data = data.results[0].event_data.res; + } + else { + data.results[0].event_data.status = getStatus(data); + event_data = data.results[0].event_data; + } + // convert results to stdout + if (event_data.results && typeof event_data.results === "object" && Array.isArray(event_data.results)) { + event_data.stdout = ""; + event_data.results.forEach(function(row) { + event_data.stdout += row + "\n"; + }); + delete event_data.results; + } + if (event_data.invocation) { + for (key in event_data.invocation) { + event_data[key] = event_data.invocation[key]; + } + delete event_data.invocation; + } + event_data.parent = data.results[0].parent; + event_data.play = data.results[0].play; + event_data.task = data.results[0].task; + event_data.created = data.results[0].created; + event_data.role = data.results[0].role; + event_data.host_id = data.results[0].host; + event_data.host_name = data.results[0].host_name; + event_data.id = data.results[0].id; + event_data.parent = data.results[0].parent; + scope.$emit('EventReady', event_data); + }) + .error(function(data, status) { + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to get event ' + url + '. GET returned: ' + status }); + }); + }; + }]) + + .factory('LookUpName', ['Rest', 'ProcessErrors', 'Empty', function(Rest, ProcessErrors, Empty) { + return function(params) { + var url = params.url, + scope_var = params.scope_var, + scope = params.scope; + Rest.setUrl(url); + Rest.get() + .success(function(data) { + if (scope_var === 'inventory_source') { + scope[scope_var + '_name'] = data.summary_fields.group.name; + } + else if (!Empty(data.name)) { + scope[scope_var + '_name'] = data.name; + } + if (!Empty(data.group)) { + // Used for inventory_source + scope.group = data.group; + } + }) + .error(function(data, status) { + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to retrieve ' + url + '. GET returned: ' + status }); + }); + }; + }]) + + .factory('AddTable', ['$compile', '$filter', 'Empty', function($compile, $filter, Empty) { + return function(params) { + var scope = params.scope, + id = params.id, + event = params.event, + html = '', e; + + function keyToLabel(key) { + var label = ''; + switch(key) { + case "id": + label = "Event ID"; + break; + case "parent": + label = "Parent Event ID"; + break; + case "rc": + label = "Return Code"; + break; + default: + label = key.charAt(0).toUpperCase() + key.slice(1); + label = label.replace(/(\_.)/g, function(match) { + var res; + res = match.replace(/\_/,''); + res = ' ' + res.toUpperCase(); + return res; + }); + } + return label; + } + + function parseJSON(obj) { + var html = '', keys; + if (typeof obj === "object") { + html += "
" + val + "\n"; + $('#' + id).empty().html(html); + }; + }]); \ No newline at end of file diff --git a/awx/ui/static/js/helpers/JobDetail.js b/awx/ui/static/js/helpers/JobDetail.js index 74374b4f7d..6f37d4db53 100644 --- a/awx/ui/static/js/helpers/JobDetail.js +++ b/awx/ui/static/js/helpers/JobDetail.js @@ -1168,124 +1168,4 @@ 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 += "
| " + 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]) + " |
Event " + id + " details:
\n"; - //html += "