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

# Source Code for Module Products.ZenEvents.MailProcessor

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