#!/usr/bin/env python #%# family=manual #%# capabilities= """ Munin plugin that monitors the greyfix triplet database. Author: Tom Hendrikx 2011-07-24 This plugin monitors the Greyfix greylisting tool for Postfix. It creates a nice overview of the number of triplets in the greyfix database, dividing the database population in configurable ages. For more information about greyfix: http://trac.kim-minh.com/greyfix/ Installation ============ To install, create a symlink to the plugin in the munin plugin directory: $ ln -s /path/to/plugin/greyfix /etc/munin/plugins/greyfix Configuration ============= There are some settings that can be tweaked by adding statements to the munin-node config: [greyfix] # run plugin as the same user as postfix does user nobody # path to greyfix binary (default: /usr/sbin/greyfix) env.greyfix /usr/sbin/greyfix # the length of each graph step in days (default: 7) env.step_size 7 # the number of steps to graph (default: 11) env.num_steps 11 Please note that the last step has no end date, so it includes all triplets older than the second last step. I.e., the defaults (as shown above) create a graph that shows 10 steps of one week each, and one last step for everything older than 10 weeks. """ # Settings: all of these can be redefined in the munin-node config settings = { 'greyfix': '/usr/sbin/greyfix', 'step_size': 7, 'num_steps': 11 } import os import sys import subprocess import datetime def greyfix_parse_triplets(): """Parse output of greyfix --dump-triplets into something that we can use. The output of the command has the following columns: sender_ip, sender_email, recipient_email, timestamp_first_seen, timestamp_last_seen, block_count, pass_count Also see http://groups.google.com/group/greyfix/browse_thread/thread/510687a9ed94fc2c""" greyfix = subprocess.Popen(args=[ settings['greyfix'], '--dump-triplets'], stdout=subprocess.PIPE) stdout = greyfix.communicate()[0] if greyfix.returncode > 0: print '# greyfix exited with exit code %i' % (greyfix.returncode) sys.exit(greyfix.returncode) triplets = [] for line in stdout.split("\n"): triplet = line.split("\t") if len(triplet) == 7: triplet[3] = datetime.datetime.strptime(triplet[3], '%c') triplet[4] = datetime.datetime.strptime(triplet[4], '%c') triplet[5] = int(triplet[5]) triplet[6] = int(triplet[6]) triplets.append(triplet) return triplets def convert_step_to_days(step): """Compute the days that are contained in a step, according to the configuration""" start = settings['step_size'] * step end = (settings['step_size'] * (step + 1)) - 1 if step >= (settings['num_steps'] -1): return (start, '') else: return (start, end) def print_fetch(): """Generates and prints the values as retrieved from greyfix.""" triplets = greyfix_parse_triplets() steps = [0 for i in range(0, settings['num_steps'])] now = datetime.datetime.now() for triplet in triplets: delta = now - triplet[3] step = delta.days / settings['step_size'] step = min(step, settings['num_steps'] -1) # count the number of triplets in a group steps[ step ] = steps[ step ] +1 # print result counts for each group for step, count in enumerate(steps): fieldname = 'd%s_%s' % convert_step_to_days(step) print '%s.value %i' % (fieldname, count) def print_config(): """Generates and prints a munin config for a given chart.""" print 'graph_title Greyfix triplets by age' print 'graph_vlabel Number of triplets' print 'graph_info The amount of triplets in the greyfix database with a certain age' print 'graph_category postfix' print 'graph_total All triplets' steps = range(0, settings['num_steps']) for step in steps: days = convert_step_to_days(step) fieldname = 'd%s_%s' % days label = '%s - %s days' % (days[0], days[1] if days[1] != '' else '...') info = 'The number of triplets that is between %s and %s days old' % (days[0], days[1] if days[1] != '' else '...') print '%s.label %s' % (fieldname, label) print '%s.info %s' % (fieldname, info) print '%s.draw AREASTACK' % fieldname if __name__ == '__main__': # read settings from config file / environment for key in settings.iterkeys(): if key in os.environ: if os.environ[ key ].isdigit(): settings[ key ] = int(os.environ[ key ]) else: settings[ key ] = os.environ[ key ] #print '# setting %s updated to: %s' % (key, settings[ key ]) commands = ['fetch', 'config'] if len(sys.argv) > 1: command = sys.argv[1] else: command = commands[0] if command not in commands: print '# command %s unknown, choose one of: %s' % (command, commands) sys.exit(1) if command == 'fetch': print_fetch() elif command == 'config': print_config()