diff --git a/roles/validate-tempest/files/tempestmail/README.md b/roles/validate-tempest/files/tempestmail/README.md index 533fa4c86..00397dec5 100644 --- a/roles/validate-tempest/files/tempestmail/README.md +++ b/roles/validate-tempest/files/tempestmail/README.md @@ -96,6 +96,26 @@ gate-tripleo-ci-centos-7-ovb-ha-oooq or gate-tripleo-ci-centos-7-ovb-containers-oooq has a test failure that matches the regex. +```yaml +... +emails: + - mail: fail1@example.com + regex: '.*foo.*' + topics: foo1 + - mail: fail1@example.com + regex: '.*bar.*' + topics: bar1 + - mail: fail2@example.com + regex: '.*bar.*' + topics: bar2,extra +... +``` + +if a jobs contains tests matching both 'foo' and 'bar', then: +* fail1@ will receive an email '[foo1]...' and an email '[bar1]...' +* fail2@ will receive an email '[bar2][extra]...' + + So, the order is: 1. If there's no jobs list the user will receive all the emails. diff --git a/roles/validate-tempest/files/tempestmail/tempestmail.py b/roles/validate-tempest/files/tempestmail/tempestmail.py index 19aad885b..eb306c8ab 100755 --- a/roles/validate-tempest/files/tempestmail/tempestmail.py +++ b/roles/validate-tempest/files/tempestmail/tempestmail.py @@ -108,7 +108,7 @@ class Mail(object): def filter_emails(self, job, data): has_errors = False - addresses = [] + bookaddr = {} for error in [data.get(x, []) for x in ('new', 'failed', 'errors')]: if error: @@ -123,24 +123,33 @@ class Mail(object): m.get('jobs') or not m.get('jobs')] - # Now we filter for regex if doesn't exists - addresses = [m.get('mail') for m in emails if not m.get('regex')] - - # And finally, if regex exists + # Add all addresses except those that regex don't match for email in emails: - for r in email.get('regex'): - if len(filter(r.search, data.get('new'))): - addresses.append(email.get('mail')) - break - + add = True + if email.get('regex'): + for r in email.get('regex'): + if len(filter(r.search, data.get('new'))): + break + add = False + if add: + topics = '' + if email.get('topics'): + # Parse topics and format it between brackets + t = email.get('topics').split(',') + topics = ''.join('[{}]'.format(s) for s in t) + # Add the address to the bookaddr dict + # {'[foo][bar]' : ['john@redhat.com', 'mary@redhat.com']} + bookaddr.setdefault(topics, []).append(email.get('mail')) else: self.log.debug('No failures send email to everybody') addresses = [m.get('mail') for m in self.config.emails if not m.get('fail_only')] + # Single group with empty topic is added to the bookaddr + bookaddr.setdefault('', []).append(addresses) data['has_errors'] = has_errors - return addresses + return bookaddr def _send_mail_local(self, addresses, message, subject, output): msg = MIMEText(message, 'html') @@ -167,14 +176,16 @@ class Mail(object): requests.post(self.config.api_server, data=data) def send_mail(self, job, data, output): - addresses = self.filter_emails(job, data) + bookaddr = self.filter_emails(job, data) message = self.render_template(data) - subject = 'Job {} results'.format(job) - if self.config.use_api_server: - self._send_mail_api(addresses, message, subject) - else: - self._send_mail_local(addresses, message, subject, output) + # Send a separate email to the addresses grouped by topics + for topics, addresses in bookaddr.items(): + subject = '{} Job {} results'.format(topics, job).lstrip() + if self.config.use_api_server: + self._send_mail_api(addresses, message, subject) + else: + self._send_mail_local(addresses, message, subject, output) class TempestMailCmd(object): @@ -367,6 +378,7 @@ class TempestMailCmd(object): 'mail': e.get('mail'), 'jobs': e.get('jobs', []), 'regex': regex, + 'topics': e.get('topics'), 'fail_only': e.get('fail_only', False)}) for t in config.get('known_failures', []): known_failures.append({'test': t.get('test'), diff --git a/roles/validate-tempest/files/tempestmail/tests/test_tempestmail.py b/roles/validate-tempest/files/tempestmail/tests/test_tempestmail.py index 402499f61..b99e0f7e2 100644 --- a/roles/validate-tempest/files/tempestmail/tests/test_tempestmail.py +++ b/roles/validate-tempest/files/tempestmail/tests/test_tempestmail.py @@ -2,6 +2,7 @@ import datetime import mock import tempfile import unittest +import re from tempestmail import Config from tempestmail import Mail @@ -29,9 +30,9 @@ class MailTest(unittest.TestCase): config.require_auth = True config.emails = [ {'mail': 'email1@example.com', 'name': 'name 1', - 'jobs': [], 'regex': []}, + 'jobs': [], 'regex': [], 'topics': ''}, {'mail': 'email2@example.com', 'name': 'name 2', - 'jobs': [], 'regex': []} + 'jobs': [], 'regex': [], 'topics': ''} ] config.template = 'template.html' return config @@ -87,16 +88,45 @@ class MailTest(unittest.TestCase): self.assertEquals(self.data.get('has_errors'), None) addresses = mail.filter_emails( 'periodic-tripleo-ci-centos-7-ovb-ha-tempest', self.data) - self.assertEquals(['email1@example.com', 'email2@example.com'], + self.assertEquals({'' : ['email1@example.com', 'email2@example.com']}, addresses) mail.config.emails[0]['jobs'].append('another-job') addresses = mail.filter_emails( 'periodic-tripleo-ci-centos-7-ovb-ha-tempest', self.data) - self.assertEquals(['email2@example.com'], addresses) + self.assertEquals({'' : ['email2@example.com']}, addresses) self.assertEquals(self.data['has_errors'], True) mail.config.emails[0]['jobs'] = [] - mail.config.emails[0]['regex'].append('tempest.some.regex') - self.assertEquals(['email2@example.com'], addresses) + mail.config.emails[0]['regex'].append(re.compile( + 'tempest.some.regex')) + self.assertEquals({'' : ['email2@example.com']}, addresses) + + def test_filter_emails_topics(self): + mail = Mail(self.config) + addresses = mail.filter_emails( + 'periodic-tripleo-ci-centos-7-ovb-ha-tempest', self.data) + self.assertEquals({'' : ['email1@example.com', + 'email2@example.com']}, + addresses) + mail.config.emails[0]['jobs'].append( + 'periodic-tripleo-ci-centos-7-ovb-ha-tempest') + mail.config.emails[0]['regex'].append(re.compile( + 'upload_too_many_objects')) + mail.config.emails[0]['topics'] = 'many_objects' + mail.config.emails[1]['regex'].append(re.compile( + 'upload_valid_object')) + mail.config.emails[1]['topics'] = 'valid_object' + new = {'mail': 'email2@example.com', 'name': 'name 2', + 'jobs': ['periodic-tripleo-ci-centos-7-ovb-ha-tempest'], + 'regex': [re.compile('upload_valid_object')], + 'topics': 'valid_object,object_storage'} + mail.config.emails.append(new) + addresses = mail.filter_emails( + 'periodic-tripleo-ci-centos-7-ovb-ha-tempest', self.data) + bookaddr = {'[many_objects]' : ['email1@example.com'], + '[valid_object]' : ['email2@example.com'], + '[valid_object][object_storage]' : ['email2@example.com'] + } + self.assertEquals(bookaddr, addresses) @mock.patch('tempestmail.Mail._send_mail_api') @mock.patch('tempestmail.Mail._send_mail_local')