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