# HG changeset patch # User Tero Marttila # Date 1357325472 -7200 # Node ID cbdd49b76f1608a2194b8e6cedb964c3a769043f # Parent 9c82a068f8f9ffae20495eb257c4d0b42f954a13 pvl.syslog.rule: refactor SyslogRule diff -r 9c82a068f8f9 -r cbdd49b76f16 bin/pvl.verkko-syslog --- a/bin/pvl.verkko-syslog Fri Jan 04 20:50:31 2013 +0200 +++ b/bin/pvl.verkko-syslog Fri Jan 04 20:51:12 2013 +0200 @@ -68,23 +68,18 @@ log.info("Process syslog messages...") for item in syslog : - if rules : - match = rules.apply(item) - else : - match = None, item['msg'] + match = rules.apply(item) if not match : continue - tag, line = match - - log.info("%s", line) + log.info("%s", match) # TODO: map tag -> target? if target : - target(line) + target(match) else : - print tag, line + print match # done log.info("Exiting...") diff -r 9c82a068f8f9 -r cbdd49b76f16 etc/syslog.conf --- a/etc/syslog.conf Fri Jan 04 20:50:31 2013 +0200 +++ b/etc/syslog.conf Fri Jan 04 20:51:12 2013 +0200 @@ -1,17 +1,18 @@ +format = {host} {msg} + +[sudo] +program = sudo +pattern = ^\s*(?P\S+) : TTY=(?P\S+) ; PWD=(?P.+?) ; USER=(?P\S+) ; (?:ENV=(?P.+?) ; )?COMMAND=(?P.*) + +format = {login}:{tty} - {user}@{host}:{pwd} - {command!r} + +#pattern = \s*(?P\S+) : TTY=(?P\S+)\s; PWD=(?P.+?)\s; USER=(?P\S+)\s;( COMMAND=(?P.*) + [puppet_readshadow] program = sudo pattern = \s*(?Ppuppet) : TTY=(?P\S+)\s; PWD=(?P.+?)\s; USER=(?Proot)\s; COMMAND=(?P/usr/bin/getent shadow \w+) format = -[sudo] -program = sudo -pattern = \s*(?P\S+) : TTY=(?P\S+)\s; PWD=(?P.+?)\s; USER=(?P\S+)\s; COMMAND=(?P.*) -format = {login}:{tty} - {user}@{host}:{pwd} - {command!r} - -[sudo_env] -program = sudo -pattern = \s*(?P\S+) : TTY=(?P\S+) ; PWD=(?P.+?) ; USER=(?P\S+) ; ENV=(?P.+?) ; COMMAND=(?P.*) -format = {login}:{tty} - {user}@{host}:{pwd} - {env} {command!r} [ssh] program = sshd @@ -24,11 +25,6 @@ [su_nobody] program = su pattern = Successful su for nobody by root|\+ \?\?\? root:nobody -#flags = re.IGNORECASE [puppet] program = puppet -format = {host} {msg} - -[all] -format = {host} {msg} diff -r 9c82a068f8f9 -r cbdd49b76f16 pvl/syslog/rule.py --- a/pvl/syslog/rule.py Fri Jan 04 20:50:31 2013 +0200 +++ b/pvl/syslog/rule.py Fri Jan 04 20:51:12 2013 +0200 @@ -3,12 +3,10 @@ import re import optparse, sys +import configobj import logging; log = logging.getLogger('pvl.syslog.rule') -# XXX: ConfigParser kinda sucks -import ConfigParser - def parser (parser) : """ Optparse option group. @@ -27,58 +25,114 @@ """ if options.syslog_rules : - return SyslogRules.load(open(options.syslog_rules)) + return SyslogRule.load(open(options.syslog_rules)) else : - return None + return SyslogRule('default') # TODO: combine SyslogRule/Rules into one heirarchial SyslogRule -type? class SyslogRule (object) : """ - A rule matches syslog lines, and formats them. - - tag - apply given tag to matches + A named SyslogFilter with sub-rules. """ - def __init__ (self, tag, program=None, pattern=None, format=None, flags=0) : - log.debug("%s: %s", tag, pattern) - - if pattern : - pattern = re.compile(pattern, flags) + @classmethod + def load (cls, file) : + """ + Load SyslogRule from file. + """ - self.filter = SyslogFilter(prog=program) + config = configobj.ConfigObj(file) + + rules = [SyslogRule.config(section, **dict(config[section])) for section in config.sections] - self.tag = tag - self.format = format - self.pattern = pattern + return cls(file.name, rules) + @classmethod + def config (cls, name, format=None, program=None, pattern=None, **filters) : + """ + Build SyslogRule from config options + """ + + if pattern : + pattern = re.compile(pattern) + + # XXX: rules/sub-sections support? + rules = () + + filters = dict( + (cls.ATTRS.get(attr, attr), re.compile(regex)) for attr, regex in filters.iteritems() + ) + + filter = SyslogFilter(prog=program, msg=pattern, **filters) + + return cls(name, rules, filter) + + def __init__ (self, name, rules=None, filter=None) : + log.debug("%s: %s", name, filter) + + self.name = name + self.rules = rules or [] # sub-rules + self.filter = filter # SyslogFilter + + def match (self, item) : + """ + Match item against our filter, applying any matches. + """ + + if self.filter : + # filter + matches = self.filter.filter(item) + + if not matches : + # ignore + return None + else : + # match all + matches = True + + # apply + item['rule'] = self + + if matches is True : + # no-op filter + pass + else : + log.debug("%s: %s", self, matches) + item.update(matches) + + # XXX: copy, not mutate? + return item + + # TODO: __call__? def apply (self, item) : """ - Apply rule against given item. - - Returns - None - skip - False - drop - (tag, line) - output + Match item against ourselfs, apply against any sub-rules, and return XXX """ - - # filter - match = self.filter.filter(item) - if not match : - # ignore - return None - - if self.pattern : - match = self.pattern.match(item['msg']) + item = self.match(item) - if not match : - # ignore - return None + if not item : + return + + # sub-rules + for rule in self.rules : + try : + match = rule.apply(item) + + except Exception as ex : + log.exception("rule %s: %r", rule, item) + return # XXX: skip? + + if match is None : + continue + else : + item = match - # apply - item.update(match.groupdict()) - + log.debug("%s: %s", self, item) + + # TODO: routing/formatting + """ if self.tag is False : # drop return False @@ -86,54 +140,11 @@ if self.format : # return return self.tag, self.format.format(**item) + """ - def __str__ (self) : - return self.tag - -class SyslogRules (object) : - """ - Apply a set of rules against lines. - """ - - @classmethod - def load (cls, file) : - """ - Load rules from file. - """ - config = ConfigParser.RawConfigParser() - config.readfp(file) - - # XXX: ordered in python2.7, unordered in python2.6 ! - rules = [SyslogRule(section, **dict(config.items(section))) for section in config.sections()] - - return cls(rules) + return item - def __init__ (self, rules) : - self.rules = rules - - def apply (self, item) : - """ - Apply item against our rules, returning the first match (False/tag-line). - """ - - for rule in self.rules : - try : - match = rule.apply(item) - - except Exception as ex : - log.exception("rule %s: %r", rule, item) - continue - - log.debug("%s: %s", rule, match) - - if match is None : - continue - else : - break - - return match - - def process (self, items) : + def __iter__ (self, items) : """ Apply items against our rules, yielding any matches. """ @@ -143,5 +154,10 @@ if match : yield match - - + + def __str__ (self) : + return self.name + + def __repr__ (self) : + return 'SyslogRule({self.name}, ...)'.format(self=self) +