#!/usr/bin/env python ''' Audit changes to Perforce groups. This should be used as a form-save trigger: audit-grps form-save group "python audit_groups.py -p perforce:1666 -u P4USER -g group0 -g group1 -L /p4/audit.groups -S smtp.example.org:25 -e p4admins@example.org -a p4admins@example.org %formname% %formfile% %user%" $Author: lester_cheung $ $Id: //guest/lester_cheung/p4util/p4util/trigger/form/audit_groups_26.py#1 $ ''' __version__ = '0.{0}'.format('$Change: 10926 $'.split()[-2]) import difflib import functools import logging import os.path import shlex import smtplib import sys from P4 import P4 from email.mime.text import MIMEText from optparse import OptionParser from pprint import pprint, pformat def parse_args(): usage = 'usage: %prog [options] %formname% %formfile% %user%' op = OptionParser(usage=usage) p4 = P4() op.add_option('-p', '--port', metavar=p4.env('P4PORT'), default=p4.env('P4PORT')) op.add_option('-u', '--user', metavar=p4.env('P4USER'), default=p4.env('P4USER')) op.add_option('-c', '--client', metavar=p4.env('P4CLIENT'), default=p4.env('P4CLIENT')) op.add_option('-C', '--charset', metavar=p4.env('P4CHARSET'), default=p4.env('P4CHARSET') or 'none') op.add_option('-g', '--group', action='append', dest='groups', default=[], help='Group to audit. Repeat this options to specify more than one group.') op.add_option('-L', '--log', default='audit.group') op.add_option('-S', '--smtp-server', help='e.g. smtp.example.org:25') op.add_option('-e', '--email', action='append', dest='emails', help='Change notifications will be sent to this email. ' 'Repeat this option to add multiple recipients') op.add_option('-a', '--admin-email', default='p4admin@example.org', metavar='p4admin@example.org') return op.parse_args() def sendemail(options, msg): cfg, args = options formname, formfile, user = args mail = MIMEText(msg) mail['From'] = cfg.admin_email mail['To'] = ', '.join(cfg.emails) mail['Subject'] = '[{0}] Group "{1}" updated by {2}'.format(cfg.port, formname, user) # print(mail.as_string()) smtp = smtplib.SMTP( *(cfg.smtp_server.split(':')) ) smtp.sendmail(from_addr=cfg.admin_email, to_addrs=cfg.emails, msg=mail.as_string()) def main(options): cfg, args = options formname, formfile, user = args if cfg.groups and formname not in cfg.groups: sys.exit(0) # short-circuit and exit early p4 = P4() p4.prog = __file__ if cfg.user: p4.user = cfg.user if cfg.port: p4.port = cfg.port if cfg.client: p4.client = cfg.client if cfg.charset: p4.charset = cfg.charset p4.connect() # grp0 = p4.run_plaintext( *shlex.split('group -o {0}'.format(formname)) ).splitlines() grp0 = p4.run('group', '-o', formname, tagged=False)[0].splitlines() p4.disconnect() with open(formfile) as fd: grp1 = fd.read().splitlines() differ = difflib.Differ() diff = list(differ.compare(grp0, grp1)) if not functools.reduce(lambda x, y: x or y, [ x[0] in '-+?' for x in filter(lambda x: x.strip() != '', diff) ] ): # Exit if there is no diff markers in front of all lines sys.exit(0) msg = 'Group {0} updated by {1}:\n'.format(formname, user) + '\n'.join(diff) log.info(msg) if cfg.emails: try: sendemail(options, '\n'.join(diff)) except Exception as e: import io, traceback sbuf = io.StringIO() traceback.print_exc(file=sbuf) log.error('Error sending notification email.' + sbuf.getvalue()) if __name__ == '__main__': opts, args = parse_args() logging.basicConfig(format='%(asctime)-15s %(funcName)s.%(levelname)s %(message)s', filename=opts.log, level=logging.DEBUG) log = logging.getLogger(__name__) log.debug((opts, args)) main((opts, args)) # making sure that we exit with zero return so the group mod # gets committed sys.exit(0)