third-party-ci-tools/monitoring/lastcomment-scoreboard/cireporter.py

265 lines
9.9 KiB
Python
Executable File

#!/usr/bin/env python
"""Generate a report on CI comments and jobs."""
import argparse
import calendar
import datetime
import json
import yaml
import requests
import comment
def query_gerrit(name, count, project, quiet=False):
"""Query gerrit and fetch the comments."""
# Include review messages in query
search = "reviewer:\"%s\"" % name
if project:
search = search + (" AND project:\"%s\"" % project)
query = ("https://review.openstack.org/changes/?q=%s&"
"o=MESSAGES&o=DETAILED_ACCOUNTS" % search)
r = requests.get(query)
try:
changes = json.loads(r.text[4:])
except ValueError:
if not quiet:
print("query: '%s' failed with:\n%s" % (query, r.text))
return []
comments = []
for change in changes:
for date, message in comment.get_comments(change, name):
if date is None:
# no comments from reviewer yet. This can happen since
# 'Uploaded patch set X.' is considered a comment.
continue
comments.append(comment.Comment(date, change['_number'],
change['subject'], message))
return sorted(comments, key=lambda comment: comment.date,
reverse=True)[0:count]
def get_votes(comments):
"""Get the stats for all of the jobs in all comments."""
last_success = None
votes = {'success': 0, 'failure': 0}
for cmt in comments:
if cmt.jobs:
for job in cmt.jobs:
if job.result == "SUCCESS":
if job.name not in votes:
votes[job.name] = {'success': 1, 'failure': 0,
'last_success': cmt}
elif votes[job.name]['success'] == 0:
votes[job.name]['success'] += 1
votes[job.name]['last_success'] = cmt
else:
votes[job.name]['success'] += 1
votes['success'] += 1
if not last_success:
last_success = cmt
elif job.result == 'FAILURE':
if job.name not in votes:
votes[job.name] = {'success': 0, 'failure': 1,
'last_success': None}
else:
votes[job.name]['failure'] += 1
votes['failure'] += 1
else:
# We got something other than
# SUCCESS or FAILURE
# for now, mark it as a failure
if job.name not in votes:
votes[job.name] = {'success': 0, 'failure': 1,
'last_success': None}
else:
votes[job.name]['failure'] += 1
votes['failure'] += 1
#print("Job %(name)s result = %(result)s" %
# {'name': job.name,
# 'result': job.result})
return votes, last_success
def generate_report(name, count, project, quiet=False):
"""Process all of the comments and generate the stats."""
result = {'name': name, 'project': project}
last_success = None
comments = query_gerrit(name, count, project)
if not comments:
print("No comments found. CI SYSTEM UNKNOWN")
return
votes, last_success = get_votes(comments)
result['last_seen'] = {'date': epoch(comments[0].date),
'age': str(comments[0].age()),
'url': comments[0].url()}
last = len(comments) - 1
result['first_seen'] = {'date': epoch(comments[last].date),
'age': str(comments[last].age()),
'url': comments[last].url()}
if not quiet:
print(" first seen: %s (%s old) %s" % (comments[last].date,
comments[last].age(),
comments[last].url()))
print(" last seen: %s (%s old) %s" % (comments[0].date,
comments[0].age(),
comments[0].url()))
if last_success:
result['last_success'] = {'date': epoch(comments[0].date),
'age': str(comments[0].age()),
'url': comments[0].url()}
if not quiet:
print(" last success: %s (%s old) %s" % (last_success.date,
last_success.age(),
last_success.url()))
else:
result['last_success'] = None
if not quiet:
print(" last success: None")
result['jobs'] = []
jobs = dict.fromkeys(votes, 0)
jobs.pop('success', None)
jobs.pop('failure', None)
for job in jobs:
reported_comments = votes[job]['success'] + votes[job]['failure']
if votes[job]['failure'] == 0:
success_rate = 100
else:
success_rate = int(votes[job]['success'] /
float(reported_comments) * 100)
if not quiet:
print(" Job %(job_name)s %(success_rate)s%% success out of "
"%(comments)s comments S=%(success)s, F=%(failures)s"
% {'success_rate': success_rate,
'job_name': job,
'comments': reported_comments,
'success': votes[job]['success'],
'failures': votes[job]['failure']})
# Only print the job's last success rate if the succes rate
# is low enough to warrant showing it.
if votes[job]['last_success'] and success_rate <= 60 and not quiet:
print(" last success: %s (%s old) %s" %
(votes[job]['last_success'].date,
votes[job]['last_success'].age(),
votes[job]['last_success'].url()))
job_entry = {'name': job, 'success_rate': success_rate,
'num_success': votes[job]['success'],
'num_failures': votes[job]['failure'],
'comments': reported_comments,
}
if votes[job]['last_success']:
job_entry['last_success'] = {
'date': epoch(votes[job]['last_success'].date),
'age': str(votes[job]['last_success'].age()),
'url': votes[job]['last_success'].url()}
else:
job_entry['last_success'] = None
result['jobs'].append(job_entry)
total = votes['success'] + votes['failure']
if total > 0:
success_rate = int(votes['success'] / float(total) * 100)
result['success_rate'] = success_rate
if not quiet:
print("Overall success rate: %s%% of %s comments" %
(success_rate, len(comments)))
return result
def epoch(timestamp):
return int(calendar.timegm(timestamp.timetuple()))
def main():
parser = argparse.ArgumentParser(description='list most recent comment by '
'reviewer')
parser.add_argument('-n', '--name',
default="Jenkins",
help='unique gerrit name of the reviewer')
parser.add_argument('-c', '--count',
default=10,
type=int,
help='number of records to evaluate')
parser.add_argument('-i', '--input',
default=None,
help='yaml file containing list of names to search on'
'project: name'
' (overwrites -p and -n)')
parser.add_argument('-o', '--output',
default=None,
help='write the output to a file. Defaults to stdout.')
parser.add_argument('-j', '--json',
default=False,
action="store_true",
help=("generate report output in json format."))
parser.add_argument('-p', '--project',
help='only list hits for a specific project '
'(ie: openstack/cinder)')
args = parser.parse_args()
names = {args.project: [args.name]}
quiet = False
if args.json and not args.output:
# if we are writing json data to stdout
# we shouldn't be outputting anything else
quiet = True
if args.input:
with open(args.input) as f:
names = yaml.load(f)
if args.json:
if not quiet:
print "generating report %s" % args.json
print "report is over last %s comments" % args.count
report = {}
timestamp = epoch(datetime.datetime.utcnow())
report['timestamp'] = timestamp
report['rows'] = []
for project in names:
if not quiet:
print 'Checking project: %s' % project
for name in names[project]:
if name != 'Jenkins':
url = ("https://wiki.openstack.org/wiki/ThirdPartySystems/%s" %
name.replace(" ", "_"))
if not quiet:
print 'Checking name: %s - %s' % (name, url)
else:
if not quiet:
print('Checking name: %s' % name)
try:
report_result = generate_report(name, args.count, project,
quiet)
if args.json:
report['rows'].append(report_result)
except Exception as e:
print e
pass
if args.json:
if not args.output:
print(json.dumps(report))
else:
with open(args.output, 'w') as f:
json.dump(report, f)
if __name__ == "__main__":
main()