Hotfix pkgdb-sync-bugzilla to automatically send notifications

This commit is contained in:
Pierre-Yves Chibon 2015-03-06 09:30:58 +01:00
parent 3cba755812
commit 2198e5709c

View file

@ -32,9 +32,12 @@ __requires__ = ['SQLAlchemy >= 0.7', 'jinja2 >= 2.4']
import pkg_resources import pkg_resources
import argparse import argparse
import datetime
import time
import sys import sys
import os import os
import itertools import itertools
import json
import xmlrpclib import xmlrpclib
import codecs import codecs
import smtplib import smtplib
@ -70,18 +73,24 @@ NOTIFYEMAIL = pkgdb2.APP.config.get('PKGDB2_BUGZILLA_NOTIFY_EMAIL')
PKGDBSERVER = pkgdb2.APP.config.get('SITE_URL') PKGDBSERVER = pkgdb2.APP.config.get('SITE_URL')
DRY_RUN = pkgdb2.APP.config.get('PKGDB2_BUGZILLA_DRY_RUN', False) DRY_RUN = pkgdb2.APP.config.get('PKGDB2_BUGZILLA_DRY_RUN', False)
EMAIL_FROM = 'accounts@fedoraproject.org'
DATA_CACHE = '/var/tmp/pkgdb_sync_bz.json'
# When querying for current info, take segments of 1000 packages a time # When querying for current info, take segments of 1000 packages a time
BZ_PKG_SEGMENT = 1000 BZ_PKG_SEGMENT = 1000
class DataChangedError(Exception): class DataChangedError(Exception):
'''Raised when data we are manipulating changes while we're modifying it.''' '''Raised when data we are manipulating changes while we're modifying it.'''
pass pass
def segment(iterable, chunk, fill=None): def segment(iterable, chunk, fill=None):
'''Collect data into `chunk` sized block''' '''Collect data into `chunk` sized block'''
args = [iter(iterable)] * chunk args = [iter(iterable)] * chunk
return itertools.izip_longest(*args, fillvalue=fill) return itertools.izip_longest(*args, fillvalue=fill)
class ProductCache(dict): class ProductCache(dict):
def __init__(self, bz, acls): def __init__(self, bz, acls):
self.bz = bz self.bz = bz
@ -284,7 +293,7 @@ class Bugzilla(object):
raise raise
def send_email(fromAddress, toAddress, subject, message): def send_email(fromAddress, toAddress, subject, message, ccAddress=None):
'''Send an email if there's an error. '''Send an email if there's an error.
This will be replaced by sending messages to a log later. This will be replaced by sending messages to a log later.
@ -293,12 +302,70 @@ def send_email(fromAddress, toAddress, subject, message):
msg.add_header('To', ','.join(toAddress)) msg.add_header('To', ','.join(toAddress))
msg.add_header('From', fromAddress) msg.add_header('From', fromAddress)
msg.add_header('Subject', subject) msg.add_header('Subject', subject)
if ccAddress is not None:
msg.add_header('Cc', ','.join(ccAddress))
msg.set_payload(message) msg.set_payload(message)
smtp = smtplib.SMTP('bastion') smtp = smtplib.SMTP('bastion')
smtp.sendmail(fromAddress, toAddress, msg.as_string()) smtp.sendmail(fromAddress, toAddress, msg.as_string())
smtp.quit() smtp.quit()
def notify_users(errors):
''' Browse the list of errors and when we can retrieve the email
address, use it to notify the user about the issue.
'''
tmpl_email = pkgdb2.APP.config.get('PKGDB_SYNC_BUGZILLA_EMAIL', None)
if not tmpl_email:
print 'No template email configured in the configuration file, '\
'no notification sent to the users'
return
data = {}
if os.path.exists(DATA_CACHE):
try:
with open(DATA_CACHE) as stream:
data = json.load(stream)
except Exception as err:
print 'Could not read the json file at %s: \nError: %s' % (
DATA_CACHE, err)
new_data = {}
for error in errors:
notify_user = False
if 'The name ' in error and ' is not a valid username' in error:
user_email = error.split(' is not a valid username')[0].split(
'The name ')[1].strip()
now = datetime.datetime.utcnow()
# See if we already know about this user
if user_email in data and data[user_email]['last_update']:
last_update = datetime.datetime.fromtimestamp(
int(data[user_email]['last_update']))
# Only notify users once per hour
if (now - last_update).seconds >= 3600:
notify_user = True
else:
new_data[user_email] = data[user_email]
elif not data or user_email not in data:
notify_user = True
if notify_user:
send_email(
EMAIL_FROM,
[user_email],
subject='Please fix your bugzilla.redhat.com account',
message=tmpl_email,
ccAddress=NOTIFYEMAIL,
)
new_data[user_email] = {
'last_update': time.mktime(now.timetuple())
}
with open(DATA_CACHE, 'w') as stream:
json.dump(new_data, stream)
if __name__ == '__main__': if __name__ == '__main__':
sys.stdout = codecs.getwriter('utf-8')(sys.stdout) sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
@ -328,7 +395,7 @@ if __name__ == '__main__':
for product in acls.keys(): for product in acls.keys():
if product not in ('Fedora', 'Fedora EPEL'): if product not in ('Fedora', 'Fedora EPEL'):
continue continue
for pkg in acls[product]: for pkg in sorted(acls[product]):
if DRY_RUN: if DRY_RUN:
print pkg print pkg
pkgInfo = acls[product][pkg] pkgInfo = acls[product][pkg]
@ -355,12 +422,16 @@ if __name__ == '__main__':
except xmlrpclib.Error, e: except xmlrpclib.Error, e:
# An error occurred in the xmlrpc call. Shouldn't happen but # An error occurred in the xmlrpc call. Shouldn't happen but
# we better see what it is # we better see what it is
errors.append(str(e.args)) errors.append('%s -- %s' % (pkg, e.args[-1]))
# Send notification of errors # Send notification of errors
if errors: if errors:
#print '[DEBUG]', '\n'.join(errors) if DRY_RUN:
send_email('accounts@fedoraproject.org', print '[DEBUG]', '\n'.join(errors)
else:
notify_users(errors)
send_email(
EMAIL_FROM,
NOTIFYEMAIL, NOTIFYEMAIL,
'Errors while syncing bugzilla with the PackageDB', 'Errors while syncing bugzilla with the PackageDB',
''' '''