diff options
author | Tiancong Wang <tcwang@google.com> | 2020-02-13 21:08:49 +0000 |
---|---|---|
committer | Tiancong Wang <tcwang@google.com> | 2020-02-13 21:08:49 +0000 |
commit | b75f321fc8978b92ce3db6886ccb966768f0c7a8 (patch) | |
tree | 35fa0fbaeaaddd9cc2a126a05eee3527b51e83a8 /deprecated/automation/server/monitor/dashboard.py | |
parent | cddd960b0ba2eb62c372c0d3176c75f0bd05d5e8 (diff) | |
parent | e617e3393dd24003aa976ece5050bb291070041c (diff) | |
download | toolchain-utils-b75f321fc8978b92ce3db6886ccb966768f0c7a8.tar.gz |
Merging 18 commit(s) from Chromium's toolchain-utils am: 0ae38c8498 am: 2a19d36a82 am: e617e3393dr_aml_301500702android-mainline-12.0.0_r55android-mainline-11.0.0_r9android-mainline-11.0.0_r8android-mainline-11.0.0_r7android-mainline-11.0.0_r6android-mainline-11.0.0_r5android-mainline-11.0.0_r45android-mainline-11.0.0_r44android-mainline-11.0.0_r43android-mainline-11.0.0_r42android-mainline-11.0.0_r41android-mainline-11.0.0_r40android-mainline-11.0.0_r4android-mainline-11.0.0_r39android-mainline-11.0.0_r38android-mainline-11.0.0_r37android-mainline-11.0.0_r36android-mainline-11.0.0_r35android-mainline-11.0.0_r34android-mainline-11.0.0_r33android-mainline-11.0.0_r32android-mainline-11.0.0_r31android-mainline-11.0.0_r30android-mainline-11.0.0_r3android-mainline-11.0.0_r29android-mainline-11.0.0_r28android-mainline-11.0.0_r27android-mainline-11.0.0_r26android-mainline-11.0.0_r25android-mainline-11.0.0_r24android-mainline-11.0.0_r23android-mainline-11.0.0_r22android-mainline-11.0.0_r21android-mainline-11.0.0_r20android-mainline-11.0.0_r2android-mainline-11.0.0_r19android-mainline-11.0.0_r18android-mainline-11.0.0_r17android-mainline-11.0.0_r16android-mainline-11.0.0_r15android-mainline-11.0.0_r14android-mainline-11.0.0_r13android-mainline-11.0.0_r12android-mainline-11.0.0_r10android-mainline-11.0.0_r1android-11.0.0_r48android-11.0.0_r47android-11.0.0_r46android-11.0.0_r45android-11.0.0_r44android-11.0.0_r43android-11.0.0_r42android-11.0.0_r41android-11.0.0_r40android-11.0.0_r39android-11.0.0_r38android-11.0.0_r37android-11.0.0_r36android-11.0.0_r35android-11.0.0_r34android-11.0.0_r33android-11.0.0_r32android-11.0.0_r31android-11.0.0_r30android-11.0.0_r29android-11.0.0_r28android-11.0.0_r27android-11.0.0_r26android-11.0.0_r24android-11.0.0_r23android-11.0.0_r22android-11.0.0_r21android-11.0.0_r20android-11.0.0_r19android-11.0.0_r18android-11.0.0_r16android11-qpr3-s1-releaseandroid11-qpr3-releaseandroid11-qpr2-releaseandroid11-qpr1-s2-releaseandroid11-qpr1-s1-releaseandroid11-qpr1-releaseandroid11-qpr1-d-s1-releaseandroid11-qpr1-d-releaseandroid11-qpr1-c-releaseandroid11-mainline-tethering-releaseandroid11-mainline-sparse-2021-jan-releaseandroid11-mainline-sparse-2020-dec-releaseandroid11-mainline-releaseandroid11-mainline-permission-releaseandroid11-mainline-os-statsd-releaseandroid11-mainline-networkstack-releaseandroid11-mainline-media-swcodec-releaseandroid11-mainline-media-releaseandroid11-mainline-extservices-releaseandroid11-mainline-documentsui-releaseandroid11-mainline-conscrypt-releaseandroid11-mainline-cellbroadcast-releaseandroid11-mainline-captiveportallogin-releaseandroid11-devandroid11-d2-releaseandroid11-d1-b-release
Change-Id: I3f25c7ee034b2e20e37ed941b8eae24eec7043eb
Diffstat (limited to 'deprecated/automation/server/monitor/dashboard.py')
-rw-r--r-- | deprecated/automation/server/monitor/dashboard.py | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/deprecated/automation/server/monitor/dashboard.py b/deprecated/automation/server/monitor/dashboard.py new file mode 100644 index 00000000..f6befed8 --- /dev/null +++ b/deprecated/automation/server/monitor/dashboard.py @@ -0,0 +1,259 @@ +# Copyright 2011 Google Inc. All Rights Reserved. +# + +__author__ = 'kbaclawski@google.com (Krystian Baclawski)' + +from collections import namedtuple +import glob +import gzip +import os.path +import pickle +import time +import xmlrpclib + +from django import forms +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template import Context +from django.views import static + +Link = namedtuple('Link', 'href name') + + +def GetServerConnection(): + return xmlrpclib.Server('http://localhost:8000') + + +def MakeDefaultContext(*args): + context = Context({'links': [ + Link('/job-group', 'Job Groups'), Link('/machine', 'Machines') + ]}) + + for arg in args: + context.update(arg) + + return context + + +class JobInfo(object): + + def __init__(self, job_id): + self._job = pickle.loads(GetServerConnection().GetJob(job_id)) + + def GetAttributes(self): + job = self._job + + group = [Link('/job-group/%d' % job.group.id, job.group.label)] + + predecessors = [Link('/job/%d' % pred.id, pred.label) + for pred in job.predecessors] + + successors = [Link('/job/%d' % succ.id, succ.label) + for succ in job.successors] + + machines = [Link('/machine/%s' % mach.hostname, mach.hostname) + for mach in job.machines] + + logs = [Link('/job/%d/log' % job.id, 'Log')] + + commands = enumerate(job.PrettyFormatCommand().split('\n'), start=1) + + return {'text': [('Label', job.label), ('Directory', job.work_dir)], + 'link': [('Group', group), ('Predecessors', predecessors), + ('Successors', successors), ('Machines', machines), + ('Logs', logs)], + 'code': [('Command', commands)]} + + def GetTimeline(self): + return [{'started': evlog.GetTimeStartedFormatted(), + 'state_from': evlog.event.from_, + 'state_to': evlog.event.to_, + 'elapsed': evlog.GetTimeElapsedRounded()} + for evlog in self._job.timeline.GetTransitionEventHistory()] + + def GetLog(self): + log_path = os.path.join(self._job.logs_dir, + '%s.gz' % self._job.log_filename_prefix) + + try: + log = gzip.open(log_path, 'r') + except IOError: + content = [] + else: + # There's a good chance that file is not closed yet, so EOF handling + # function and CRC calculation will fail, thus we need to monkey patch the + # _read_eof method. + log._read_eof = lambda: None + + def SplitLine(line): + prefix, msg = line.split(': ', 1) + datetime, stream = prefix.rsplit(' ', 1) + + return datetime, stream, msg + + content = map(SplitLine, log.readlines()) + finally: + log.close() + + return content + + +class JobGroupInfo(object): + + def __init__(self, job_group_id): + self._job_group = pickle.loads(GetServerConnection().GetJobGroup( + job_group_id)) + + def GetAttributes(self): + group = self._job_group + + home_dir = [Link('/job-group/%d/files/' % group.id, group.home_dir)] + + return {'text': [('Label', group.label), + ('Time submitted', time.ctime(group.time_submitted)), + ('State', group.status), + ('Cleanup on completion', group.cleanup_on_completion), + ('Cleanup on failure', group.cleanup_on_failure)], + 'link': [('Directory', home_dir)]} + + def _GetJobStatus(self, job): + status_map = {'SUCCEEDED': 'success', 'FAILED': 'failure'} + return status_map.get(str(job.status), None) + + def GetJobList(self): + return [{'id': job.id, + 'label': job.label, + 'state': job.status, + 'status': self._GetJobStatus(job), + 'elapsed': job.timeline.GetTotalTime()} + for job in self._job_group.jobs] + + def GetHomeDirectory(self): + return self._job_group.home_dir + + def GetReportList(self): + job_dir_pattern = os.path.join(self._job_group.home_dir, 'job-*') + + filenames = [] + + for job_dir in glob.glob(job_dir_pattern): + filename = os.path.join(job_dir, 'report.html') + + if os.access(filename, os.F_OK): + filenames.append(filename) + + reports = [] + + for filename in sorted(filenames, key=lambda f: os.stat(f).st_ctime): + try: + with open(filename, 'r') as report: + reports.append(report.read()) + except IOError: + pass + + return reports + + +class JobGroupListInfo(object): + + def __init__(self): + self._all_job_groups = pickle.loads(GetServerConnection().GetAllJobGroups()) + + def _GetJobGroupState(self, group): + return str(group.status) + + def _GetJobGroupStatus(self, group): + status_map = {'SUCCEEDED': 'success', 'FAILED': 'failure'} + return status_map.get(self._GetJobGroupState(group), None) + + def GetList(self): + return [{'id': group.id, + 'label': group.label, + 'submitted': time.ctime(group.time_submitted), + 'state': self._GetJobGroupState(group), + 'status': self._GetJobGroupStatus(group)} + for group in self._all_job_groups] + + def GetLabelList(self): + return sorted(set(group.label for group in self._all_job_groups)) + + +def JobPageHandler(request, job_id): + job = JobInfo(int(job_id)) + + ctx = MakeDefaultContext({ + 'job_id': job_id, + 'attributes': job.GetAttributes(), + 'timeline': job.GetTimeline() + }) + + return render_to_response('job.html', ctx) + + +def LogPageHandler(request, job_id): + job = JobInfo(int(job_id)) + + ctx = MakeDefaultContext({'job_id': job_id, 'log_lines': job.GetLog()}) + + return render_to_response('job_log.html', ctx) + + +def JobGroupPageHandler(request, job_group_id): + group = JobGroupInfo(int(job_group_id)) + + ctx = MakeDefaultContext({ + 'group_id': job_group_id, + 'attributes': group.GetAttributes(), + 'job_list': group.GetJobList(), + 'reports': group.GetReportList() + }) + + return render_to_response('job_group.html', ctx) + + +def JobGroupFilesPageHandler(request, job_group_id, path): + group = JobGroupInfo(int(job_group_id)) + + return static.serve(request, + path, + document_root=group.GetHomeDirectory(), + show_indexes=True) + + +class FilterJobGroupsForm(forms.Form): + label = forms.ChoiceField(label='Filter by label:', required=False) + + +def JobGroupListPageHandler(request): + groups = JobGroupListInfo() + group_list = groups.GetList() + + field = FilterJobGroupsForm.base_fields['label'] + field.choices = [('*', '--- no filtering ---')] + field.choices.extend([(label, label) for label in groups.GetLabelList()]) + + if request.method == 'POST': + form = FilterJobGroupsForm(request.POST) + + if form.is_valid(): + label = form.cleaned_data['label'] + + if label != '*': + group_list = [group for group in group_list if group['label'] == label] + else: + form = FilterJobGroupsForm({'initial': '*'}) + + ctx = MakeDefaultContext({'filter': form, 'groups': group_list}) + + return render_to_response('job_group_list.html', ctx) + + +def MachineListPageHandler(request): + machine_list = pickle.loads(GetServerConnection().GetMachineList()) + + return render_to_response('machine_list.html', + MakeDefaultContext({'machines': machine_list})) + + +def DefaultPageHandler(request): + return HttpResponseRedirect('/job-group') |