mirror of
https://github.com/ZwareBear/awx.git
synced 2026-04-28 19:01:48 -05:00
add event processing for stats and host status components
This commit is contained in:
@@ -206,12 +206,11 @@
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.HostStatusBar-ok,
|
||||
.HostStatusBar-changed,
|
||||
.HostStatusBar-unreachable,
|
||||
.HostStatusBar-dark,
|
||||
.HostStatusBar-failed,
|
||||
.HostStatusBar-skipped,
|
||||
.HostStatusBar-noData {
|
||||
@@ -231,7 +230,7 @@
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.HostStatusBar-unreachable {
|
||||
.HostStatusBar-dark {
|
||||
background-color: @default-unreachable;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
@@ -265,7 +264,7 @@
|
||||
background-color: @default-succ;
|
||||
}
|
||||
|
||||
.HostStatusBar-tooltipBadge--unreachable {
|
||||
.HostStatusBar-tooltipBadge--dark {
|
||||
background-color: @default-unreachable;
|
||||
}
|
||||
|
||||
|
||||
@@ -441,7 +441,7 @@ function AtDetailsController (
|
||||
|
||||
vm.init = _$scope_ => {
|
||||
$scope = _$scope_;
|
||||
resource = $scope.resource;
|
||||
resource = $scope.resource; // eslint-disable-line prefer-destructuring
|
||||
|
||||
vm.status = getStatusDetails();
|
||||
vm.started = getStartTimeDetails();
|
||||
@@ -466,27 +466,35 @@ function AtDetailsController (
|
||||
vm.extraVars = getExtraVarsDetails();
|
||||
vm.labels = getLabelDetails();
|
||||
|
||||
// Relaunch Component
|
||||
vm.job = _.get(resource.model, 'model.GET', {});
|
||||
|
||||
// XX - Codemirror
|
||||
if (vm.extraVars) {
|
||||
const cm = {
|
||||
parseType: 'yaml',
|
||||
$apply: $scope.$apply,
|
||||
variables: vm.extraVars.value,
|
||||
};
|
||||
|
||||
ParseTypeChange({
|
||||
field_id: 'cm-extra-vars',
|
||||
readOnly: true,
|
||||
scope: cm,
|
||||
});
|
||||
}
|
||||
|
||||
vm.cancelJob = cancelJob;
|
||||
vm.deleteJob = deleteJob;
|
||||
vm.toggleLabels = toggleLabels;
|
||||
|
||||
const observe = (key, transform) => {
|
||||
$scope.$watch(key, value => { this[key] = transform(value); });
|
||||
$scope.$watch(key, value => { vm[key] = transform(value); });
|
||||
};
|
||||
|
||||
observe('finished', getFinishTimeDetails);
|
||||
observe('status', getStatusDetails);
|
||||
observe('started', getStartTimeDetails);
|
||||
observe('finished', getFinishTimeDetails);
|
||||
|
||||
// relaunch component
|
||||
$scope.job = _.get(resource.model, 'model.GET', {});
|
||||
this.job = $scope.job;
|
||||
|
||||
// codemirror
|
||||
if (this.extraVars) {
|
||||
const cm = { parseType: 'yaml', variables: this.extraVars.value, $apply: $scope.$apply };
|
||||
ParseTypeChange({ scope: cm, field_id: 'cm-extra-vars', readOnly: true });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
<!-- LEFT PANE HEADER -->
|
||||
<!-- todo: styling, css etc. - disposition according to project lib conventions -->
|
||||
<div class="JobResults-panelHeader">
|
||||
<div class="JobResults-panelHeaderText" translate> DETAILS</div>
|
||||
<!-- LEFT PANE HEADER ACTIONS -->
|
||||
<div class="JobResults-panelHeaderButtonActions">
|
||||
<!-- RELAUNCH ACTION -->
|
||||
<at-relaunch state="vm.job"></at-relaunch>
|
||||
<at-relaunch job="vm.job"></at-relaunch>
|
||||
|
||||
<!-- CANCEL ACTION -->
|
||||
<button class="List-actionButton List-actionButton--delete"
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
let vm;
|
||||
const JOB_START = 'playbook_on_start';
|
||||
const JOB_END = 'playbook_on_stats';
|
||||
const PLAY_START = 'playbook_on_play_start';
|
||||
const TASK_START = 'playbook_on_task_start';
|
||||
|
||||
let $compile;
|
||||
let $scope;
|
||||
let $q;
|
||||
let $scope;
|
||||
let $state;
|
||||
let moment;
|
||||
let page;
|
||||
let qs;
|
||||
let render;
|
||||
let resource;
|
||||
let scroll;
|
||||
let stream;
|
||||
let resource;
|
||||
let $state;
|
||||
let qs;
|
||||
|
||||
let hack;
|
||||
let vm;
|
||||
|
||||
let eventCounter;
|
||||
let statsEvent;
|
||||
|
||||
function JobsIndexController (
|
||||
_resource_,
|
||||
@@ -23,6 +31,7 @@ function JobsIndexController (
|
||||
_$q_,
|
||||
_$state_,
|
||||
_qs_,
|
||||
_moment_,
|
||||
) {
|
||||
vm = this || {};
|
||||
|
||||
@@ -36,6 +45,8 @@ function JobsIndexController (
|
||||
render = _render_;
|
||||
stream = _stream_;
|
||||
|
||||
moment = _moment_;
|
||||
|
||||
// Development helper(s)
|
||||
vm.clear = devClear;
|
||||
|
||||
@@ -69,11 +80,19 @@ function JobsIndexController (
|
||||
vm.removeSearchTag = removeSearchTag;
|
||||
vm.searchTags = getSearchTags(getCurrentQueryset());
|
||||
|
||||
// Host Status Bar
|
||||
// Events
|
||||
eventCounter = null;
|
||||
statsEvent = resource.stats;
|
||||
|
||||
// Status Bar
|
||||
vm.status = {
|
||||
stats: statsEvent,
|
||||
elapsed: resource.model.get('elapsed'),
|
||||
running: Boolean(resource.model.get('started')) && !resource.model.get('finished'),
|
||||
stats: resource.stats,
|
||||
}
|
||||
title: resource.model.get('name'),
|
||||
plays: null,
|
||||
tasks: null,
|
||||
};
|
||||
|
||||
// Details
|
||||
vm.details = {
|
||||
@@ -83,33 +102,10 @@ function JobsIndexController (
|
||||
status: resource.model.get('status'),
|
||||
};
|
||||
|
||||
render.requestAnimationFrame(() => init());
|
||||
render.requestAnimationFrame(() => init(!vm.status.running));
|
||||
}
|
||||
|
||||
function onStreamStart (data) {
|
||||
const status = _.get(data, 'summary_fields.job.status');
|
||||
|
||||
if (!hack) {
|
||||
hack = true;
|
||||
vm.details.status = status;
|
||||
vm.details.started = data.created;
|
||||
|
||||
vm.status.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
function onStreamFinish (data) {
|
||||
const failed = _.get(data, 'summary_fields.job.failed');
|
||||
|
||||
vm.details.status = failed ? 'failed' : 'successful';
|
||||
vm.details.finished = data.created;
|
||||
|
||||
vm.status = { stats: data, running: false };
|
||||
};
|
||||
|
||||
function init (pageMode) {
|
||||
hack = false;
|
||||
|
||||
page.init({
|
||||
resource,
|
||||
});
|
||||
@@ -123,26 +119,68 @@ function init (pageMode) {
|
||||
scroll.init({
|
||||
isAtRest: scrollIsAtRest,
|
||||
previous,
|
||||
next
|
||||
next,
|
||||
});
|
||||
|
||||
stream.init({
|
||||
page,
|
||||
scroll,
|
||||
resource,
|
||||
onStreamStart,
|
||||
onStreamFinish,
|
||||
render: events => shift().then(() => append(events, true)),
|
||||
listen: (namespace, listener) => {
|
||||
$scope.$on(namespace, (scope, data) => listener(data));
|
||||
onEventFrame (events) {
|
||||
return shift().then(() => append(events, true));
|
||||
},
|
||||
onStart () {
|
||||
vm.status.plays = 0;
|
||||
vm.status.tasks = 0;
|
||||
vm.status.running = true;
|
||||
},
|
||||
onStop () {
|
||||
vm.status.stats = statsEvent;
|
||||
vm.status.running = false;
|
||||
|
||||
vm.details.status = statsEvent.failed ? 'failed' : 'successful';
|
||||
vm.details.finished = statsEvent.created;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on(resource.ws.namespace, handleSocketEvent);
|
||||
|
||||
if (pageMode) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
function handleSocketEvent (scope, data) {
|
||||
const isLatest = ((!eventCounter) || (data.counter > eventCounter));
|
||||
|
||||
if (isLatest) {
|
||||
eventCounter = data.counter;
|
||||
|
||||
vm.details.status = _.get(data, 'summary_fields.job.status');
|
||||
|
||||
vm.status.elapsed = moment(data.created)
|
||||
.diff(resource.model.get('created'), 'seconds');
|
||||
}
|
||||
|
||||
if (data.event === JOB_START) {
|
||||
vm.details.started = data.created;
|
||||
}
|
||||
|
||||
if (data.event === PLAY_START) {
|
||||
vm.status.plays++;
|
||||
}
|
||||
|
||||
if (data.event === TASK_START) {
|
||||
vm.status.tasks++;
|
||||
}
|
||||
|
||||
if (data.event === JOB_END) {
|
||||
statsEvent = data;
|
||||
}
|
||||
|
||||
stream.pushEventData(data);
|
||||
}
|
||||
|
||||
function devClear (pageMode) {
|
||||
init(pageMode);
|
||||
render.clear();
|
||||
@@ -158,9 +196,11 @@ function next () {
|
||||
return shift()
|
||||
.then(() => append(events))
|
||||
.then(() => {
|
||||
if(scroll.isMissing()) {
|
||||
if (scroll.isMissing()) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -242,9 +282,11 @@ function scrollHome () {
|
||||
scroll.resume();
|
||||
})
|
||||
.then(() => {
|
||||
if(scroll.isMissing()) {
|
||||
if (scroll.isMissing()) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -359,7 +401,7 @@ function toggleSearchKey () {
|
||||
}
|
||||
|
||||
function getCurrentQueryset () {
|
||||
const { job_event_search } = $state.params;
|
||||
const { job_event_search } = $state.params; // eslint-disable-line camelcase
|
||||
|
||||
return qs.decodeArr(job_event_search);
|
||||
}
|
||||
@@ -414,6 +456,7 @@ JobsIndexController.$inject = [
|
||||
'$q',
|
||||
'$state',
|
||||
'QuerySet',
|
||||
'moment',
|
||||
];
|
||||
|
||||
module.exports = JobsIndexController;
|
||||
|
||||
@@ -10,7 +10,7 @@ import StreamService from '~features/output/stream.service';
|
||||
|
||||
import DetailsDirective from '~features/output/details.directive';
|
||||
import SearchKeyDirective from '~features/output/search-key.directive';
|
||||
import StatusDirective from '~features/output/status.directive';
|
||||
import StatsDirective from '~features/output/stats.directive';
|
||||
|
||||
const Template = require('~features/output/index.view.html');
|
||||
|
||||
@@ -31,7 +31,7 @@ function resolveResource (
|
||||
qs,
|
||||
Wait
|
||||
) {
|
||||
const { id, type, job_event_search } = $stateParams;
|
||||
const { id, type, job_event_search } = $stateParams; // eslint-disable-line camelcase
|
||||
|
||||
let Resource;
|
||||
let related = 'events';
|
||||
@@ -61,7 +61,7 @@ function resolveResource (
|
||||
const params = { page_size: PAGE_SIZE, order_by: 'start_line' };
|
||||
const config = { pageCache: PAGE_CACHE, pageLimit: PAGE_LIMIT, params };
|
||||
|
||||
if (job_event_search) {
|
||||
if (job_event_search) { // eslint-disable-line camelcase
|
||||
const queryParams = qs.encodeQuerysetObject(qs.decodeArr(job_event_search));
|
||||
|
||||
Object.assign(config.params, queryParams);
|
||||
@@ -211,7 +211,7 @@ angular
|
||||
.service('JobStreamService', StreamService)
|
||||
.directive('atDetails', DetailsDirective)
|
||||
.directive('atSearchKey', SearchKeyDirective)
|
||||
.directive('atStatus', StatusDirective)
|
||||
.directive('atStats', StatsDirective)
|
||||
.run(JobsRun);
|
||||
|
||||
export default MODULE_NAME;
|
||||
|
||||
@@ -13,7 +13,14 @@
|
||||
|
||||
<div class="col-md-8">
|
||||
<at-panel class="at-Stdout">
|
||||
<at-status running="vm.status.running" stats="vm.status.stats"></at-status>
|
||||
<at-stats
|
||||
elapsed="vm.status.elapsed"
|
||||
running="vm.status.running"
|
||||
stats="vm.status.stats"
|
||||
title="vm.status.title"
|
||||
plays="vm.status.plays"
|
||||
tasks="vm.status.tasks">
|
||||
</at-stats>
|
||||
<!-- search ===================================================================================== -->
|
||||
<form ng-submit="vm.search()">
|
||||
<div class="input-group">
|
||||
|
||||
@@ -250,7 +250,6 @@ function JobRenderService ($q, $sce, $window) {
|
||||
|
||||
this.clear = () => {
|
||||
const elements = this.el.children();
|
||||
|
||||
return this.remove(elements);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const templateUrl = require('~features/output/status.partial.html');
|
||||
const templateUrl = require('~features/output/stats.partial.html');
|
||||
|
||||
const HOST_STATUS_KEYS = ['dark', 'failures', 'changed', 'ok', 'skipped'];
|
||||
|
||||
@@ -21,23 +21,25 @@ function getHostStatusCounts (statsEvent) {
|
||||
});
|
||||
});
|
||||
|
||||
counts.hosts = countedHostNames.length;
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
function createStatusBarTooltip (key, count) {
|
||||
function createStatsBarTooltip (key, count) {
|
||||
const label = `<span class='HostStatusBar-tooltipLabel'>${key}</span>`;
|
||||
const badge = `<span class='badge HostStatusBar-tooltipBadge HostStatusBar-tooltipBadge--${key}'>${count}</span>`;
|
||||
|
||||
return `${label}${badge}`;
|
||||
}
|
||||
|
||||
function atStatusLink (scope, el, attrs, controllers) {
|
||||
const [atStatusController] = controllers;
|
||||
function atStatsLink (scope, el, attrs, controllers) {
|
||||
const [atStatsController] = controllers;
|
||||
|
||||
atStatusController.init(scope);
|
||||
atStatsController.init(scope);
|
||||
}
|
||||
|
||||
function AtStatusController (strings) {
|
||||
function AtStatsController (strings) {
|
||||
const vm = this || {};
|
||||
|
||||
vm.tooltips = {
|
||||
@@ -46,12 +48,22 @@ function AtStatusController (strings) {
|
||||
};
|
||||
|
||||
vm.init = scope => {
|
||||
const { running, stats } = scope;
|
||||
const { elapsed, running, stats, title, plays, tasks } = scope;
|
||||
|
||||
vm.title = title;
|
||||
|
||||
vm.plays = plays;
|
||||
vm.tasks = tasks;
|
||||
vm.elapsed = elapsed;
|
||||
vm.running = running || false;
|
||||
|
||||
vm.setStats(stats);
|
||||
|
||||
scope.$watch('elapsed', value => { vm.elapsed = value; });
|
||||
scope.$watch('running', value => { vm.running = value; });
|
||||
scope.$watch('plays', value => { vm.plays = value; });
|
||||
scope.$watch('tasks', value => { vm.tasks = value; });
|
||||
|
||||
scope.$watch('stats', vm.setStats);
|
||||
};
|
||||
|
||||
@@ -64,29 +76,34 @@ function AtStatusController (strings) {
|
||||
|
||||
statusBarElement.css('flex', `${count} 0 auto`);
|
||||
|
||||
vm.tooltips[key] = createStatusBarTooltip(key, count);
|
||||
vm.tooltips[key] = createStatsBarTooltip(key, count);
|
||||
});
|
||||
|
||||
vm.hosts = counts.hosts;
|
||||
vm.statsAreAvailable = Boolean(stats);
|
||||
};
|
||||
}
|
||||
|
||||
function atStatus () {
|
||||
function atStats () {
|
||||
return {
|
||||
templateUrl,
|
||||
restrict: 'E',
|
||||
require: ['atStatus'],
|
||||
require: ['atStats'],
|
||||
controllerAs: 'vm',
|
||||
link: atStatusLink,
|
||||
link: atStatsLink,
|
||||
controller: [
|
||||
'JobStrings',
|
||||
AtStatusController
|
||||
AtStatsController
|
||||
],
|
||||
scope: {
|
||||
elapsed: '=',
|
||||
running: '=',
|
||||
stats: '=',
|
||||
title: '=',
|
||||
plays: '=',
|
||||
tasks: '=',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default atStatus;
|
||||
export default atStats;
|
||||
@@ -1,9 +1,8 @@
|
||||
const JOB_START = 'playbook_on_start';
|
||||
const JOB_END = 'playbook_on_stats';
|
||||
const MAX_LAG = 120;
|
||||
|
||||
function JobStreamService ($q) {
|
||||
this.init = ({ resource, scroll, page, onStreamStart, onStreamFinish, render, listen }) => {
|
||||
this.init = ({ resource, scroll, page, onEventFrame, onStart, onStop }) => {
|
||||
this.resource = resource;
|
||||
this.scroll = scroll;
|
||||
this.page = page;
|
||||
@@ -13,20 +12,21 @@ function JobStreamService ($q) {
|
||||
this.pageCount = 0;
|
||||
this.chain = $q.resolve();
|
||||
this.factors = this.getBatchFactors(this.resource.page.size);
|
||||
|
||||
this.state = {
|
||||
started: false,
|
||||
paused: false,
|
||||
pausing: false,
|
||||
resuming: false,
|
||||
ending: false,
|
||||
ended: false
|
||||
ended: false,
|
||||
counting: false,
|
||||
};
|
||||
|
||||
this.hooks = {
|
||||
onStreamStart,
|
||||
onStreamFinish,
|
||||
render,
|
||||
listen,
|
||||
onEventFrame,
|
||||
onStart,
|
||||
onStop,
|
||||
};
|
||||
|
||||
this.lines = {
|
||||
@@ -36,8 +36,6 @@ function JobStreamService ($q) {
|
||||
min: 0,
|
||||
max: 0
|
||||
};
|
||||
|
||||
this.hooks.listen(resource.ws.namespace, this.listener);
|
||||
};
|
||||
|
||||
this.getBatchFactors = size => {
|
||||
@@ -91,7 +89,7 @@ function JobStreamService ($q) {
|
||||
this.lines.used.push(i);
|
||||
}
|
||||
|
||||
let missing = [];
|
||||
const missing = [];
|
||||
for (let i = this.lines.min; i < this.lines.max; i++) {
|
||||
if (this.lines.used.indexOf(i) === -1) {
|
||||
missing.push(i);
|
||||
@@ -107,25 +105,19 @@ function JobStreamService ($q) {
|
||||
}
|
||||
};
|
||||
|
||||
this.listener = data => {
|
||||
this.pushEventData = data => {
|
||||
this.lag++;
|
||||
|
||||
this.chain = this.chain
|
||||
.then(() => {
|
||||
// console.log(data);
|
||||
if (!this.isActive()) {
|
||||
this.start();
|
||||
if (!this.isEnding()) {
|
||||
this.hooks.onStreamStart(data);
|
||||
}
|
||||
} else if (data.event === JOB_END) {
|
||||
if (this.isPaused()) {
|
||||
this.end(true);
|
||||
} else {
|
||||
this.end();
|
||||
}
|
||||
|
||||
this.hooks.onStreamFinish(data);
|
||||
}
|
||||
|
||||
this.checkLines(data);
|
||||
@@ -142,9 +134,11 @@ function JobStreamService ($q) {
|
||||
return this.renderFrame(events);
|
||||
})
|
||||
.then(() => --this.lag);
|
||||
|
||||
return this.chain;
|
||||
};
|
||||
|
||||
this.renderFrame = events => this.hooks.render(events)
|
||||
this.renderFrame = events => this.hooks.onEventFrame(events)
|
||||
.then(() => {
|
||||
if (this.scroll.isLocked()) {
|
||||
this.scroll.scrollToBottom();
|
||||
@@ -190,9 +184,13 @@ function JobStreamService ($q) {
|
||||
};
|
||||
|
||||
this.start = () => {
|
||||
this.state.started = true;
|
||||
this.scroll.pause();
|
||||
this.scroll.lock();
|
||||
if (!this.state.ending && !this.state.ended) {
|
||||
this.state.started = true;
|
||||
this.scroll.pause();
|
||||
this.scroll.lock();
|
||||
|
||||
this.hooks.onStart();
|
||||
}
|
||||
};
|
||||
|
||||
this.end = done => {
|
||||
@@ -202,13 +200,15 @@ function JobStreamService ($q) {
|
||||
this.scroll.unlock();
|
||||
this.scroll.resume();
|
||||
|
||||
this.hooks.onStop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.ending = true;
|
||||
};
|
||||
|
||||
this.isReadyToRender = () => this.isEnding() ||
|
||||
this.isReadyToRender = () => this.isDone() ||
|
||||
(!this.isPaused() && this.hasAllLines() && this.isBatchFull());
|
||||
this.hasAllLines = () => this.lines.ready;
|
||||
this.isBatchFull = () => this.count % this.framesPerRender === 0;
|
||||
|
||||
Reference in New Issue
Block a user