Package Products :: Package ZenEvents :: Module SyslogProcessing |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenEvents.SyslogProcessing

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  __doc__ = """SyslogProcessing
     15  Class for turning syslog events into Zenoss Events
     16  """
     17
     18  import re
     19  import logging
     20  slog = logging.getLogger("zen.Syslog")
     21
     22  import Globals
     23  from Products.ZenEvents.syslog_h import *
     24  from Products.ZenUtils.IpUtil import isip
     25
     26  import socket
     27
     28  # Regular expressions that parse syslog tags from different sources
     29  parsers = (
     30  # generic mark
     31  r"^(?P<summary>-- (?P<eventClassKey>MARK) --)",
     32
     33  # ntsyslog windows msg
     34  r"^(?P<component>.+)\[(?P<ntseverity>\D+)\] (?P<ntevid>\d+) (?P<summary>.*)",
     35
     36  # cisco msg with card indicator
     37  r"%CARD-\S+:(SLOT\d+) %(?P<eventClassKey>\S+): (?P<summary>.*)",
     38
     39  # cisco standard msg
     40  r"%(?P<eventClassKey>(?P<component>\S+)-\d-\S+): (?P<summary>.*)",
     41
     42  # Cisco ACS
     43  r"^(?P<ipAddress>\S+)\s+(?P<summary>(?P<eventClassKey>CisACS_\d\d_\S+)\s+(?P<eventKey>\S+)\s.*)",
     44
     45  # netscreen device msg
     46  r"device_id=\S+\s+\[\S+\](?P<eventClassKey>\S+\d+):\s+(?P<summary>.*)\s+\((?P<originalTime>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)\)",
     47
     48  # unix syslog with pid
     49  r"(?P<component>\S+)\[(?P<pid>\d+)\]:\s*(?P<summary>.*)",
     50
     51  # unix syslog without pid
     52  r"(?P<component>\S+): (?P<summary>.*)",
     53
     54  # adtran devices
     55  r"^(?P<deviceModel>[^\[]+)\[(?P<deviceManufacturer>ADTRAN)\]:(?P<component>[^\|]+\|\d+\|\d+)\|(?P<summary>.*)",
     56
     57  r"^date=.+ (?P<summary>devname=.+ log_id=(?P<eventClassKey>\d+) type=(?P<component>\S+).+)",
     58
     59  # proprietary message passing system
     60  r"^(?P<component>\S+)(\.|\s)[A-Z]{3} \d \S+ \d\d:\d\d:\d\d-\d\d:\d\d:\d\d \d{5} \d{2} \d{5} \S+ \d{4} \d{3,5} (- )*(?P<summary>.*) \d{4} \d{4}",
     61
     62  # Cisco port state logging info
     63  r"^Process (?P<process_id>\d+), Nbr (?P<device>\d+\.\d+\.\d+\.\d+) on (?P<interface>\w+/\d+) from (?P<start_state>\w+) to (?P<end_state>\w+), (?P<summary>.+)",
     64
     65  # Cisco VPN Concentrator
     66  # 54884 05/25/2009 13:41:14.060 SEV=3 HTTP/42 RPT=4623 Error on socket accept.
     67  r"^\d+ \d+\/\d+\/\d+ \d+:\d+:\d+\.\d+ SEV=\d+ (?P<eventClassKey>\S+) RPT=\d+ (?P<summary>.*)",
     68
     69  # Dell Storage Array
     70  # 2626:48:VolExec:27-Aug-2009 13:15:58.072049:VE_VolSetWorker.hh:75:WARNING:43.3.2:Volume volumeName has reached 96 percent of its reported size and is currently using 492690MB.
     71  r'^\d+:\d+:(?P<component>[^:]+):\d+-\w{3}-\d{4} \d{2}:\d{2}:\d{2}\.\d+:[^:]+:\d+:\w+:(?P<eventClassKey>[^:]+):(?P<summary>.*)',
     72  )
     73
     74  # compile regex parsers on load
     75  compiledParsers = []
     76  for regex in parsers:
     77      try:
     78          compiled = re.compile(regex)
     79          compiledParsers.append(compiled)
     80      except:
     81          pass
     82
     83
     84 -class SyslogProcessor(object):
     85      """
     86      Class to process syslog messages and convert them into events viewable
     87      in the Zenoss event console.
     88      """
     89
     90 -    def __init__(self,sendEvent,minpriority,parsehost,monitor,defaultPriority):
     91          """
     92          Initializer
     93
     94          @param sendEvent: message from a remote host
     95          @type sendEvent: string
     96          @param minpriority: ignore anything under this priority
     97          @type minpriority: integer
     98          @param parsehost: hostname where this parser is running
     99          @type parsehost: string
    100          @param monitor: name of the distributed collector monitor
    101          @type monitor: string
    102          @param defaultPriority: priority to use if it can't be understood from the received packet
    103          @type defaultPriority: integer
    104          """
    105          self.minpriority = minpriority
    106          self.parsehost = parsehost
    107          self.sendEvent = sendEvent
    108          self.monitor = monitor
    109          self.defaultPriority = defaultPriority
    110
    111
    112 -    def process(self, msg, ipaddr, host, rtime):
    113          """
    114          Process an event from syslog and convert to a Zenoss event
    115
    116          @param msg: message from a remote host
    117          @type msg: string
    118          @param ipaddr: IP address of the remote host
    119          @type ipaddr: string
    120          @param host: remote host's name
    121          @type host: string
    122          @param rtime: time as reported by the remote host
    123          @type rtime: string
    124          """
    125          evt = dict(device=host,
    126                     ipAddress=ipaddr,
    127                     firstTime=rtime,
    128                     lastTime=rtime,
    129                     eventGroup='syslog')
    130          slog.debug("host=%s, ip=%s", host, ipaddr)
    131          slog.debug(msg)
    132
    133          evt, msg = self.parsePRI(evt, msg)
    134          if evt['priority'] > self.minpriority: return
    135
    136          evt, msg = self.parseHEADER(evt, msg)
    137          evt = self.parseTag(evt, msg)
    138          #rest of msg now in summary of event
    139          evt = self.buildEventClassKey(evt)
    140          evt['monitor'] = self.monitor
    141          self.sendEvent(evt)
    142
    143
    144 -    def parsePRI(self, evt, msg):
    145          """
    146          Parse RFC-3164 PRI part of syslog message to get facility and priority.
    147
    148          @param evt: dictionary of event properties
    149          @type evt: dictionary
    150          @param msg: message from host
    151          @type msg: string
    152          @return: tuple of dictionary of event properties and the message
    153          @type: (dictionary, string)
    154          """
    155          pri = self.defaultPriority
    156          fac = None
    157          if msg[:1] == '<':
    158              pos = msg.find('>')
    159              fac, pri = LOG_UNPACK(int(msg[1:pos]))
    160              msg = msg[pos+1:]
    161          elif msg and msg[0] < ' ':
    162              fac, pri = LOG_KERN, ord(msg[0])
    163              msg = msg[1:]
    164          evt['facility'] = fac_names.get(fac,"unknown")
    165          evt['priority'] = pri
    166          evt['severity'] = self.defaultSeverityMap(pri)
    167          slog.debug("fac=%s pri=%s", fac, pri)
    168          slog.debug("facility=%s severity=%s", evt['facility'], evt['severity'])
    169          return evt, msg
    170
    171
    172 -    def defaultSeverityMap(self, pri):
    173          """
    174          Default mapping from syslog priority to severity.
    175
    176          @param pri: syslog priority from host
    177          @type pri: integer
    178          @return: numeric severity
    179          @type: integer
    180          """
    181          sev = 1
    182          if pri < 3: sev = 5
    183          elif pri == 3: sev = 4
    184          elif pri == 4: sev = 3
    185          elif pri == 5 or pri == 6: sev = 2
    186          return sev
    187
    188
    189      timeParse = \
    190          re.compile("^(\S{3} [\d ]{2} [\d ]{2}:[\d ]{2}:[\d ]{2}) (.*)").search
    191      notHostSearch = re.compile("[\[:]").search
    192 -    def parseHEADER(self, evt, msg):
    193          """
    194          Parse RFC-3164 HEADER part of syslog message.  TIMESTAMP format is:
    195          MMM HH:MM:SS and host is next token without the characters '[' or ':'.
    196
    197          @param evt: dictionary of event properties
    198          @type evt: dictionary
    199          @param msg: message from host
    200          @type msg: string
    201          @return: tuple of dictionary of event properties and the message
    202          @type: (dictionary, string)
    203          """
    204          slog.debug(msg)
    205          m = re.sub("Kiwi_Syslog_Daemon \d+: \d+: "
    206              "\S{3} [\d ]{2} [\d ]{2}:[\d ]{2}:[^:]+: ", "", msg)
    207          m = self.timeParse(msg)
    208          if m:
    209              slog.debug("parseHEADER timestamp=%s", m.group(1))
    210              evt['originalTime'] = m.group(1)
    211              msg = m.group(2).strip()
    212          msglist = msg.split()
    213          if self.parsehost and not self.notHostSearch(msglist[0]):
    214              device = msglist[0]
    215              if device.find('@') >= 0:
    216                  device = device.split('@', 1)[1]
    217              slog.debug("parseHEADER hostname=%s", evt['device'])
    218              msg = " ".join(msglist[1:])
    219              evt['device'] = device
    220              if isip(device):
    221                  evt['ipAddress'] = device
    222              else:
    223                  if 'ipAddress' in evt:
    224                      del(evt['ipAddress'])
    225          return evt, msg
    226
    227
    228 -    def parseTag(self, evt, msg):
    229          """
    230          Parse the RFC-3164 tag of the syslog message using the regex defined
    231          at the top of this module.
    232
    233          @param evt: dictionary of event properties
    234          @type evt: dictionary
    235          @param msg: message from host
    236          @type msg: string
    237          @return: dictionary of event properties
    238          @type: dictionary
    239          """
    240          slog.debug(msg)
    241          for parser in compiledParsers:
    242              slog.debug("tag regex: %s", parser.pattern)
    243              m = parser.search(msg)
    244              if not m: continue
    245              slog.debug("tag match: %s", m.groupdict())
    246              evt.update(m.groupdict())
    247              break
    248          else:
    249              slog.info("No matching parser: '%s'", msg)
    250              evt['summary'] = msg
    251          return evt
    252
    253
    254 -    def buildEventClassKey(self, evt):
    255          """
    256          Build the key used to find an events dictionary record. If eventClass
    257          is defined it is used. For NT events "Source_Evid" is used. For other
    258          syslog events we use the summary of the event to perform a full text
    259          or'ed search.
    260
    261          @param evt: dictionary of event properties
    262          @type evt: dictionary
    263          @return: dictionary of event properties
    264          @type: dictionary
    265          """
    266          if evt.has_key('eventClassKey') or evt.has_key( 'eventClass'):
    267              return evt
    268          elif evt.has_key( 'ntevid'):
    269              evt['eventClassKey'] = "%s_%s" % (evt['component'],evt['ntevid'])
    270          elif evt.has_key( 'component'):
    271              evt['eventClassKey'] = evt['component']
    272          if evt.has_key( 'eventClassKey'):
    273              slog.debug("eventClassKey=%s", evt['eventClassKey'])
    274              try:
    275                  evt['eventClassKey'] = evt['eventClassKey'].decode('latin-1')
    276              except:
    277                  evt['eventClassKey'] = evt['eventClassKey'].decode('utf-8')
    278          else:
    279              slog.debug("No eventClassKey assigned")
    280          return evt
    281