mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 16:51:11 +03:00
Merge pull request #1755 from jakemcdermott/1747
always render initial set of job events on initialization for still-running jobs
This commit is contained in:
commit
a74b8fa744
@ -95,7 +95,7 @@ class ReplayJobEvents():
|
||||
raise RuntimeError("Job is of type {} and replay is not yet supported.".format(type(job)))
|
||||
sys.exit(1)
|
||||
|
||||
def run(self, job_id, speed=1.0, verbosity=0):
|
||||
def run(self, job_id, speed=1.0, verbosity=0, skip=0):
|
||||
stats = {
|
||||
'events_ontime': {
|
||||
'total': 0,
|
||||
@ -126,7 +126,10 @@ class ReplayJobEvents():
|
||||
sys.exit(1)
|
||||
|
||||
je_previous = None
|
||||
for je_current in job_events:
|
||||
for n, je_current in enumerate(job_events):
|
||||
if n < skip:
|
||||
continue
|
||||
|
||||
if not je_previous:
|
||||
stats['recording_start'] = je_current.created
|
||||
self.start(je_current.created)
|
||||
@ -163,21 +166,25 @@ class ReplayJobEvents():
|
||||
|
||||
stats['events_total'] += 1
|
||||
je_previous = je_current
|
||||
|
||||
stats['replay_end'] = self.now()
|
||||
stats['replay_duration'] = (stats['replay_end'] - stats['replay_start']).total_seconds()
|
||||
stats['replay_start'] = stats['replay_start'].isoformat()
|
||||
stats['replay_end'] = stats['replay_end'].isoformat()
|
||||
|
||||
stats['recording_end'] = je_current.created
|
||||
stats['recording_duration'] = (stats['recording_end'] - stats['recording_start']).total_seconds()
|
||||
stats['recording_start'] = stats['recording_start'].isoformat()
|
||||
stats['recording_end'] = stats['recording_end'].isoformat()
|
||||
if stats['events_total'] > 2:
|
||||
stats['replay_end'] = self.now()
|
||||
stats['replay_duration'] = (stats['replay_end'] - stats['replay_start']).total_seconds()
|
||||
stats['replay_start'] = stats['replay_start'].isoformat()
|
||||
stats['replay_end'] = stats['replay_end'].isoformat()
|
||||
|
||||
stats['recording_end'] = je_current.created
|
||||
stats['recording_duration'] = (stats['recording_end'] - stats['recording_start']).total_seconds()
|
||||
stats['recording_start'] = stats['recording_start'].isoformat()
|
||||
stats['recording_end'] = stats['recording_end'].isoformat()
|
||||
|
||||
stats['events_ontime']['percentage'] = (stats['events_ontime']['total'] / float(stats['events_total'])) * 100.00
|
||||
stats['events_late']['percentage'] = (stats['events_late']['total'] / float(stats['events_total'])) * 100.00
|
||||
stats['events_distance_average'] = stats['events_distance_total'] / stats['events_total']
|
||||
stats['events_late']['lateness_average'] = stats['events_late']['lateness_total'] / stats['events_late']['total']
|
||||
else:
|
||||
stats = {'events_total': stats['events_total']}
|
||||
|
||||
stats['events_ontime']['percentage'] = (stats['events_ontime']['total'] / float(stats['events_total'])) * 100.00
|
||||
stats['events_late']['percentage'] = (stats['events_late']['total'] / float(stats['events_total'])) * 100.00
|
||||
stats['events_distance_average'] = stats['events_distance_total'] / stats['events_total']
|
||||
stats['events_late']['lateness_average'] = stats['events_late']['lateness_total'] / stats['events_late']['total']
|
||||
if verbosity >= 2:
|
||||
print(json.dumps(stats, indent=4, sort_keys=True))
|
||||
|
||||
@ -191,11 +198,14 @@ class Command(BaseCommand):
|
||||
help='Id of the job to replay (job or adhoc)')
|
||||
parser.add_argument('--speed', dest='speed', type=int, metavar='s',
|
||||
help='Speedup factor.')
|
||||
parser.add_argument('--skip', dest='skip', type=int, metavar='k',
|
||||
help='Number of events to skip.')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
job_id = options.get('job_id')
|
||||
speed = options.get('speed') or 1
|
||||
verbosity = options.get('verbosity') or 0
|
||||
skip = options.get('skip') or 0
|
||||
|
||||
replayer = ReplayJobEvents()
|
||||
replayer.run(job_id, speed, verbosity)
|
||||
replayer.run(job_id, speed, verbosity, skip)
|
||||
|
@ -38,6 +38,12 @@ function JobEventEngine ($q) {
|
||||
};
|
||||
};
|
||||
|
||||
this.setMinLine = min => {
|
||||
if (min > this.lines.min) {
|
||||
this.lines.min = min;
|
||||
}
|
||||
};
|
||||
|
||||
this.getBatchFactors = size => {
|
||||
const factors = [1];
|
||||
|
||||
@ -140,10 +146,6 @@ function JobEventEngine ($q) {
|
||||
|
||||
this.renderFrame = events => this.hooks.onEventFrame(events)
|
||||
.then(() => {
|
||||
if (this.scroll.isLocked()) {
|
||||
this.scroll.scrollToBottom();
|
||||
}
|
||||
|
||||
if (this.isEnding()) {
|
||||
const lastEvents = this.page.emptyBuffer();
|
||||
|
||||
|
@ -7,8 +7,11 @@ let resource;
|
||||
let scroll;
|
||||
let engine;
|
||||
let status;
|
||||
let $http;
|
||||
|
||||
let vm;
|
||||
let streaming;
|
||||
let listeners = [];
|
||||
|
||||
function JobsIndexController (
|
||||
_resource_,
|
||||
@ -20,6 +23,7 @@ function JobsIndexController (
|
||||
_$compile_,
|
||||
_$q_,
|
||||
_status_,
|
||||
_$http_,
|
||||
) {
|
||||
vm = this || {};
|
||||
|
||||
@ -33,6 +37,7 @@ function JobsIndexController (
|
||||
render = _render_;
|
||||
engine = _engine_;
|
||||
status = _status_;
|
||||
$http = _$http_;
|
||||
|
||||
// Development helper(s)
|
||||
vm.clear = devClear;
|
||||
@ -67,7 +72,6 @@ function init () {
|
||||
});
|
||||
|
||||
render.init({
|
||||
get: () => resource.model.get(`related.${resource.related}.results`),
|
||||
compile: html => $compile(html)($scope),
|
||||
isStreamActive: engine.isActive,
|
||||
});
|
||||
@ -90,32 +94,72 @@ function init () {
|
||||
status.setJobStatus('running');
|
||||
},
|
||||
onStop () {
|
||||
stopListening();
|
||||
status.updateStats();
|
||||
status.dispatch();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on(resource.ws.events, handleJobEvent);
|
||||
$scope.$on(resource.ws.status, handleStatusEvent);
|
||||
|
||||
if (!status.state.running) {
|
||||
next();
|
||||
}
|
||||
streaming = false;
|
||||
return next().then(() => startListening());
|
||||
}
|
||||
|
||||
function handleStatusEvent (scope, data) {
|
||||
function stopListening () {
|
||||
listeners.forEach(deregister => deregister());
|
||||
listeners = [];
|
||||
}
|
||||
|
||||
function startListening () {
|
||||
stopListening();
|
||||
listeners.push($scope.$on(resource.ws.events, (scope, data) => handleJobEvent(data)));
|
||||
listeners.push($scope.$on(resource.ws.status, (scope, data) => handleStatusEvent(data)));
|
||||
}
|
||||
|
||||
function handleStatusEvent (data) {
|
||||
status.pushStatusEvent(data);
|
||||
}
|
||||
|
||||
function handleJobEvent (scope, data) {
|
||||
engine.pushJobEvent(data);
|
||||
|
||||
status.pushJobEvent(data);
|
||||
function handleJobEvent (data) {
|
||||
streaming = streaming || attachToRunningJob();
|
||||
streaming.then(() => {
|
||||
engine.pushJobEvent(data);
|
||||
status.pushJobEvent(data);
|
||||
});
|
||||
}
|
||||
|
||||
function devClear (pageMode) {
|
||||
init(pageMode);
|
||||
render.clear();
|
||||
function attachToRunningJob () {
|
||||
const target = `${resource.model.get('url')}${resource.related}/`;
|
||||
const params = { order_by: '-created', page_size: resource.page.size };
|
||||
|
||||
scroll.pause();
|
||||
|
||||
return render.clear()
|
||||
.then(() => $http.get(target, { params }))
|
||||
.then(res => {
|
||||
const { results } = res.data;
|
||||
|
||||
const minLine = 1 + Math.max(...results.map(event => event.end_line));
|
||||
const maxCount = Math.max(...results.map(event => event.counter));
|
||||
|
||||
const lastPage = resource.model.updateCount(maxCount);
|
||||
|
||||
page.emptyCache(lastPage);
|
||||
page.addPage(lastPage, [], true);
|
||||
|
||||
engine.setMinLine(minLine);
|
||||
|
||||
if (resource.model.page.current === lastPage) {
|
||||
return $q.resolve();
|
||||
}
|
||||
|
||||
return append(results);
|
||||
})
|
||||
.then(() => {
|
||||
scroll.setScrollPosition(scroll.getScrollHeight());
|
||||
scroll.resume();
|
||||
|
||||
return $q.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function next () {
|
||||
@ -281,6 +325,10 @@ function toggleExpanded () {
|
||||
vm.expanded = !vm.expanded;
|
||||
}
|
||||
|
||||
function devClear () {
|
||||
render.clear().then(() => init());
|
||||
}
|
||||
|
||||
// function showHostDetails (id) {
|
||||
// jobEvent.request('get', id)
|
||||
// .then(() => {
|
||||
@ -331,6 +379,7 @@ JobsIndexController.$inject = [
|
||||
'$compile',
|
||||
'$q',
|
||||
'JobStatusService',
|
||||
'$http',
|
||||
];
|
||||
|
||||
module.exports = JobsIndexController;
|
||||
|
@ -70,13 +70,20 @@ function resolveResource (
|
||||
return null;
|
||||
}
|
||||
|
||||
const params = { page_size: PAGE_SIZE, order_by: 'start_line' };
|
||||
const config = { pageCache: PAGE_CACHE, pageLimit: PAGE_LIMIT, params };
|
||||
const params = {
|
||||
page_size: PAGE_SIZE,
|
||||
order_by: 'start_line',
|
||||
};
|
||||
|
||||
const config = {
|
||||
params,
|
||||
pageCache: PAGE_CACHE,
|
||||
pageLimit: PAGE_LIMIT,
|
||||
};
|
||||
|
||||
if (job_event_search) { // eslint-disable-line camelcase
|
||||
const queryParams = qs.encodeQuerysetObject(qs.decodeArr(job_event_search));
|
||||
|
||||
Object.assign(config.params, queryParams);
|
||||
const query = qs.encodeQuerysetObject(qs.decodeArr(job_event_search));
|
||||
Object.assign(config.params, query);
|
||||
}
|
||||
|
||||
Wait('start');
|
||||
@ -89,7 +96,6 @@ function resolveResource (
|
||||
}
|
||||
|
||||
promises.push(model.extend('get', related, config));
|
||||
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(([stats, model]) => ({
|
||||
@ -107,18 +113,19 @@ function resolveResource (
|
||||
size: PAGE_SIZE,
|
||||
pageLimit: PAGE_LIMIT
|
||||
}
|
||||
}))
|
||||
.finally(() => Wait('stop'));
|
||||
}));
|
||||
|
||||
if (!handleErrors) {
|
||||
return resourcePromise;
|
||||
return resourcePromise
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
|
||||
return resourcePromise
|
||||
.catch(({ data, status }) => {
|
||||
$state.go($state.current, $state.params, { reload: true });
|
||||
qs.error(data, status);
|
||||
});
|
||||
return $state.go($state.current, $state.params, { reload: true });
|
||||
})
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
|
||||
function resolveWebSocketConnection ($stateParams, SocketService) {
|
||||
|
@ -30,11 +30,11 @@ const re = new RegExp(pattern);
|
||||
const hasAnsi = input => re.test(input);
|
||||
|
||||
function JobRenderService ($q, $sce, $window) {
|
||||
this.init = ({ compile, apply, isStreamActive }) => {
|
||||
this.init = ({ compile, isStreamActive }) => {
|
||||
this.parent = null;
|
||||
this.record = {};
|
||||
this.el = $(ELEMENT_TBODY);
|
||||
this.hooks = { isStreamActive, compile, apply };
|
||||
this.hooks = { isStreamActive, compile };
|
||||
};
|
||||
|
||||
this.sortByLineNumber = (a, b) => {
|
||||
@ -239,8 +239,6 @@ function JobRenderService ($q, $sce, $window) {
|
||||
return list;
|
||||
};
|
||||
|
||||
this.getEvents = () => this.hooks.get();
|
||||
|
||||
this.insert = (events, insert) => {
|
||||
const result = this.transformEventGroup(events);
|
||||
const html = this.trustHtml(result.html);
|
||||
|
@ -49,48 +49,60 @@ function JobStatusService (moment, message) {
|
||||
};
|
||||
|
||||
this.pushStatusEvent = data => {
|
||||
const isJobEvent = (this.job === data.unified_job_id);
|
||||
const isProjectEvent = (this.project && (this.project === data.project_id));
|
||||
const isJobStatusEvent = (this.job === data.unified_job_id);
|
||||
const isProjectStatusEvent = (this.project && (this.project === data.project_id));
|
||||
|
||||
if (isJobEvent) {
|
||||
if (isJobStatusEvent) {
|
||||
this.setJobStatus(data.status);
|
||||
} else if (isProjectEvent) {
|
||||
this.dispatch();
|
||||
} else if (isProjectStatusEvent) {
|
||||
this.setProjectStatus(data.status);
|
||||
this.setProjectUpdateId(data.unified_job_id);
|
||||
this.dispatch();
|
||||
}
|
||||
};
|
||||
|
||||
this.pushJobEvent = data => {
|
||||
const isLatest = ((!this.counter) || (data.counter > this.counter));
|
||||
|
||||
let changed = false;
|
||||
|
||||
if (!this.active && !(data.event === JOB_END)) {
|
||||
this.active = true;
|
||||
this.setJobStatus('running');
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (isLatest) {
|
||||
this.counter = data.counter;
|
||||
this.latestTime = data.created;
|
||||
this.setElapsed(moment(data.created).diff(this.created, 'seconds'));
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (data.event === JOB_START) {
|
||||
this.setStarted(this.state.started || data.created);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (data.event === PLAY_START) {
|
||||
this.state.counts.plays++;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (data.event === TASK_START) {
|
||||
this.state.counts.tasks++;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (data.event === JOB_END) {
|
||||
this.setStatsEvent(data);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
this.dispatch();
|
||||
if (changed) {
|
||||
this.dispatch();
|
||||
}
|
||||
};
|
||||
|
||||
this.isExpectingStatsEvent = () => (this.jobType === 'job') ||
|
||||
|
@ -398,6 +398,13 @@ function extend (method, related, config = {}) {
|
||||
return Promise.reject(new Error(`No related property, ${related}, exists`));
|
||||
}
|
||||
|
||||
function updateCount (count) {
|
||||
this.page.count = count;
|
||||
this.page.last = Math.ceil(count / this.page.size);
|
||||
|
||||
return this.page.last;
|
||||
}
|
||||
|
||||
function goToPage (config) {
|
||||
const params = config.params || {};
|
||||
const { page } = config;
|
||||
@ -693,6 +700,7 @@ function BaseModel (resource, settings) {
|
||||
this.extend = extend;
|
||||
this.copy = copy;
|
||||
this.getDependentResourceCounts = getDependentResourceCounts;
|
||||
this.updateCount = updateCount;
|
||||
|
||||
this.http = {
|
||||
get: httpGet.bind(this),
|
||||
|
Loading…
Reference in New Issue
Block a user