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          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')
    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.info('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 type(payloads) == types.ListType:
    139              payloads = payloads[0].get_payload()
    140          if type(payloads) == types.StringType:
    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          log.info('Sending event...')
    150          self.zem.sendEvent(event.__dict__)
    151          log.info('The event has been sent.')
    152
    153
    154 -    def enrich(self, event, subject):
    155          """
    156          Sanitize the event facility and severity fields.
    157
    158          @param event: event
    159          @type event: simple class
    160          @param subject: e-mail subject (unused)
    161          @type subject: string
    162          """
    163          unused(subject)
    164          event.facility = "unknown"
    165          event.severity = self.eventSeverity
    166
    167
    168 -    def buildEventClassKey(self, evt):
    169          """
    170          Set the Zenoss eventClassKey
    171
    172          @param evt: event
    173          @type evt: simple class
    174          @return: modified event
    175          @rtype: simple class
    176          """
    177          if getattr(evt, 'eventClassKey', '') or getattr(evt, 'eventClass', ''):
    178              return evt
    179          elif getattr(evt, 'ntevid', ''):
    180              evt.eventClassKey = "%s_%s" % (evt.component,evt.ntevid)
    181          elif getattr(evt, 'component', ''):
    182              evt.eventClassKey = evt.component
    183          else:
    184              evt.eventClassKey = 'email'
    185
    186          if getattr(evt, 'eventClassKey', ''):
    187              log.debug("eventClassKey=%s", evt.eventClassKey)
    188          else:
    189              log.debug("No eventClassKey assigned")
    190          return evt
    191
    192
    193
    194 -class POPProcessor(MessageProcessor):
    195      """
    196      Extension point for messages received via POP.  If you need to
    197      override the behavior of "process" you should do so by
    198      implementing it here.
    199      """
    200
    201 -    def __init__(self, zem, defaultSeverity = 2):
    202          """
    203          Initializer
    204
    205          @param zem: class that provides sendEvent() method
    206          @type zem: Zenoss event manager object
    207          @param defaultSeverity: severity level to use if we can't figure one out
    208          """
    209          MessageProcessor.__init__(self, zem, defaultSeverity)
    210
    211
    212
    213 -class MailProcessor(MessageProcessor):
    214      """
    215      Extension point for messages received via SMTP.  If you need to
    216      override the behavior of "process" you should do so by
    217      implementing it here.
    218      """
    219
    220 -    def __init__(self, zem, defaultSeverity = 2):
    221          """
    222          Initializer
    223
    224          @param zem: class that provides sendEvent() method
    225          @type zem: Zenoss event manager object
    226          @param defaultSeverity: severity level to use if we can't figure one out
    227          """
    228          MessageProcessor.__init__(self, zem, defaultSeverity)
    229