mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 18:07:36 -02:30
Adds the host event modal to the standard out feature
Removes old host modal code
This commit is contained in:
committed by
Jake McDermott
parent
18dc0e9066
commit
fe58b74d1e
@@ -1,3 +1,4 @@
|
||||
@import 'host-event/_index';
|
||||
.at-Stdout {
|
||||
&-menuTop {
|
||||
color: @at-gray-848992;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<!-- todo: styling, css etc. - disposition according to project lib conventions -->
|
||||
<div ui-view></div>
|
||||
<div class="JobResults-panelHeader">
|
||||
<div class="JobResults-panelHeaderText" translate> DETAILS</div>
|
||||
<!-- LEFT PANE HEADER ACTIONS -->
|
||||
|
||||
190
awx/ui/client/features/output/host-event/_index.less
Normal file
190
awx/ui/client/features/output/host-event/_index.less
Normal file
@@ -0,0 +1,190 @@
|
||||
.noselect {
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Chrome/Safari/Opera */
|
||||
-khtml-user-select: none; /* Konqueror */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
not supported by any browser */
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px){
|
||||
.HostEvent .modal-dialog{
|
||||
width: 700px;
|
||||
}
|
||||
}
|
||||
.HostEvent .CodeMirror{
|
||||
overflow-x: hidden;
|
||||
max-height: none!important;
|
||||
}
|
||||
|
||||
.HostEvent-close:hover{
|
||||
color: @btn-txt;
|
||||
background-color: @btn-bg-hov;
|
||||
}
|
||||
|
||||
.HostEvent-body{
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.HostEvent-tab {
|
||||
color: @btn-txt;
|
||||
background-color: @btn-bg;
|
||||
font-size: 12px;
|
||||
border: 1px solid @btn-bord;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
margin-right: 20px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
transition: background-color 0.2s;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
.noselect;
|
||||
}
|
||||
.HostEvent-tab:hover {
|
||||
color: @btn-txt;
|
||||
background-color: @btn-bg-hov;
|
||||
cursor: pointer;
|
||||
}
|
||||
.HostEvent-tab--selected{
|
||||
color: @btn-txt-sel!important;
|
||||
background-color: @default-icon!important;
|
||||
border-color: @default-icon!important;
|
||||
}
|
||||
.HostEvent-view--container{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.HostEvent .modal-footer{
|
||||
border: 0;
|
||||
margin-top: 0px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.HostEvent-controls{
|
||||
float: right;
|
||||
button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.HostEvent-status--ok{
|
||||
color: @green;
|
||||
}
|
||||
.HostEvent-status--unreachable{
|
||||
color: @unreachable;
|
||||
}
|
||||
.HostEvent-status--changed{
|
||||
color: @changed;
|
||||
}
|
||||
.HostEvent-status--failed{
|
||||
color: @default-err;
|
||||
}
|
||||
.HostEvent-status--skipped{
|
||||
color: @skipped;
|
||||
}
|
||||
.HostEvent-header{
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.HostEvent-title{
|
||||
color: @default-interface-txt;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.HostEvent .modal-body{
|
||||
padding: 0px!important;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.HostEvent-nav{
|
||||
padding-top: 12px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.HostEvent-field{
|
||||
margin-bottom: 8px;
|
||||
flex: 0 1 12em;
|
||||
}
|
||||
.HostEvent-field--label{
|
||||
text-transform: uppercase;
|
||||
flex: 0 1 80px;
|
||||
max-width: 80px;
|
||||
min-width: 80px;
|
||||
font-size: 12px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.HostEvent-field{
|
||||
.OnePlusTwo-left--detailsRow;
|
||||
}
|
||||
.HostEvent-field--content{
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.HostEvent-field--monospaceContent{
|
||||
font-family: monospace;
|
||||
}
|
||||
.HostEvent-button:disabled {
|
||||
pointer-events: all!important;
|
||||
}
|
||||
|
||||
.HostEvent-stdout{
|
||||
height:200px;
|
||||
width:100%
|
||||
}
|
||||
|
||||
.HostEvent-stdoutContainer {
|
||||
height:200px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
font-style: normal;
|
||||
background-color: @default-no-items-bord;
|
||||
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
||||
.HostEvent-numberColumnPreload {
|
||||
background-color: @default-list-header-bg;
|
||||
height: 198px;
|
||||
border-right: 1px solid #ccc;
|
||||
width: 30px;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.HostEvent-numberColumn {
|
||||
background-color: @default-list-header-bg;
|
||||
border-right: 1px solid #ccc;
|
||||
border-bottom-left-radius: 5px;
|
||||
color: #999;
|
||||
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
position: fixed;
|
||||
padding: 4px 3px 0 5px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.HostEvent-numberColumn--second{
|
||||
padding-top:0px;
|
||||
}
|
||||
|
||||
.HostEvent-stdoutColumn{
|
||||
white-space: pre;
|
||||
overflow-y: scroll;
|
||||
margin-left: 46px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.HostEvent-noJson{
|
||||
align-items: center;
|
||||
background-color: @default-no-items-bord;
|
||||
border: 1px solid @default-icon-hov;
|
||||
border-radius: 5px;
|
||||
color: @b7grey;
|
||||
display: flex;
|
||||
height: 200px;
|
||||
justify-content: center;
|
||||
text-transform: uppercase;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<textarea ng-hide="no_json" id="HostEvent-codemirror" class="HostEvent-codemirror">
|
||||
</textarea>
|
||||
<div ng-if="no_json" class="HostEvent-noJson" translate>No JSON data returned by the module</div>
|
||||
@@ -0,0 +1,72 @@
|
||||
<div id="HostEvent" class="HostEvent modal" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<!-- modal body -->
|
||||
<div class="modal-body">
|
||||
<div class="HostEvent-header">
|
||||
<a class="HostEvents-status">
|
||||
<i class="fa fa-circle" ng-class="processEventStatus(event).class"></i>
|
||||
</a>
|
||||
<span class="HostEvent-title">{{event.host_name}}</span>
|
||||
<!-- close -->
|
||||
<button ng-click="closeHostEvent()" type="button" class="close">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="HostEvent-details">
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">CREATED</span>
|
||||
<span class="HostEvent-field--content">{{(event.created | longDate) || "No result found"}}</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">ID</span>
|
||||
<span class="HostEvent-field--content">{{event.id || "No result found"}}</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">PLAY</span>
|
||||
<span class="HostEvent-field--content">{{event.play || "No result found"}}</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">TASK</span>
|
||||
<span class="HostEvent-field--content">{{event.task || "No result found"}}</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">MODULE</span>
|
||||
<span class="HostEvent-field--content HostEvent-field--monospaceContent">{{module_name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="HostEvent-nav">
|
||||
<!-- view navigation buttons -->
|
||||
<button ui-sref="jobz.host-event.json" type="button"
|
||||
class="btn btn-sm btn-default HostEvent-tab"
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobz.host-event.json')}">
|
||||
JSON
|
||||
</button>
|
||||
<button ng-if="stdout" ui-sref="jobz.host-event.stdout"
|
||||
type="button" class="btn btn-sm btn-default HostEvent-tab"
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobz.host-event.stdout')}">
|
||||
Standard Out
|
||||
</button>
|
||||
<button ng-if="stderr" ui-sref="jobz.host-event.stderr"
|
||||
type="button" class="btn btn-sm btn-default HostEvent-tab"
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobz.host-event.stderr')}">
|
||||
Standard Error
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<div class="HostEvent-body">
|
||||
<!-- views -->
|
||||
<div class="HostEvent-view--container" ui-view></div>
|
||||
</div>
|
||||
|
||||
<!-- controls -->
|
||||
<div class="HostEvent-controls">
|
||||
<button ng-click="closeHostEvent()" class="btn btn-sm btn-default HostEvent-close">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="HostEvent-stdout">
|
||||
<div class="HostEvent-stdoutContainer">
|
||||
<div class="HostEvent-numberColumnPreload"></div>
|
||||
<div class="HostEvent-numberColumn">1</div>
|
||||
<div class="HostEvent-stdoutColumn" ng-bind-html="stderr"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div id="HostEvent-stdout" class="HostEvent-stdout">
|
||||
<div class="HostEvent-stdoutContainer">
|
||||
<div class="HostEvent-numberColumnPreload"></div>
|
||||
<div class="HostEvent-numberColumn">1</div>
|
||||
<div class="HostEvent-stdoutColumn" ng-bind-html="stdout"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,170 @@
|
||||
function HostEventsController (
|
||||
$scope,
|
||||
$state,
|
||||
HostEventService,
|
||||
hostEvent
|
||||
) {
|
||||
$scope.processEventStatus = HostEventService.processEventStatus;
|
||||
$scope.processResults = processResults;
|
||||
$scope.isActiveState = isActiveState;
|
||||
$scope.getActiveHostIndex = getActiveHostIndex;
|
||||
$scope.closeHostEvent = closeHostEvent;
|
||||
|
||||
function init () {
|
||||
hostEvent.event_name = hostEvent.event;
|
||||
$scope.event = _.cloneDeep(hostEvent);
|
||||
|
||||
// grab standard out & standard error if present from the host
|
||||
// event's 'res' object, for things like Ansible modules. Small
|
||||
// wrinkle in this implementation is that the stdout/stderr tabs
|
||||
// should be shown if the `res` object has stdout/stderr keys, even
|
||||
// if they're a blank string. The presence of these keys is
|
||||
// potentially significant to a user.
|
||||
if (_.has(hostEvent.event_data, 'task_action')) {
|
||||
$scope.module_name = hostEvent.event_data.task_action;
|
||||
} else if (!_.has(hostEvent.event_data, 'task_action')) {
|
||||
$scope.module_name = 'No result found';
|
||||
}
|
||||
|
||||
if (_.has(hostEvent.event_data, 'res.result.stdout')) {
|
||||
if (hostEvent.event_data.res.stdout === '') {
|
||||
$scope.stdout = ' ';
|
||||
} else {
|
||||
$scope.stdout = hostEvent.event_data.res.stdout;
|
||||
}
|
||||
}
|
||||
|
||||
if (_.has(hostEvent.event_data, 'res.result.stderr')) {
|
||||
if (hostEvent.event_data.res.stderr === '') {
|
||||
$scope.stderr = ' ';
|
||||
} else {
|
||||
$scope.stderr = hostEvent.event_data.res.stderr;
|
||||
}
|
||||
}
|
||||
|
||||
if (_.has(hostEvent.event_data, 'res')) {
|
||||
$scope.json = hostEvent.event_data.res;
|
||||
}
|
||||
|
||||
if ($scope.module_name === 'debug' &&
|
||||
_.has(hostEvent.event_data, 'res.result.stdout')) {
|
||||
$scope.stdout = hostEvent.event_data.res.result.stdout;
|
||||
}
|
||||
if ($scope.module_name === 'yum' &&
|
||||
_.has(hostEvent.event_data, 'res.results') &&
|
||||
_.isArray(hostEvent.event_data.res.results)) {
|
||||
const event = hostEvent.event_data.res.results;
|
||||
$scope.stdout = event[0];// eslint-disable-line prefer-destructuring
|
||||
}
|
||||
// instantiate Codemirror
|
||||
if ($state.current.name === 'jobz.host-event.json') {
|
||||
try {
|
||||
if (_.has(hostEvent.event_data, 'res')) {
|
||||
initCodeMirror(
|
||||
'HostEvent-codemirror',
|
||||
JSON.stringify($scope.json, null, 4),
|
||||
{ name: 'javascript', json: true }
|
||||
);
|
||||
resize();
|
||||
} else {
|
||||
$scope.no_json = true;
|
||||
}
|
||||
} catch (err) {
|
||||
// element with id HostEvent-codemirror is not the view
|
||||
// controlled by this instance of HostEventController
|
||||
}
|
||||
} else if ($state.current.name === 'jobz.host-event.stdout') {
|
||||
try {
|
||||
resize();
|
||||
} catch (err) {
|
||||
// element with id HostEvent-codemirror is not the view
|
||||
// controlled by this instance of HostEventController
|
||||
}
|
||||
} else if ($state.current.name === 'jobz.host-event.stderr') {
|
||||
try {
|
||||
resize();
|
||||
} catch (err) {
|
||||
// element with id HostEvent-codemirror is not the view
|
||||
// controlled by this instance of HostEventController
|
||||
}
|
||||
}
|
||||
$('#HostEvent').modal('show');
|
||||
$('.modal-content').resizable({
|
||||
minHeight: 523,
|
||||
minWidth: 600
|
||||
});
|
||||
$('.modal-dialog').draggable({
|
||||
cancel: '.HostEvent-view--container'
|
||||
});
|
||||
|
||||
function resize () {
|
||||
if ($state.current.name === 'jobz.host-event.json') {
|
||||
const editor = $('.CodeMirror')[0].CodeMirror;
|
||||
const height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120;
|
||||
editor.setSize('100%', height);
|
||||
} else if ($state.current.name === 'jobz.host-event.stdout' || $state.current.name === 'jobz.host-event.stderr') {
|
||||
const height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120;
|
||||
$('.HostEvent-stdout').width('100%');
|
||||
$('.HostEvent-stdout').height(height);
|
||||
$('.HostEvent-stdoutContainer').height(height);
|
||||
$('.HostEvent-numberColumnPreload').height(height);
|
||||
}
|
||||
}
|
||||
|
||||
$('.modal-dialog').on('resize', resize);
|
||||
|
||||
$('#HostEvent').on('hidden.bs.modal', $scope.closeHostEvent);
|
||||
}
|
||||
|
||||
function processResults (value) {
|
||||
if (typeof value === 'object') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function initCodeMirror (el, data, mode) {
|
||||
const container = document.getElementById(el);
|
||||
const options = {};
|
||||
options.lineNumbers = true;
|
||||
options.mode = mode;
|
||||
options.readOnly = true;
|
||||
options.scrollbarStyle = null;
|
||||
const editor = CodeMirror.fromTextArea(// eslint-disable-line no-undef
|
||||
container,
|
||||
options
|
||||
);
|
||||
editor.setSize('100%', 200);
|
||||
editor.getDoc().setValue(data);
|
||||
}
|
||||
|
||||
function isActiveState (name) {
|
||||
return $state.current.name === name;
|
||||
}
|
||||
|
||||
function getActiveHostIndex () {
|
||||
function hostResultfilter (obj) {
|
||||
return obj.id === $scope.event.id;
|
||||
}
|
||||
const result = $scope.hostResults.filter(hostResultfilter);
|
||||
return $scope.hostResults.indexOf(result[0]);
|
||||
}
|
||||
|
||||
function closeHostEvent () {
|
||||
// Unbind the listener so it doesn't fire when we close the modal via navigation
|
||||
$('#HostEvent').off('hidden.bs.modal');
|
||||
$('#HostEvent').modal('hide');
|
||||
$state.go('jobz');
|
||||
}
|
||||
$scope.init = init;
|
||||
$scope.init();
|
||||
}
|
||||
|
||||
HostEventsController.$inject = [
|
||||
'$scope',
|
||||
'$state',
|
||||
'HostEventService',
|
||||
'hostEvent',
|
||||
];
|
||||
|
||||
module.exports = HostEventsController;
|
||||
72
awx/ui/client/features/output/host-event/host-event.route.js
Normal file
72
awx/ui/client/features/output/host-event/host-event.route.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const HostEventModalTemplate = require('~features/output/host-event/host-event-modal.partial.html');
|
||||
const HostEventCodeMirrorTemplate = require('~features/output/host-event/host-event-codemirror.partial.html');
|
||||
const HostEventStdoutTemplate = require('~features/output/host-event/host-event-stdout.partial.html');
|
||||
const HostEventStderrTemplate = require('~features/output/host-event/host-event-stderr.partial.html');
|
||||
|
||||
function exit () {
|
||||
// close the modal
|
||||
// using an onExit event to handle cases where the user navs away
|
||||
// using the url bar / back and not modal "X"
|
||||
$('#HostEvent').modal('hide');
|
||||
// hacky way to handle user browsing away via URL bar
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open');
|
||||
}
|
||||
|
||||
function HostEventResolve (HostEventService, $stateParams) {
|
||||
return HostEventService.getRelatedJobEvents($stateParams.id, {
|
||||
id: $stateParams.eventId
|
||||
}).then((response) => response.data.results[0]);
|
||||
}
|
||||
|
||||
HostEventResolve.$inject = [
|
||||
'HostEventService',
|
||||
'$stateParams',
|
||||
];
|
||||
|
||||
const hostEventModal = {
|
||||
name: 'jobz.host-event',
|
||||
url: '/host-event/:eventId',
|
||||
controller: 'HostEventsController',
|
||||
templateUrl: HostEventModalTemplate,
|
||||
abstract: false,
|
||||
ncyBreadcrumb: {
|
||||
skip: true
|
||||
},
|
||||
resolve: {
|
||||
hostEvent: HostEventResolve
|
||||
},
|
||||
onExit: exit
|
||||
};
|
||||
|
||||
const hostEventJson = {
|
||||
name: 'jobz.host-event.json',
|
||||
url: '/json',
|
||||
controller: 'HostEventsController',
|
||||
templateUrl: HostEventCodeMirrorTemplate,
|
||||
ncyBreadcrumb: {
|
||||
skip: true
|
||||
},
|
||||
};
|
||||
|
||||
const hostEventStdout = {
|
||||
name: 'jobz.host-event.stdout',
|
||||
url: '/stdout',
|
||||
controller: 'HostEventsController',
|
||||
templateUrl: HostEventStdoutTemplate,
|
||||
ncyBreadcrumb: {
|
||||
skip: true
|
||||
},
|
||||
};
|
||||
|
||||
const hostEventStderr = {
|
||||
name: 'jobz.host-event.stderr',
|
||||
url: '/stderr',
|
||||
controller: 'HostEventsController',
|
||||
templateUrl: HostEventStderrTemplate,
|
||||
ncyBreadcrumb: {
|
||||
skip: true
|
||||
},
|
||||
};
|
||||
|
||||
export { hostEventJson, hostEventModal, hostEventStdout, hostEventStderr };
|
||||
@@ -0,0 +1,69 @@
|
||||
function HostEventService (
|
||||
Rest,
|
||||
ProcessErrors,
|
||||
GetBasePath,
|
||||
$rootScope
|
||||
) {
|
||||
// GET events related to a job run
|
||||
// e.g.
|
||||
// ?event=playbook_on_stats
|
||||
// ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter
|
||||
this.getRelatedJobEvents = (id, params) => {
|
||||
let url = GetBasePath('jobs');
|
||||
url = `${url}${id}/job_events/?${this.stringifyParams(params)}`;
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.then(response => response)
|
||||
.catch(({ data, status }) => {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: `Call to ${url}. GET returned: ${status}` });
|
||||
});
|
||||
};
|
||||
|
||||
this.stringifyParams = params => {
|
||||
function reduceFunction (result, value, key) {
|
||||
return `${result}${key}=${value}&`;
|
||||
}
|
||||
return _.reduce(params, reduceFunction, '');
|
||||
};
|
||||
|
||||
// Generate a helper class for job_event statuses
|
||||
// the stack for which status to display is
|
||||
// unreachable > failed > changed > ok
|
||||
// uses the API's runner events and convenience properties .failed .changed to determine status.
|
||||
// see: job_event_callback.py for more filters to support
|
||||
this.processEventStatus = event => {
|
||||
const obj = {};
|
||||
if (event.event === 'runner_on_unreachable') {
|
||||
obj.class = 'HostEvent-status--unreachable';
|
||||
obj.status = 'unreachable';
|
||||
}
|
||||
// equiv to 'runner_on_error' && 'runner on failed'
|
||||
if (event.failed) {
|
||||
obj.class = 'HostEvent-status--failed';
|
||||
obj.status = 'failed';
|
||||
}
|
||||
// catch the changed case before ok, because both can be true
|
||||
if (event.changed) {
|
||||
obj.class = 'HostEvent-status--changed';
|
||||
obj.status = 'changed';
|
||||
}
|
||||
if (event.event === 'runner_on_ok' || event.event === 'runner_on_async_ok') {
|
||||
obj.class = 'HostEvent-status--ok';
|
||||
obj.status = 'ok';
|
||||
}
|
||||
if (event.event === 'runner_on_skipped') {
|
||||
obj.class = 'HostEvent-status--skipped';
|
||||
obj.status = 'skipped';
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
HostEventService.$inject = [
|
||||
'Rest',
|
||||
'ProcessErrors',
|
||||
'GetBasePath',
|
||||
'$rootScope'
|
||||
];
|
||||
export default HostEventService;
|
||||
26
awx/ui/client/features/output/host-event/index.js
Normal file
26
awx/ui/client/features/output/host-event/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
hostEventModal,
|
||||
hostEventJson,
|
||||
hostEventStdout,
|
||||
hostEventStderr
|
||||
} from './host-event.route';
|
||||
import controller from './host-event.controller';
|
||||
import service from './host-event.service';
|
||||
|
||||
const MODULE_NAME = 'hostEvents';
|
||||
|
||||
function hostEventRun ($stateExtender) {
|
||||
$stateExtender.addState(hostEventModal);
|
||||
$stateExtender.addState(hostEventJson);
|
||||
$stateExtender.addState(hostEventStdout);
|
||||
$stateExtender.addState(hostEventStderr);
|
||||
}
|
||||
hostEventRun.$inject = [
|
||||
'$stateExtender'
|
||||
];
|
||||
|
||||
angular.module(MODULE_NAME, [])
|
||||
.controller('HostEventsController', controller)
|
||||
.service('HostEventService', service)
|
||||
.run(hostEventRun);
|
||||
export default MODULE_NAME;
|
||||
@@ -12,6 +12,7 @@ import StatusService from '~features/output/status.service';
|
||||
import DetailsDirective from '~features/output/details.directive';
|
||||
import SearchDirective from '~features/output/search.directive';
|
||||
import StatsDirective from '~features/output/stats.directive';
|
||||
import HostEvent from './host-event/index';
|
||||
|
||||
const Template = require('~features/output/index.view.html');
|
||||
|
||||
@@ -211,7 +212,8 @@ JobsRun.$inject = ['$stateRegistry'];
|
||||
angular
|
||||
.module(MODULE_NAME, [
|
||||
atLibModels,
|
||||
atLibComponents
|
||||
atLibComponents,
|
||||
HostEvent
|
||||
])
|
||||
.service('JobStrings', Strings)
|
||||
.service('JobPageService', PageService)
|
||||
|
||||
@@ -169,7 +169,7 @@ function JobRenderService ($q, $sce, $window) {
|
||||
}
|
||||
|
||||
if (current.isHost) {
|
||||
tdEvent = `<td class="at-Stdout-event--host" ng-click="vm.showHostDetails('${current.id}')">${content}</td>`;
|
||||
tdEvent = `<td class="at-Stdout-event--host" ui-sref="jobz.host-event.json({eventId: ${current.id}, taskUuid: '${current.uuid}' })">${content}</td>`;
|
||||
}
|
||||
|
||||
if (current.time && current.line === ln) {
|
||||
@@ -251,6 +251,7 @@ function JobRenderService ($q, $sce, $window) {
|
||||
});
|
||||
|
||||
this.compile = html => {
|
||||
html = $(this.el);
|
||||
this.hooks.compile(html);
|
||||
|
||||
return this.requestAnimationFrame();
|
||||
|
||||
Reference in New Issue
Block a user