123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- #!/usr/bin/python
- '''CONFIG_JSON is a json encoded string base64 environment variable. It is used
- to clone docker-ci database, generate docker-ci report and submit it by email.
- CONFIG_JSON data comes from the file /report/credentials.json inserted in this
- container by deployment.py:
- { "DOCKER_CI_PUB": "$(cat docker-ci_ssh_public_key.pub)",
- "DOCKER_CI_KEY": "$(cat docker-ci_ssh_private_key.key)",
- "DOCKER_CI_ADDRESS": "user@docker-ci_fqdn_server",
- "SMTP_USER": "SMTP_server_user",
- "SMTP_PWD": "SMTP_server_password",
- "EMAIL_SENDER": "Buildbot_mailing_sender",
- "EMAIL_RCP": "Buildbot_mailing_receipient" } '''
- import os, re, json, sqlite3, datetime, base64
- import smtplib
- from datetime import timedelta
- from subprocess import call
- from os import environ as env
- TODAY = datetime.date.today()
- # Load credentials to the environment
- env['CONFIG_JSON'] = base64.b64decode(open('/report/credentials.json').read())
- # Remove SSH private key as it needs more processing
- CONFIG = json.loads(re.sub(r'("DOCKER_CI_KEY".+?"(.+?)",)','',
- env['CONFIG_JSON'], flags=re.DOTALL))
- # Populate environment variables
- for key in CONFIG:
- env[key] = CONFIG[key]
- # Load SSH private key
- env['DOCKER_CI_KEY'] = re.sub('^.+"DOCKER_CI_KEY".+?"(.+?)".+','\\1',
- env['CONFIG_JSON'],flags=re.DOTALL)
- # Prevent rsync to validate host on first connection to docker-ci
- os.makedirs('/root/.ssh')
- open('/root/.ssh/id_rsa','w').write(env['DOCKER_CI_KEY'])
- os.chmod('/root/.ssh/id_rsa',0600)
- open('/root/.ssh/config','w').write('StrictHostKeyChecking no\n')
- # Sync buildbot database from docker-ci
- call('rsync {}:/data/buildbot/master/state.sqlite .'.format(
- env['DOCKER_CI_ADDRESS']), shell=True)
- class SQL:
- def __init__(self, database_name):
- sql = sqlite3.connect(database_name)
- # Use column names as keys for fetchall rows
- sql.row_factory = sqlite3.Row
- sql = sql.cursor()
- self.sql = sql
- def query(self,query_statement):
- return self.sql.execute(query_statement).fetchall()
- sql = SQL("state.sqlite")
- class Report():
- def __init__(self,period='',date=''):
- self.data = []
- self.period = 'date' if not period else period
- self.date = str(TODAY) if not date else date
- self.compute()
- def compute(self):
- '''Compute report'''
- if self.period == 'week':
- self.week_report(self.date)
- else:
- self.date_report(self.date)
- def date_report(self,date):
- '''Create a date test report'''
- builds = []
- # Get a queryset with all builds from date
- rows = sql.query('SELECT * FROM builds JOIN buildrequests'
- ' WHERE builds.brid=buildrequests.id and'
- ' date(start_time, "unixepoch", "localtime") = "{0}"'
- ' GROUP BY number'.format(date))
- build_names = sorted(set([row['buildername'] for row in rows]))
- # Create a report build line for a given build
- for build_name in build_names:
- tried = len([row['buildername']
- for row in rows if row['buildername'] == build_name])
- fail_tests = [row['buildername'] for row in rows if (
- row['buildername'] == build_name and row['results'] != 0)]
- fail = len(fail_tests)
- fail_details = ''
- fail_pct = int(100.0*fail/tried) if tried != 0 else 100
- builds.append({'name': build_name, 'tried': tried, 'fail': fail,
- 'fail_pct': fail_pct, 'fail_details':fail_details})
- if builds:
- self.data.append({'date': date, 'builds': builds})
- def week_report(self,date):
- '''Add the week's date test reports to report.data'''
- date = datetime.datetime.strptime(date,'%Y-%m-%d').date()
- last_monday = date - datetime.timedelta(days=date.weekday())
- week_dates = [last_monday + timedelta(days=x) for x in range(7,-1,-1)]
- for date in week_dates:
- self.date_report(str(date))
- def render_text(self):
- '''Return rendered report in text format'''
- retval = ''
- fail_tests = {}
- for builds in self.data:
- retval += 'Test date: {0}\n'.format(builds['date'],retval)
- table = ''
- for build in builds['builds']:
- table += ('Build {name:15} Tried: {tried:4} '
- ' Failures: {fail:4} ({fail_pct}%)\n'.format(**build))
- if build['name'] in fail_tests:
- fail_tests[build['name']] += build['fail_details']
- else:
- fail_tests[build['name']] = build['fail_details']
- retval += '{0}\n'.format(table)
- retval += '\n Builds failing'
- for fail_name in fail_tests:
- retval += '\n' + fail_name + '\n'
- for (fail_id,fail_url,rn_tests,nr_errors,log_errors,
- tracelog_errors) in fail_tests[fail_name]:
- retval += fail_url + '\n'
- retval += '\n\n'
- return retval
- # Send email
- smtp_from = env['EMAIL_SENDER']
- subject = '[docker-ci] Daily report for {}'.format(str(TODAY))
- msg = "From: {}\r\nTo: {}\r\nSubject: {}\r\n\r\n".format(
- smtp_from, env['EMAIL_RCP'], subject)
- msg = msg + Report('week').render_text()
- server = smtplib.SMTP_SSL('smtp.mailgun.org')
- server.login(env['SMTP_USER'], env['SMTP_PWD'])
- server.sendmail(smtp_from, env['EMAIL_RCP'], msg)
|