aboutsummaryrefslogtreecommitdiff
path: root/deprecated/automation/server/monitor/dashboard.py
diff options
context:
space:
mode:
authorTiancong Wang <tcwang@google.com>2020-02-13 21:08:49 +0000
committerTiancong Wang <tcwang@google.com>2020-02-13 21:08:49 +0000
commitb75f321fc8978b92ce3db6886ccb966768f0c7a8 (patch)
tree35fa0fbaeaaddd9cc2a126a05eee3527b51e83a8 /deprecated/automation/server/monitor/dashboard.py
parentcddd960b0ba2eb62c372c0d3176c75f0bd05d5e8 (diff)
parente617e3393dd24003aa976ece5050bb291070041c (diff)
downloadtoolchain-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.py259
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')