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 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__ = """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          log.debug("Found a 'from' address of %s" % fromAddr)
     94          if not fromAddr or fromAddr.find('@') == -1:
     95              log.warning("Unable to process the 'from' address %s -- ignoring mail" \
     96                          % fromAddr)
     97              return
     98
     99          fromAddr = message.get('From').split('@')[1].rstrip('>')
    100          fromAddr = fromAddr.split(' ')[0]
    101          log.debug("The from address after processing is '%s'" % fromAddr)
    102          try:
    103              fromIp = socket.gethostbyname(fromAddr)
    104          except socket.gaierror:
    105              fromIp = None
    106              log.info('Hostname lookup failed for host: %s' % fromAddr)
    107
    108          subject = message.get('Subject')
    109
    110          # This is tricky...  date comes in with an offset value that
    111          # represents the number of seconds of difference between the
    112          # parsed timezone and UTC.  The events database wants all time
    113          # as seconds since the epoch and treats it as UTC.  As a
    114          # result we have to use the datetime class to do the
    115          # conversion because the functions in the time module do all
    116          # kinds of covnersions "to be helpful"
    117          t = rfc822.parsedate_tz(message.get('Date'))
    118
    119          offset_secs = t[-1]
    120
    121          # Convert the offset in seconds to minutes.  calendar wants minutes
    122          offset_mins = offset_secs / 60
    123          tz = FixedOffset(offset_mins, "Unknown")
    124
    125          # Construct dt using the date and time as well as the timezone
    126          dt = datetime(t[0], t[1], t[2], t[3], t[4], t[5], 0, tz)
    127          secs = calendar.timegm(dt.utctimetuple())
    128          log.info('Timestamp of the event (should be in UTC): %f' % secs)
    129
    130          event = MailEvent(device=fromAddr, rcvtime=secs)
    131          if fromIp:
    132              event.ipAddress = fromIp
    133
    134          payloads = message.get_payload()
    135          payload = 'This is the default message'
    136          while type(payloads) == types.ListType:
    137              payloads = payloads[0].get_payload()
    138          if type(payloads) == types.StringType:
    139              payload = payloads
    140
    141          body = payload
    142          event.summary = subject
    143          event.message = body
    144          self.enrich(event, subject)
    145
    146          event = self.buildEventClassKey(event)
    147          log.info('Sending event...')
    148          self.zem.sendEvent(event.__dict__)
    149          log.info('The event has been sent.')
    150
    151
    152 -    def enrich(self, event, subject):
    153          """
    154          Sanitize the event facility and severity fields.
    155
    156          @param event: event
    157          @type event: simple class
    158          @param subject: e-mail subject (unused)
    159          @type subject: string
    160          """
    161          unused(subject)
    162          event.facility = "unknown"
    163          event.severity = self.eventSeverity
    164
    165
    166 -    def buildEventClassKey(self, evt):
    167          """
    168          Set the Zenoss eventClassKey
    169
    170          @param evt: event
    171          @type evt: simple class
    172          @return: modified event
    173          @rtype: simple class
    174          """
    175          if getattr(evt, 'eventClassKey', '') or getattr(evt, 'eventClass', ''):
    176              return evt
    177          elif getattr(evt, 'ntevid', ''):
    178              evt.eventClassKey = "%s_%s" % (evt.component,evt.ntevid)
    179          elif getattr(evt, 'component', ''):
    180              evt.eventClassKey = evt.component
    181          else:
    182              evt.eventClassKey = 'email'
    183
    184          if getattr(evt, 'eventClassKey', ''):
    185              log.debug("eventClassKey=%s", evt.eventClassKey)
    186          else:
    187              log.debug("No eventClassKey assigned")
    188          return evt
    189
    190
    191
    192 -class POPProcessor(MessageProcessor):
    193      """
    194      Extension point for messages received via POP.  If you need to
    195      override the behavior of "process" you should do so by
    196      implementing it here.
    197      """
    198
    199 -    def __init__(self, zem, defaultSeverity = 2):
    200          """
    201          Initializer
    202
    203          @param zem: class that provides sendEvent() method
    204          @type zem: Zenoss event manager object
    205          @param defaultSeverity: severity level to use if we can't figure one out
    206          """
    207          MessageProcessor.__init__(self, zem, defaultSeverity)
    208
    209
    210
    211 -class MailProcessor(MessageProcessor):
    212      """
    213      Extension point for messages received via SMTP.  If you need to
    214      override the behavior of "process" you should do so by
    215      implementing it here.
    216      """
    217
    218 -    def __init__(self, zem, defaultSeverity = 2):
    219          """
    220          Initializer
    221
    222          @param zem: class that provides sendEvent() method
    223          @type zem: Zenoss event manager object
    224          @param defaultSeverity: severity level to use if we can't figure one out
    225          """
    226          MessageProcessor.__init__(self, zem, defaultSeverity)
    227