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

# Source Code for Module Products.ZenEvents.MailProcessor

      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 or (at your
      8  # option) any later version as published by the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  __doc__ = """MailProcessor
     15  Base class module that other servers will subclass.
     16  """
     17
     18  import email, socket, rfc822, types
     19  import calendar
     20  from datetime import tzinfo, timedelta, datetime
     21
     22  from Event import Event
     23
     24  from Products.ZenUtils.Utils import unused
     25
     26  import logging
     27  log = logging.getLogger("zen.mail")
     28
     29
     30 -class MailEvent(Event):
     31      """
     32      Defaults for events created by the processor
     33      """
     34      agent="zenmail"
     35      eventGroup="mail"
     36
     37
     38  # The following is copied from the Python standard library
     39  # examples page: http://docs.python.org/library/datetime.html
     40  ZERO = timedelta(0)
     41
     42  # A class building tzinfo objects for fixed-offset time zones.
     43  # Note that FixedOffset(0, "UTC") is a different way to build a
     44  # UTC tzinfo object.
     45 -class FixedOffset(tzinfo):
     46      """Fixed offset in minutes east from UTC."""
     47
     48 -    def __init__(self, offset, name):
     49          self.__offset = timedelta(minutes = offset)
     50          self.__name = name
     51
     52 -    def utcoffset(self, unused):
     53          return self.__offset
     54
     55 -    def tzname(self, unused):
     56          return self.__name
     57
     58 -    def dst(self, dt):
     59          return ZERO
     60
     61
     62
     63 -class MessageProcessor(object):
     64      """
     65      Base class for parsing email messages that are retrieved via POP or
     66      received via SMTP.
     67      """
     68
     69 -    def __init__(self, zem, defaultSeverity = 2):
     70          """
     71          Initializer
     72
     73          @param zem: class that provides sendEvent() method
     74          @type zem: Zenoss event manager object
     75          @param defaultSeverity: severity level to use if we can't figure one out
     76          @type defaultSeverity: integer
     77          """
     78          self.zem = zem
     79          self.eventSeverity = defaultSeverity
     80
     81
     82 -    def process(self, messageStr):
     83          """
     84          Convert an e-mail message into a Zenoss event.
     85
     86          @param messageStr: e-mail message
     87          @type messageStr: string
     88          """
     89          message = email.message_from_string(messageStr)
     90          self.message = message
     91
     92          fromAddr = message.get('From')
     93          origFromAddr = fromAddr
     94          log.debug("Found a 'from' address of %s" % fromAddr)
     95          if not fromAddr or fromAddr.find('@') == -1:
     96              log.warning("Unable to process the 'from' address %s -- ignoring mail" \
     97                          % fromAddr)
     98              return
     99
    100          fromAddr = message.get('From').split('@')[1].rstrip('>')
    101          fromAddr = fromAddr.split(' ')[0]
    102          log.debug("The from address after processing is '%s'" % fromAddr)
    103          try:
    104              fromIp = socket.gethostbyname(fromAddr)
    105          except socket.gaierror:
    106              fromIp = None
    107              log.info('Hostname lookup failed for host: %s' % fromAddr)
    108
    109          subject = message.get('Subject').replace("\r","").replace("\n", "")
    110
    111          # This is tricky...  date comes in with an offset value that
    112          # represents the number of seconds of difference between the
    113          # parsed timezone and UTC.  The events database wants all time
    114          # as seconds since the epoch and treats it as UTC.  As a
    115          # result we have to use the datetime class to do the
    116          # conversion because the functions in the time module do all
    117          # kinds of covnersions "to be helpful"
    118          t = rfc822.parsedate_tz(message.get('Date'))
    119
    120          offset_secs = t[-1]
    121
    122          # Convert the offset in seconds to minutes.  calendar wants minutes
    123          offset_mins = offset_secs / 60
    124          tz = FixedOffset(offset_mins, "Unknown")
    125
    126          # Construct dt using the date and time as well as the timezone
    127          dt = datetime(t[0], t[1], t[2], t[3], t[4], t[5], 0, tz)
    128          secs = calendar.timegm(dt.utctimetuple())
    129          log.debug('Timestamp of the event (should be in UTC): %f' % secs)
    130
    131          event = MailEvent(device=fromAddr, rcvtime=secs,
    132                            fromEmailAddress=origFromAddr)
    133          if fromIp:
    134              event.ipAddress = fromIp
    135
    136          payloads = message.get_payload()
    137          payload = 'This is the default message'
    138          while isinstance(payloads, list):
    139              payloads = payloads[0].get_payload()
    140          if isinstance(payloads, basestring):
    141              payload = payloads
    142
    143          body = payload
    144          event.summary = subject
    145          event.message = body
    146          self.enrich(event, subject)
    147
    148          event = self.buildEventClassKey(event)
    149          self.zem.sendEvent(event.__dict__)
    150
    151 -    def enrich(self, event, subject):
    152          """
    153          Sanitize the event facility and severity fields.
    154
    155          @param event: event
    156          @type event: simple class
    157          @param subject: e-mail subject (unused)
    158          @type subject: string
    159          """
    160          unused(subject)
    161          event.facility = "unknown"
    162          event.severity = self.eventSeverity
    163
    164
    165 -    def buildEventClassKey(self, evt):
    166          """
    167          Set the Zenoss eventClassKey
    168
    169          @param evt: event
    170          @type evt: simple class
    171          @return: modified event
    172          @rtype: simple class
    173          """
    174          if getattr(evt, 'eventClassKey', '') or getattr(evt, 'eventClass', ''):
    175              return evt
    176          elif getattr(evt, 'ntevid', ''):
    177              evt.eventClassKey = "%s_%s" % (evt.component,evt.ntevid)
    178          elif getattr(evt, 'component', ''):
    179              evt.eventClassKey = evt.component
    180          else:
    181              evt.eventClassKey = 'email'
    182
    183          if getattr(evt, 'eventClassKey', ''):
    184              log.debug("eventClassKey=%s", evt.eventClassKey)
    185          else:
    186              log.debug("No eventClassKey assigned")
    187          return evt
    188
    189
    190
    191 -class POPProcessor(MessageProcessor):
    192      """
    193      Extension point for messages received via POP.  If you need to
    194      override the behavior of "process" you should do so by
    195      implementing it here.
    196      """
    197
    198 -    def __init__(self, zem, defaultSeverity = 2):
    199          """
    200          Initializer
    201
    202          @param zem: class that provides sendEvent() method
    203          @type zem: Zenoss event manager object
    204          @param defaultSeverity: severity level to use if we can't figure one out
    205          """
    206          MessageProcessor.__init__(self, zem, defaultSeverity)
    207
    208
    209
    210 -class MailProcessor(MessageProcessor):
    211      """
    212      Extension point for messages received via SMTP.  If you need to
    213      override the behavior of "process" you should do so by
    214      implementing it here.
    215      """
    216
    217 -    def __init__(self, zem, defaultSeverity = 2):
    218          """
    219          Initializer
    220
    221          @param zem: class that provides sendEvent() method
    222          @type zem: Zenoss event manager object
    223          @param defaultSeverity: severity level to use if we can't figure one out
    224          """
    225          MessageProcessor.__init__(self, zem, defaultSeverity)
    226