Files
awx/awx/ui/static/js/helpers/EventViewer.js
2015-04-24 12:21:59 -04:00

565 lines
26 KiB
JavaScript

/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* EventsViewer.js
*
*/
/**
* @ngdoc function
* @name helpers.function:EventViewer
* @description eventviewerhelper
*/
export default
angular.module('EventViewerHelper', ['ModalDialog', 'Utilities', 'EventsViewerFormDefinition', 'HostsHelper'])
.factory('EventViewer', ['$compile', 'CreateDialog', 'GetEvent', 'Wait', 'EventAddTable', 'GetBasePath', 'LookUpName', 'Empty', 'EventAddPreFormattedText',
function($compile, CreateDialog, GetEvent, Wait, EventAddTable, GetBasePath, LookUpName, Empty, EventAddPreFormattedText) {
return function(params) {
var parent_scope = params.scope,
url = params.url,
event_id = params.event_id,
parent_id = params.parent_id,
title = params.title, //optional
scope = parent_scope.$new(true),
index = params.index,
page,
current_event;
if (scope.removeShowNextEvent) {
scope.removeShowNextEvent();
}
scope.removeShowNextEvent = scope.$on('ShowNextEvent', function(e, data, show_event) {
scope.events = data;
$('#event-next-spinner').slideUp(200);
if (show_event === 'prev') {
showEvent(scope.events.length - 1);
}
else if (show_event === 'next') {
showEvent(0);
}
});
// show scope.events[idx]
function showEvent(idx) {
var show_tabs = false, elem, data;
if (idx > scope.events.length - 1) {
GetEvent({
scope: scope,
url: scope.next_event_set,
show_event: 'next'
});
return;
}
if (idx < 0) {
GetEvent({
scope: scope,
url: scope.prev_event_set,
show_event: 'prev'
});
return;
}
data = scope.events[idx];
current_event = idx;
$('#status-form-container').empty();
$('#results-form-container').empty();
$('#timing-form-container').empty();
$('#stdout-form-container').empty();
$('#stderr-form-container').empty();
$('#traceback-form-container').empty();
$('#json-form-container').empty();
$('#eventview-tabs li:eq(1)').hide();
$('#eventview-tabs li:eq(2)').hide();
$('#eventview-tabs li:eq(3)').hide();
$('#eventview-tabs li:eq(4)').hide();
$('#eventview-tabs li:eq(5)').hide();
$('#eventview-tabs li:eq(6)').hide();
EventAddTable({ scope: scope, id: 'status-form-container', event: data, section: 'Event' });
if (EventAddTable({ scope: scope, id: 'results-form-container', event: data, section: 'Results'})) {
show_tabs = true;
$('#eventview-tabs li:eq(1)').show();
}
if (EventAddTable({ scope: scope, id: 'timing-form-container', event: data, section: 'Timing' })) {
show_tabs = true;
$('#eventview-tabs li:eq(2)').show();
}
if (data.stdout) {
show_tabs = true;
$('#eventview-tabs li:eq(3)').show();
EventAddPreFormattedText({
id: 'stdout-form-container',
val: data.stdout
});
}
if (data.stderr) {
show_tabs = true;
$('#eventview-tabs li:eq(4)').show();
EventAddPreFormattedText({
id: 'stderr-form-container',
val: data.stderr
});
}
if (data.traceback) {
show_tabs = true;
$('#eventview-tabs li:eq(5)').show();
EventAddPreFormattedText({
id: 'traceback-form-container',
val: data.traceback
});
}
show_tabs = true;
$('#eventview-tabs li:eq(6)').show();
EventAddPreFormattedText({
id: 'json-form-container',
val: JSON.stringify(data, null, 2)
});
if (!show_tabs) {
$('#eventview-tabs').hide();
}
elem = angular.element(document.getElementById('eventviewer-modal-dialog'));
$compile(elem)(scope);
}
function setButtonMargin() {
var width = ($('.ui-dialog[aria-describedby="eventviewer-modal-dialog"] .ui-dialog-buttonpane').innerWidth() / 2) - $('#events-next-button').outerWidth() - 73;
$('#events-next-button').css({'margin-right': width + 'px'});
}
function addSpinner() {
var position;
if ($('#event-next-spinner').length > 0) {
$('#event-next-spinner').remove();
}
position = $('#events-next-button').position();
$('#events-next-button').after('<i class="fa fa-cog fa-spin" id="event-next-spinner" style="display:none; position:absolute; top:' + (position.top + 15) + 'px; left:' + (position.left + 75) + 'px;"></i>');
}
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 btns;
scope.events = data;
if (event_id) {
// find and show the selected event
data.every(function(row, idx) {
if (parseInt(row.id,10) === parseInt(event_id,10)) {
current_event = idx;
return false;
}
return true;
});
}
else {
current_event = 0;
}
showEvent(current_event);
btns = [];
if (scope.events.length > 1) {
btns.push({
label: "Prev",
onClick: function () {
if (current_event - 1 === 0 && !scope.prev_event_set) {
$('#events-prev-button').prop('disabled', true);
}
if (current_event - 1 < scope.events.length - 1) {
$('#events-next-button').prop('disabled', false);
}
showEvent(current_event - 1);
},
icon: "fa-chevron-left",
"class": "btn btn-primary",
id: "events-prev-button"
});
btns.push({
label: "Next",
onClick: function() {
if (current_event + 1 > 0) {
$('#events-prev-button').prop('disabled', false);
}
if (current_event + 1 >= scope.events.length - 1 && !scope.next_event_set) {
$('#events-next-button').prop('disabled', true);
}
showEvent(current_event + 1);
},
icon: "fa-chevron-right",
"class": "btn btn-primary",
id: "events-next-button"
});
}
btns.push({
label: "OK",
onClick: function() {
scope.modalOK();
},
icon: "",
"class": "btn btn-primary",
id: "dialog-ok-button"
});
CreateDialog({
scope: scope,
width: 675,
height: 600,
minWidth: 450,
callback: 'ModalReady',
id: 'eventviewer-modal-dialog',
// onResizeStop: resizeText,
title: ( (title) ? title : 'Host Event' ),
buttons: btns,
closeOnEscape: true,
onResizeStop: function() {
setButtonMargin();
addSpinner();
},
onClose: function() {
try {
scope.$destroy();
}
catch(e) {
//ignore
}
},
onOpen: function() {
$('#eventview-tabs a:first').tab('show');
$('#dialog-ok-button').focus();
if (scope.events.length > 1 && current_event === 0 && !scope.prev_event_set) {
$('#events-prev-button').prop('disabled', true);
}
if ((current_event === scope.events.length - 1) && !scope.next_event_set) {
$('#events-next-button').prop('disabled', true);
}
if (scope.events.length > 1) {
setButtonMargin();
addSpinner();
}
}
});
});
page = (index) ? Math.ceil((index+1)/50) : 1;
url += (/\/$/.test(url)) ? '?' : '&';
url += (parent_id) ? 'page='+page +'&parent=' + parent_id + '&page_size=50&order=host_name,counter' : 'page_size=50&order=host_name,counter';
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,
show_event = params.show_event,
results= [];
if (show_event) {
$('#event-next-spinner').show();
}
else {
Wait('start');
}
function getStatus(e) {
return (e.event === "runner_on_unreachable") ? "unreachable" : (e.event === "runner_on_skipped") ? 'skipped' : (e.failed) ? 'failed' :
(e.changed) ? 'changed' : 'ok';
}
Rest.setUrl(url);
Rest.get()
.success( function(data) {
if(jQuery.isEmptyObject(data)) {
Wait('stop');
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get event ' + url + '. ' });
}
else {
scope.next_event_set = data.next;
scope.prev_event_set = data.previous;
data.results.forEach(function(event) {
var msg, key, event_data = {};
if (event.event_data.res) {
if (typeof event.event_data.res !== 'object') {
// turn event_data.res into an object
msg = event.event_data.res;
event.event_data.res = {};
event.event_data.res.msg = msg;
}
for (key in event.event_data) {
if (key !== "res") {
event.event_data.res[key] = event.event_data[key];
}
}
if (event.event_data.res.ansible_facts) {
// don't show fact gathering results
event.event_data.res.task = "Gathering Facts";
delete event.event_data.res.ansible_facts;
}
event.event_data.res.status = getStatus(event);
event_data = event.event_data.res;
}
else {
event.event_data.status = getStatus(event);
event_data = event.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.play = event.play;
if (event.task) {
event_data.task = event.task;
}
event_data.created = event.created;
event_data.role = event.role;
event_data.host_id = event.host;
event_data.host_name = event.host_name;
if (event_data.host) {
delete event_data.host;
}
event_data.id = event.id;
event_data.parent = event.parent;
event_data.event = (event.event_display) ? event.event_display : event.event;
results.push(event_data);
});
if (show_event) {
scope.$emit('ShowNextEvent', results, show_event);
}
else {
scope.$emit('EventReady', results);
}
} //else statement
})
.error(function(data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get event ' + url + '. GET returned: ' + status });
});
};
}])
.factory('EventAddTable', ['$compile', '$filter', 'Empty', 'EventsViewerForm', function($compile, $filter, Empty, EventsViewerForm) {
return function(params) {
var scope = params.scope,
id = params.id,
event = params.event,
section = params.section,
html = '', e;
function parseObject(obj) {
// parse nested JSON objects. a mini version of parseJSON without references to the event form object.
var i, key, html = '';
for (key in obj) {
if (typeof obj[key] === "boolean" || typeof obj[key] === "number" || typeof obj[key] === "string") {
html += "<tr><td class=\"key\">" + key + ":</td><td class=\"value\">" + obj[key] + "</td></tr>";
}
else if (typeof obj[key] === "object" && Array.isArray(obj[key])) {
html += "<tr><td class=\"key\">" + key + ":</td><td class=\"value\">[";
for (i = 0; i < obj[key].length; i++) {
html += obj[key][i] + ",";
}
html = html.replace(/,$/,'');
html += "]</td></tr>\n";
}
else if (typeof obj[key] === "object") {
html += "<tr><td class=\"key\">" + key + ":</td><td class=\"nested-table\"><table>\n<tbody>\n" + parseObject(obj[key]) + "</tbody>\n</table>\n</td></tr>\n";
}
}
return html;
}
function parseItem(itm, key, label) {
var i, html = '';
if (Empty(itm)) {
// exclude empty items
}
else if (typeof itm === "boolean" || typeof itm === "number" || typeof itm === "string") {
html += "<tr><td class=\"key\">" + label + ":</td><td class=\"value\">";
if (key === "status") {
html += "<i class=\"fa icon-job-" + itm + "\"></i> " + itm;
}
else if (key === "start" || key === "end" || key === "created") {
if (!/Z$/.test(itm)) {
itm = itm.replace(/\ /,'T') + 'Z';
html += $filter('date')(itm, 'MM/dd/yy HH:mm:ss.sss');
}
else {
html += $filter('date')(itm, 'MM/dd/yy HH:mm:ss');
}
}
else if (key === "host_name" && event.host_id) {
html += "<a href=\"/#/home/hosts/?id=" + event.host_id + "\" target=\"_blank\" " +
"aw-tool-tip=\"View host. Opens in new tab or window.\" data-placement=\"top\" " +
">" + itm + "</a>";
}
else {
if( typeof itm === "string"){
if(itm.indexOf('<') > -1 || itm.indexOf('>') > -1){
itm = itm.replace(/</g, "&lt;");
itm = itm.replace(/>/g, "&gt;");
}
}
html += "<span ng-non-bindable>" + itm + "</span>";
}
html += "</td></tr>\n";
}
else if (typeof itm === "object" && Array.isArray(itm)) {
html += "<tr><td class=\"key\">" + label + ":</td><td class=\"value\">[";
for (i = 0; i < itm.length; i++) {
html += itm[i] + ",";
}
html = html.replace(/,$/,'');
html += "]</td></tr>\n";
}
else if (typeof itm === "object") {
html += "<tr><td class=\"key\">" + label + ":</td><td class=\"nested-table\"><table>\n<tbody>\n" + parseObject(itm) + "</tbody>\n</table>\n</td></tr>\n";
}
return html;
}
function parseJSON(obj) {
var h, html = '', key, keys, found = false, string_warnings = "", string_cmd = "";
if (typeof obj === "object") {
html += "<table class=\"table eventviewer-status\">\n";
html += "<tbody>\n";
keys = [];
for (key in EventsViewerForm.fields) {
if (EventsViewerForm.fields[key].section === section) {
keys.push(key);
}
}
keys.forEach(function(key) {
var h, label;
label = EventsViewerForm.fields[key].label;
h = parseItem(obj[key], key, label);
if (h) {
html += h;
found = true;
}
});
if (section === 'Results') {
// Add to result fields that might not be found in the form object.
for (key in obj) {
h = '';
if (key !== 'host_id' && key !== 'parent' && key !== 'event' && key !== 'src' && key !== 'md5sum' &&
key !== 'stdout' && key !== 'traceback' && key !== 'stderr' && key !== 'cmd' && key !=='changed' && key !== "verbose_override" &&
key !== 'feature_result' && key !== 'warnings') {
if (!EventsViewerForm.fields[key]) {
h = parseItem(obj[key], key, key);
if (h) {
html += h;
found = true;
}
}
} else if (key === 'cmd') {
// only show cmd if it's a cmd that was run
if (!EventsViewerForm.fields[key] && obj[key].length > 0) {
// include the label head Shell Command instead of CMD in the modal
string_cmd += obj[key].join(" ");
h = parseItem(string_cmd, key, "Shell Command");
if (h) {
html += h;
found = true;
}
}
} else if (key === 'warnings') {
if (!EventsViewerForm.fields[key] && obj[key].length > 0) {
string_warnings += obj[key].join(" ");
h = parseItem(string_warnings, key, "Warnings");
if (h) {
html += h;
found = true;
}
}
}
}
}
html += "</tbody>\n";
html += "</table>\n";
}
return (found) ? html : '';
}
html = parseJSON(event);
e = angular.element(document.getElementById(id));
e.empty();
if (html) {
e.html(html);
$compile(e)(scope);
}
return (html) ? true : false;
};
}])
.factory('EventAddTextarea', [ function() {
return function(params) {
var container_id = params.container_id,
val = params.val,
fld_id = params.fld_id,
html;
html = "<div class=\"form-group\">\n" +
"<textarea ng-non-bindable id=\"" + fld_id + "\" class=\"form-control mono-space\" rows=\"12\" readonly>" + val + "</textarea>" +
"</div>\n";
$('#' + container_id).empty().html(html);
};
}])
.factory('EventAddPreFormattedText', [function() {
return function(params) {
var id = params.id,
val = params.val,
html;
if( typeof val === "string"){
if(val.indexOf('<') > -1 || val.indexOf('>') > -1){
val = val.replace(/</g, "&lt;");
val = val.replace(/>/g, "&gt;");
}
}
html = "<pre ng-non-bindable>" + val + "</pre>\n";
$('#' + id).empty().html(html);
};
}]);