#!/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)