Guest User

Zenoss Acknowledge Events Via Email

a guest
Apr 16th, 2014
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.43 KB | None | 0 0
  1. #!/usr/bin/env /usr/local/zenoss/python/bin/python
  2. #
  3. # Author: Scott Haskell
  4. # Date: 08/30/2008
  5. # Modified: 10/30/2009
  6. # License: This is free software. You can re-distribuite it and/or modify
  7. # it under the terms of the GNU General Public License version 2 (GPLv2), as
  8. # published by the Free Software Foundation.
  9. #
  10. # Description: Script that reads body of Zenoss event email, passed by Procmail to STDIN.
  11. # Script attempts to find what state the event is in (0 - new, 1 - acknowledged, 2 - suppressed)
  12. # and acts accordingly.
  13. #
  14. # For detailed installation and configuration, please visit:
  15. #  http://www.zenoss.com/Members/shaskell/email-ack/email-acknowledgement-postfix-procmail-and-python
  16. #
  17. import Globals
  18. from Products.ZenUtils.ZenScriptBase import ZenScriptBase
  19. import sys
  20. import os
  21. import string
  22. import re
  23. import commands
  24. import string
  25. import signal
  26. import email
  27.  
  28. # Update for outgoing email
  29. MAIL = "/usr/sbin/sendmail -t "
  30. server_from_address = "[email protected]"
  31. cc_address = ""
  32.  
  33. # Debug flag
  34. DEBUG = 0
  35.  
  36. # Alarm Handler timeout (in seconds)
  37. TIMEOUT = 15
  38.  
  39. # Alarm handler
  40. def handler(signum, frame):
  41.     print 'Signal handler called with', signum
  42.     raise IOError, "Operation timed out"
  43.     sys.exit(255)
  44.  
  45. # Zenackevent Class - borrowed from
  46. # http://dev.zenoss.com/trac/browser/trunk/Products/ZenEvents/zenackevents.py
  47. class zenackevents(ZenScriptBase):
  48.     def ack(self, state=1, evids=(), userid=""):
  49.         self.dmd.ZenEventManager.manage_setEventStates(state,
  50.                                  evids, userid)
  51.  
  52. # Parse email headers and save payload
  53. # Argument(s): None
  54. # Return:
  55. #  1) Email Payload
  56. #  2) From Address
  57. def parse_email():
  58.     payload = None
  59.    
  60.     m = email.message_from_file(sys.stdin)
  61.  
  62.     # Debug Email
  63.     if(DEBUG):
  64.         keys = m.keys()
  65.         for key in keys:
  66.             print("%s: %s") % (key, m[key])
  67.  
  68.     # Get From Address
  69.     try:
  70.         from_address = m['From']
  71.     except:
  72.         print "Can't parse From Address in Email"
  73.  
  74.     # Check for multipart message
  75.     if(m.is_multipart()):
  76.         # Save message instances and loop
  77.         sub_parts = m.get_payload()
  78.        
  79.         # Get 'text/plain' instance
  80.         for sub_part in sub_parts:
  81.             if(sub_part.get_content_type() == "text/plain"):
  82.                 # save payload
  83.                 payload = sub_part.get_payload(decode=True)
  84.     else:
  85.         # save payload
  86.         payload = m.get_payload(decode=True)
  87.        
  88.     if(DEBUG):
  89.         print "DEBUG --"
  90.         print payload
  91.         print "-- DEBUG"
  92.  
  93.     if(not payload):
  94.         print "Could not successfully get email payload - Exiting"
  95.         sys.exit(1)
  96.        
  97.     return(payload, from_address)
  98.  
  99. # Subroutine to extract email addres, device and event id from body
  100. # of email.
  101. # Argument(s):
  102. #  1) string - email (header and body as single string)
  103. # Return:
  104. #  Dictionary that contains:
  105. #   1) email address of acknowledger
  106. #   2) eventid:device
  107. def parse(payload):
  108.     events = {}
  109.     evid = None
  110.     dev = None
  111.  
  112.     # Save Device
  113.     d = re.search(r'Device: (.*)', payload)
  114.    
  115.     try:
  116.         dev = d.group(1)
  117.     except:
  118.         print "Could not parse Device Name -- Setting to Unknown"
  119.    
  120.     # Save Event ID
  121.     # From what I can tell it's hex and digits repeating (at least 20 times)
  122.     # I did an re.findall with this regex and it matched all instances of the
  123.     # event ID in the email.
  124.     e = re.findall(r'([a-f0-9-]{20,})', payload)
  125.     if(e):
  126.         evid = parse_evids(e)
  127.     if(not evid):
  128.         print "Could not parse Event ID - Exiting"
  129.         sys.exit(1)
  130.    
  131.     if(dev):
  132.         events[evid] = dev
  133.     else:
  134.         events[evid] = "Unknown"
  135.    
  136.     return(events)
  137.  
  138. # It should match all evids, but if some random string gets detected in the
  139. # regex, take the highest occurance of what it believes to be the evid.
  140. # Arguments:
  141. #  1) dictionary - Event ID's
  142. # Returns:
  143. #  1) string - parsed event ID for ack
  144. def parse_evids(evids):
  145.     all_evids = {}
  146.    
  147.     # initialize and increment evid occurances
  148.     for v in evids:
  149.         if(not all_evids.has_key(v)):
  150.             all_evids[v] = 1
  151.         else:
  152.             all_evids[v] += 1
  153.    
  154.     k = all_evids.keys()
  155.     # sort all evid keys by number of occurances
  156.     if(len(all_evids) > 1): k.sort(key = all_evids.__getitem__)
  157.     evid = k.pop()
  158.    
  159.     return(evid)
  160.  
  161. # Subroutine to email that event was ack'd
  162. # Arguments:
  163. #  1) string - email address
  164. #  2) string - status message
  165. def email_ack(to_address, status, dev, evid, user, event_summary):
  166.     p = os.popen(MAIL, 'w')
  167.     p.write("From: %s\n" % (server_from_address))
  168.     p.write("To: %s\n" % (to_address))
  169.     if(cc_address): p.write("Cc: %s\n" % (cc_address))
  170.     p.write("Content-Type: text/plain\n")
  171.     p.write("Subject: ACK: %s on %s\n\n" % (event_summary, dev))
  172.     p.write("%s\n" % status)
  173.     exitcode = p.close()
  174.     if exitcode:
  175.         print "Exit code: %s" % exitcode
  176.  
  177. # Subroutine to acknowledge event
  178. # Argument(s):
  179. #  1) zae object
  180. #  2) dictionary - events dictionary
  181. def ack_event(zae, events, from_address):
  182.     user = None
  183.     status = None
  184.    
  185.     # save date for logging
  186.     cmd = "/bin/date"
  187.     date = commands.getoutput(cmd)
  188.    
  189.     # extract user from email address
  190.     try:
  191.         u = re.search(r'(\w+)@.*', from_address)
  192.         user = u.group(1)
  193.     except:
  194.         user = "admin"
  195.    
  196.     # loop through events dictionary and ack event(s)
  197.     for k, v in events.iteritems():
  198.         state = None
  199.         evids = []
  200.    
  201.         evid = k
  202.         dev = v
  203.    
  204.         try:
  205.             # save event state; 0 == new, 1 == ack, 2 == suppressed
  206.             event_summary = zae.dmd.ZenEventManager.getEventDetail(evid).summary
  207.             state = int(zae.dmd.ZenEventManager.getEventDetail(evid).eventState)
  208.            
  209.             if(state == 0):
  210.                 try:
  211.                     # append to list, because manage_setEventStates expects list
  212.                     evids.append(evid)
  213.                     zae.ack(evids=evids, userid=user)
  214.                     status = "%s - Acknowledged evid: %s on %s by %s" % (date, evid, dev, user)
  215.                 except:
  216.                     status = "%s - ERROR: Could not ack evid: %s on %s" % (date, evid, dev)
  217.            
  218.             if(state == 1):
  219.                 status = "%s - evid: %s on %s has already been acknowledged" % (date, evid, dev)
  220.            
  221.             if(state == 2):
  222.                 status = "%s - evid: %s on %s has been suppressed" % (date, evid, dev)
  223.         except:
  224.             if(not state):
  225.                 status = "could not get event state on event id %s - Exiting" % (evid)
  226.                 sys.exit(1)
  227.             else:
  228.                 status = "%s - Eventid %s on %s has already cleared or has been moved to history" % (date, evid, dev)
  229.    
  230.         # log status message
  231.         print status
  232.  
  233. if __name__ == '__main__':
  234.     events = {}
  235.     zae = zenackevents(connect=True)
  236.    
  237.     # Set alarm handler and alarm
  238.     signal.signal(signal.SIGALRM, handler)
  239.     signal.alarm(TIMEOUT)
  240.    
  241.     (payload, from_address) = parse_email()
  242.     events = parse(payload)
  243.     ack_event(zae, events, from_address)
  244.    
  245.     # disable alarm
  246.     signal.alarm(0)
  247.     sys.exit(0)
Advertisement
Add Comment
Please, Sign In to add comment