--- 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...")
--- 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<username>\S+) : TTY=(?P<tty>\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<target_user>\S+) ; (?:ENV=(?P<env>.+?) ; )?COMMAND=(?P<command>.*)
+
+format = {login}:{tty} - {user}@{host}:{pwd} - {command!r}
+
+#pattern = \s*(?P<login>\S+) : TTY=(?P<tty>\S+)\s; PWD=(?P<pwd>.+?)\s; USER=(?P<user>\S+)\s;( COMMAND=(?P<command>.*)
+
[puppet_readshadow]
program = sudo
pattern = \s*(?P<login>puppet) : TTY=(?P<tty>\S+)\s; PWD=(?P<pwd>.+?)\s; USER=(?P<user>root)\s; COMMAND=(?P<command>/usr/bin/getent shadow \w+)
format =
-[sudo]
-program = sudo
-pattern = \s*(?P<login>\S+) : TTY=(?P<tty>\S+)\s; PWD=(?P<pwd>.+?)\s; USER=(?P<user>\S+)\s; COMMAND=(?P<command>.*)
-format = {login}:{tty} - {user}@{host}:{pwd} - {command!r}
-
-[sudo_env]
-program = sudo
-pattern = \s*(?P<login>\S+) : TTY=(?P<tty>\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<user>\S+) ; ENV=(?P<env>.+?) ; COMMAND=(?P<command>.*)
-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}
--- 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)
+