def __init__(self): """ """ self.getOptions() #self.options.verbose = True self.zenoss = ZenossHandler(self.options.zenhost, self.options.zenuser, self.options.zenpass, self.options.verbose) self.pagerduty = PagerDutyHandler(self.options.pdhost, self.options.pdtoken, self.options.verbose) # dictionary of corresponding event/incident states self.statusData = { "ack": { "zenoss": "Acknowledged", "pagerduty": "acknowledged", "zenaction": "acknowledge", "num": 1 }, "close": { "zenoss": "Closed", "pagerduty": "resolved", "zenaction": "close", "num": 2 }, "open": { "zenoss": "New", "pagerduty": "triggered", "zenaction": "unacknowledge", "num": 0 }, } if self.options.zenver == True: self.statusData["open"]["zenaction"] = "reopen" self.messenger = MessageHandler() self.statusDict = {}
def __init__(self): """ """ self.getOptions() #self.options.verbose = True self.zenoss = ZenossHandler(self.options.zenhost, self.options.zenuser, self.options.zenpass, self.options.verbose) self.pagerduty = PagerDutyHandler(self.options.pdhost, self.options.pdtoken, self.options.verbose) # dictionary of corresponding event/incident states self.statusData = { "ack":{"zenoss":"Acknowledged","pagerduty":"acknowledged","zenaction":"acknowledge","num":1}, "close":{"zenoss":"Closed","pagerduty":"resolved","zenaction":"close","num":2}, "open":{"zenoss":"New","pagerduty":"triggered","zenaction":"unacknowledge","num":0}, } if self.options.zenver == True: self.statusData["open"]["zenaction"] = "reopen" self.messenger = MessageHandler() self.statusDict = {}
class Main(): """ Either: 1) Create Pagerduty Incident based on new Zenoss event 2) Synchronize Zenoss and Pagerduty Events and Incidents """ def __init__(self): """ """ self.getOptions() #self.options.verbose = True self.zenoss = ZenossHandler(self.options.zenhost, self.options.zenuser, self.options.zenpass, self.options.verbose) self.pagerduty = PagerDutyHandler(self.options.pdhost, self.options.pdtoken, self.options.verbose) # dictionary of corresponding event/incident states self.statusData = { "ack": { "zenoss": "Acknowledged", "pagerduty": "acknowledged", "zenaction": "acknowledge", "num": 1 }, "close": { "zenoss": "Closed", "pagerduty": "resolved", "zenaction": "close", "num": 2 }, "open": { "zenoss": "New", "pagerduty": "triggered", "zenaction": "unacknowledge", "num": 0 }, } if self.options.zenver == True: self.statusData["open"]["zenaction"] = "reopen" self.messenger = MessageHandler() self.statusDict = {} def lastDisabled(self, service): """ return PagerDuty newest disabled entry """ message = "" details = self.pagerduty.getServiceLog(service["id"])["log_entries"] last = 0 lastentry = None for d in details: if d["maintenance_window"]["type"] == "disable": starttime = self.messenger.getLocalTime( d["maintenance_window"]["time"]) start = self.messenger.getTimestamp(starttime) if start > last: lastentry = d last = start return lastentry def getMaintenanceWindows(self, service): """ return list of maintenance windows (ongoing) for a given service """ output = [] windows = self.pagerduty.getMaintenanceWindows()["maintenance_windows"] for w in windows: services = w["services"] for s in services: if s["id"] == service["id"]: beg = self.messenger.getLocalTime(w["start_time"]) fin = self.messenger.getLocalTime(w["end_time"]) start = self.messenger.getTimestamp(beg) end = self.messenger.getTimestamp(fin) now = time.time() if start <= now and now <= end: output.append(w) return output def createIncidentDetails(self, evid): """ Retrieve event detail from Zenoss and format it for PagerDuty incident creation """ self.incidentData = { "service_key": self.options.servicekey, "incident_key": self.options.evid } details = self.zenoss.getEventDetails( evid)["result"]["event"][0]["properties"] self.statusDict[evid] = {} self.statusDict[evid]["target"] = "open" data = {} status = 0 dev = None comp = None summ = None for d in details: k = d["key"] v = d["value"] data[k] = v if k == "device": dev = v if k == "component": comp = v if k == "summary": summ = v if k == "eventState": status = v self.incidentData["details"] = data self.incidentData["description"] = "%s | %s | %s" % (dev, comp, summ) if status == 0: self.statusDict[evid]["current"] = "open" elif status == 1: self.statusDict[evid]["current"] = "ack" elif status == 2: self.statusDict[evid]["current"] = "close" return status def createPagerDutyIncident(self): """ 1) check whether the destination service is in a maintenance window. a) If so, ack the local alert in zenoss b) and add a "in maintenance window" log message to the Zenoss event details. 2) if it is not in maintenance, proceed with the event submission. as usual a) send event to pagerduty b) update Zenoss console with submit status info c) update Zenoss Event with incident details (incident URL, service URL, oncall) 3) check issues in Zenoss w/pagerduty incidents: a) if acked in Zenoss, ack in PagerDuty b) if closed in Zenoss, resolve in PagerDuty """ status = self.createIncidentDetails(self.options.evid) if status == 0: # first find the appropriate PD service definition service = self.pagerduty.findService(self.options.servicekey) if service: # in maintenance, so ack the zenoss alert but note the window detail if self.pagerduty.inMaintenance(service) == True: self.statusDict[self.options.evid]["target"] = "ack" mws = self.getMaintenanceWindows(service) for mw in mws: self.messenger.serviceInMaintenance( self.options.evid, "Acknowledged", service, mw, self.pagerduty.weburl) # disabled, so leave event unacked in Zenoss, but that service is disabled elif self.pagerduty.isDisabled(service) == True: self.messenger.serviceIsDisabled( self.options.evid, "No Incident created", service, self.lastDisabled(service), self.pagerduty.weburl) # assuming service is enabled, create PD incident, note output in zenoss event console. else: output = self.pagerduty.manageIncident( self.incidentData, "trigger") try: self.messenger.serviceIncidentCreated( self.options.evid, service, self.pagerduty.weburl, output["errors"]) except KeyError: self.messenger.serviceIncidentCreated( self.options.evid, service, self.pagerduty.weburl) else: self.messenger.serviceNotFound(self.options.evid, self.options.servicekey) else: self.statusDict[self.options.evid]["target"] = self.statusDict[ self.options.evid]["current"] def correlateByZenoss(self): """ build dictionary of Zenoss events matched to PagerDuty incidents 1) get list of zenoss events 2) get list of pagerduty incidents based on date range of zenoss events 3) match them by evid - incident_key """ if self.options.verbose == True: print "CORRELATING ZENOSS EVENTS AND PAGERDUTY INCIDENTS" #incidents = self.pagerduty.getIncidentList()["incidents"] events = self.zenoss.getEvents()["events"] self.pairs = [] for e in events: data = {"id": e["evid"], 'pagerduty': None, 'zenoss': e} try: data["pagerduty"] = self.pagerduty.getIncidentByKey(e["evid"]) except: pass self.pairs.append(data) def correlateByPagerDuty(self): """ build dictionary of Zenoss events matched to PagerDuty incidents 1) get list of zenoss events 2) get list of pagerduty incidents based on date range of zenoss events 3) match them by evid - incident_key """ if self.options.verbose == True: print "CORRELATING ZENOSS EVENTS AND PAGERDUTY INCIDENTS" incidents = self.pagerduty.getIncidentList()["incidents"] events = self.zenoss.getEvents()["events"] self.pairs = [] for i in incidents: data = {"id": i["incident_key"], 'pagerduty': i, 'zenoss': None} for e in events: if i["incident_key"] == e["evid"]: data['zenoss'] = e self.pairs.append(data) def synchronize(self): """ update pagerduty based on Zenoss event console activities (acknowledge, resolve, etc) 4) if acked in one, ack in the other 5) if closed in one, close in the other """ if self.options.verbose == True: print "SYNCHRONIZING ZENOSS AND PAGERDUTY EVENT/INCIDENT STATES" for p in self.pairs: id = p["id"] zen = p["zenoss"] pd = p["pagerduty"] self.messenger.newId(id) self.statusDict[id] = {} # if pagerduty event is open, but not listed in zenoss, then close it (for Zenoss 3.x, to avoid querying event history) if zen == None: #if self.options.zenver == False: self.statusDict[id]["target"] = "close" else: # set up console messages self.messenger.incidentCreated(id, pd) #self.messenger.incidentAssigned(id, pd) details = self.pagerduty.getIncidentDetail( pd["id"])["log_entries"] self.messenger.incidentLogs(id, details, self.pagerduty.weburl) for k, v in self.statusData.items(): if v["zenoss"] == zen["eventState"]: self.statusDict[id]["current"] = k # determine whether Zenoss or Pagerduty has authority self.compare(zen, pd) def compare(self, event, incident): """ determine target state based on event vs. incident last updated """ if self.options.verbose == True: print "COMPARING EVENT AND INCIDENT FOR ID:%s" % event["evid"] evupdated = None evdetails = details = self.zenoss.getEventDetails( event["evid"])["result"]["event"][0]["properties"] for e in evdetails: k = e["key"] v = e["value"] if k == "stateChange": eupdated = v iupdated = incident["last_status_change_on"] zt = self.messenger.getZenossTime(eupdated) pt = self.messenger.getUTCTime(iupdated) if self.options.verbose == True: print "COMPARING zenoss time %s and pagerduty time %s" % (eupdated, iupdated) print "COMPARING zenoss time %s and pagerduty time %s" % (zt, pt) zentime = self.messenger.getTimestamp(zt) pdtime = self.messenger.getTimestamp(pt) zenstate = event['eventState'] pdstate = incident['status'] if self.options.verbose == True: print "EVENT ID %s IS %s IN ZENOSS, %s IN PAGERDUTY" % ( event["evid"], zenstate, pdstate) zentarget = None pdtarget = None for k, v in self.statusData.items(): if v["zenoss"] == zenstate: zentarget = k if v["pagerduty"] == pdstate: pdtarget = k if zentime >= pdtime: if self.options.verbose == True: print "ZENOSS %s NEWER THAN PAGERDUTY %s" % (zenstate, pdstate) self.statusDict[event["evid"]]["target"] = zentarget else: if self.options.verbose == True: print "PAGERDUTY %s NEWER THAN ZENOSS %s" % (pdstate, zenstate) self.statusDict[event["evid"]]["target"] = pdtarget def getIncidentLogs(self, id, evid): details = self.pagerduty.getIncidentDetail(id)["log_entries"] self.messenger.incidentLogs(evid, details, self.pagerduty.weburl) def updatePagerDuty(self): """ update PagerDuty incident status """ if self.options.verbose == True: print "UPDATING PAGERDUTY" updates = {"incidents": [], "requester_id": self.options.pduser} for p in self.pairs: id = p["id"] pd = p["pagerduty"] target = self.statusDict[id]["target"] if pd["status"] != self.statusData[target]["pagerduty"]: if self.options.verbose == True: print "INCIDENT %s IS %s; SHOULD BE %s" % ( pd["id"], pd["status"], self.statusData[target]["pagerduty"]) pdData = { "id": pd["id"], "status": self.statusData[target]["pagerduty"] } updates["incidents"].append(pdData) if len(updates["incidents"]) > 0: self.pagerduty.updateStatus(updates) def updateZenoss(self): """ update Zenoss console """ if self.options.verbose == True: print "UPDATING ZENOSS" for p in self.pairs: id = p["id"] zen = p["zenoss"] if p["zenoss"] != None: self.updateZenossMessages(id) for p in self.pairs: id = p["id"] zen = p["zenoss"] if p["zenoss"] != None: self.updateZenossStatus(id) def updateZenossStatus(self, evid): """ update Zenoss event status if target is not current """ current = self.statusDict[evid]["current"] target = self.statusDict[evid]["target"] if current != target: if self.options.verbose == True: print "CHANGING STATUS %s IN ZENOSS TO %s" % (evid, target) self.zenoss.manageEventStatus([evid], self.statusData[target]['zenaction']) def updateZenossMessages(self, evid): """ update Zenoss event console with messages if they don't exist """ eventlog = self.zenoss.getEventDetails( evid)["result"]["event"][0]["log"] logs = [] for e in eventlog: logs.append(e[-1]) for msg in self.messenger.messages[evid]: if msg not in logs: self.zenoss.addEventMessage(evid, msg) def getOptions(self): """ Command line runtime arguments """ usage = "usage: %prog [options] arg" parser = OptionParser(usage) # options for Zenoss parser.add_option("-z", "--zenhost", dest="zenhost", help="Zenoss Server hostname", default=ZENOSS_HOST) parser.add_option("-u", "--zenuser", dest="zenuser", help="Zenoss admin username", default=ZENOSS_USERNAME) parser.add_option("-p", "--zenpass", dest="zenpass", help="Zenoss admin password", default=ZENOSS_PASSWORD) parser.add_option("-e", "--evid", dest="evid", help="Zenoss Event ID", default=EVID) parser.add_option("-V", "--zenver", dest="zenver", help="True if Zenoss version >= 4", action="store_false") # options for Pagerduty parser.add_option("-H", "--pdhost", dest="pdhost", help="Pagerduty hostname", default=PAGERDUTY_HOST) parser.add_option("-T", "--pdtoken", dest="pdtoken", help="Pagerduty token", default=PAGERDUTY_TOKEN) parser.add_option( "-U", "--pduser", dest="pduser", help="Pagerduty User Key (for tracking auto updates)", default=PAGERDUTY_USER) parser.add_option("-S", "--servicekey", dest="servicekey", help="Pagerduty Service Key", default=PAGERDUTY_SERVICEKEY) # action to be performed parser.add_option("-a", "--action", dest="action", help="one of [create|update]", default="update") parser.add_option("-v", "--verbose", dest="verbose", help="Show additional output", action="store_true") # options for zenoss interaction (self.options, self.args) = parser.parse_args() def run(self): """ control script execution """ if self.options.action == 'create': self.messenger.newId(self.options.evid) self.createPagerDutyIncident() self.updateZenossMessages(self.options.evid) self.updateZenossStatus(self.options.evid) if self.options.action == 'update': self.correlateByPagerDuty() self.synchronize() self.updatePagerDuty() self.updateZenoss()
class Main(): """ Either: 1) Create Pagerduty Incident based on new Zenoss event 2) Synchronize Zenoss and Pagerduty Events and Incidents """ def __init__(self): """ """ self.getOptions() #self.options.verbose = True self.zenoss = ZenossHandler(self.options.zenhost, self.options.zenuser, self.options.zenpass, self.options.verbose) self.pagerduty = PagerDutyHandler(self.options.pdhost, self.options.pdtoken, self.options.verbose) # dictionary of corresponding event/incident states self.statusData = { "ack":{"zenoss":"Acknowledged","pagerduty":"acknowledged","zenaction":"acknowledge","num":1}, "close":{"zenoss":"Closed","pagerduty":"resolved","zenaction":"close","num":2}, "open":{"zenoss":"New","pagerduty":"triggered","zenaction":"unacknowledge","num":0}, } if self.options.zenver == True: self.statusData["open"]["zenaction"] = "reopen" self.messenger = MessageHandler() self.statusDict = {} def lastDisabled(self,service): """ return PagerDuty newest disabled entry """ message = "" details = self.pagerduty.getServiceLog(service["id"])["log_entries"] last = 0 lastentry = None for d in details: if d["maintenance_window"]["type"] == "disable": starttime = self.messenger.getLocalTime(d["maintenance_window"]["time"]) start = self.messenger.getTimestamp(starttime) if start > last: lastentry = d last = start return lastentry def getMaintenanceWindows(self,service): """ return list of maintenance windows (ongoing) for a given service """ output = [] windows = self.pagerduty.getMaintenanceWindows()["maintenance_windows"] for w in windows: services = w["services"] for s in services: if s["id"] == service["id"]: beg = self.messenger.getLocalTime(w["start_time"]) fin = self.messenger.getLocalTime(w["end_time"]) start = self.messenger.getTimestamp(beg) end = self.messenger.getTimestamp(fin) now = time.time() if start <= now and now <=end: output.append(w) return output def createIncidentDetails(self,evid): """ Retrieve event detail from Zenoss and format it for PagerDuty incident creation """ self.incidentData = { "service_key": self.options.servicekey, "incident_key": self.options.evid } details = self.zenoss.getEventDetails(evid)["result"]["event"][0]["properties"] self.statusDict[evid] = {} self.statusDict[evid]["target"] = "open" data = {} status = 0 dev = None comp = None summ = None for d in details: k = d["key"] v = d["value"] data[k] = v if k == "device": dev=v if k == "component": comp = v if k == "summary": summ = v if k == "eventState": status = v self.incidentData["details"] = data self.incidentData["description" ] = "%s | %s | %s" % (dev, comp, summ) if status == 0: self.statusDict[evid]["current"] = "open" elif status == 1: self.statusDict[evid]["current"] = "ack" elif status == 2: self.statusDict[evid]["current"] = "close" return status def createPagerDutyIncident(self): """ 1) check whether the destination service is in a maintenance window. a) If so, ack the local alert in zenoss b) and add a "in maintenance window" log message to the Zenoss event details. 2) if it is not in maintenance, proceed with the event submission. as usual a) send event to pagerduty b) update Zenoss console with submit status info c) update Zenoss Event with incident details (incident URL, service URL, oncall) 3) check issues in Zenoss w/pagerduty incidents: a) if acked in Zenoss, ack in PagerDuty b) if closed in Zenoss, resolve in PagerDuty """ status = self.createIncidentDetails(self.options.evid) if status == 0: # first find the appropriate PD service definition service = self.pagerduty.findService(self.options.servicekey) if service: # in maintenance, so ack the zenoss alert but note the window detail if self.pagerduty.inMaintenance(service) == True: self.statusDict[self.options.evid]["target"] = "ack" mws = self.getMaintenanceWindows(service) for mw in mws: self.messenger.serviceInMaintenance(self.options.evid, "Acknowledged", service, mw, self.pagerduty.weburl) # disabled, so leave event unacked in Zenoss, but that service is disabled elif self.pagerduty.isDisabled(service) == True: self.messenger.serviceIsDisabled(self.options.evid, "No Incident created", service, self.lastDisabled(service), self.pagerduty.weburl) # assuming service is enabled, create PD incident, note output in zenoss event console. else: output = self.pagerduty.manageIncident(self.incidentData,"trigger") try: self.messenger.serviceIncidentCreated(self.options.evid, service, self.pagerduty.weburl, output["errors"]) except KeyError: self.messenger.serviceIncidentCreated(self.options.evid, service, self.pagerduty.weburl) else: self.messenger.serviceNotFound(self.options.evid, self.options.servicekey) else: self.statusDict[self.options.evid]["target"] = self.statusDict[self.options.evid]["current"] def correlateByZenoss(self): """ build dictionary of Zenoss events matched to PagerDuty incidents 1) get list of zenoss events 2) get list of pagerduty incidents based on date range of zenoss events 3) match them by evid - incident_key """ if self.options.verbose == True: print "CORRELATING ZENOSS EVENTS AND PAGERDUTY INCIDENTS" #incidents = self.pagerduty.getIncidentList()["incidents"] events = self.zenoss.getEvents()["events"] self.pairs = [] for e in events: data = {"id": e["evid"], 'pagerduty': None, 'zenoss': e} try: data["pagerduty"]= self.pagerduty.getIncidentByKey(e["evid"]) except: pass self.pairs.append(data) def correlateByPagerDuty(self): """ build dictionary of Zenoss events matched to PagerDuty incidents 1) get list of zenoss events 2) get list of pagerduty incidents based on date range of zenoss events 3) match them by evid - incident_key """ if self.options.verbose == True: print "CORRELATING ZENOSS EVENTS AND PAGERDUTY INCIDENTS" incidents = self.pagerduty.getIncidentList()["incidents"] events = self.zenoss.getEvents()["events"] self.pairs = [] for i in incidents: data = {"id": i["incident_key"], 'pagerduty': i, 'zenoss': None} for e in events: if i["incident_key"] == e["evid"]: data['zenoss'] = e self.pairs.append(data) def synchronize(self): """ update pagerduty based on Zenoss event console activities (acknowledge, resolve, etc) 4) if acked in one, ack in the other 5) if closed in one, close in the other """ if self.options.verbose == True: print "SYNCHRONIZING ZENOSS AND PAGERDUTY EVENT/INCIDENT STATES" for p in self.pairs: id = p["id"] zen = p["zenoss"] pd = p["pagerduty"] self.messenger.newId(id) self.statusDict[id] = {} # if pagerduty event is open, but not listed in zenoss, then close it (for Zenoss 3.x, to avoid querying event history) if zen == None: #if self.options.zenver == False: self.statusDict[id]["target"] = "close" else: # set up console messages self.messenger.incidentCreated(id, pd) #self.messenger.incidentAssigned(id, pd) details = self.pagerduty.getIncidentDetail(pd["id"])["log_entries"] self.messenger.incidentLogs(id, details, self.pagerduty.weburl) for k,v in self.statusData.items(): if v["zenoss"] == zen["eventState"]: self.statusDict[id]["current"] = k # determine whether Zenoss or Pagerduty has authority self.compare(zen, pd) def compare(self,event,incident): """ determine target state based on event vs. incident last updated """ if self.options.verbose == True: print "COMPARING EVENT AND INCIDENT FOR ID:%s" % event["evid"] evupdated = None evdetails = details = self.zenoss.getEventDetails(event["evid"])["result"]["event"][0]["properties"] for e in evdetails: k = e["key"] v = e["value"] if k == "stateChange": eupdated = v iupdated = incident["last_status_change_on"] zt = self.messenger.getZenossTime(eupdated) pt = self.messenger.getUTCTime(iupdated) if self.options.verbose == True: print "COMPARING zenoss time %s and pagerduty time %s" % (eupdated,iupdated) print "COMPARING zenoss time %s and pagerduty time %s" % (zt,pt) zentime = self.messenger.getTimestamp(zt) pdtime = self.messenger.getTimestamp(pt) zenstate = event['eventState'] pdstate = incident['status'] if self.options.verbose == True: print "EVENT ID %s IS %s IN ZENOSS, %s IN PAGERDUTY" % (event["evid"],zenstate,pdstate) zentarget = None pdtarget = None for k,v in self.statusData.items(): if v["zenoss"] == zenstate: zentarget = k if v["pagerduty"] == pdstate: pdtarget = k if zentime >= pdtime: if self.options.verbose == True: print "ZENOSS %s NEWER THAN PAGERDUTY %s" % (zenstate,pdstate) self.statusDict[event["evid"]]["target"] = zentarget else: if self.options.verbose == True: print "PAGERDUTY %s NEWER THAN ZENOSS %s" % (pdstate,zenstate) self.statusDict[event["evid"]]["target"] = pdtarget def getIncidentLogs(self,id,evid): details = self.pagerduty.getIncidentDetail(id)["log_entries"] self.messenger.incidentLogs(evid, details, self.pagerduty.weburl) def updatePagerDuty(self): """ update PagerDuty incident status """ if self.options.verbose == True: print "UPDATING PAGERDUTY" updates = {"incidents": [], "requester_id": self.options.pduser} for p in self.pairs: id = p["id"] pd = p["pagerduty"] target = self.statusDict[id]["target"] if pd["status"] != self.statusData[target]["pagerduty"]: if self.options.verbose == True: print "INCIDENT %s IS %s; SHOULD BE %s" % (pd["id"],pd["status"],self.statusData[target]["pagerduty"]) pdData = {"id": pd["id"], "status": self.statusData[target]["pagerduty"]} updates["incidents"].append(pdData) if len(updates["incidents"]) > 0: self.pagerduty.updateStatus(updates) def updateZenoss(self): """ update Zenoss console """ if self.options.verbose == True: print "UPDATING ZENOSS" for p in self.pairs: id = p["id"] zen = p["zenoss"] if p["zenoss"] != None: self.updateZenossMessages(id) for p in self.pairs: id = p["id"] zen = p["zenoss"] if p["zenoss"] != None: self.updateZenossStatus(id) def updateZenossStatus(self,evid): """ update Zenoss event status if target is not current """ current = self.statusDict[evid]["current"] target = self.statusDict[evid]["target"] if current != target: if self.options.verbose == True: print "CHANGING STATUS %s IN ZENOSS TO %s" % (evid,target) self.zenoss.manageEventStatus([evid], self.statusData[target]['zenaction']) def updateZenossMessages(self,evid): """ update Zenoss event console with messages if they don't exist """ eventlog = self.zenoss.getEventDetails(evid)["result"]["event"][0]["log"] logs = [] for e in eventlog: logs.append(e[-1]) for msg in self.messenger.messages[evid]: if msg not in logs: self.zenoss.addEventMessage(evid,msg) def getOptions(self): """ Command line runtime arguments """ usage = "usage: %prog [options] arg" parser = OptionParser(usage) # options for Zenoss parser.add_option("-z", "--zenhost", dest="zenhost", help="Zenoss Server hostname", default=ZENOSS_HOST) parser.add_option("-u", "--zenuser", dest="zenuser", help="Zenoss admin username", default=ZENOSS_USERNAME) parser.add_option("-p", "--zenpass", dest="zenpass", help="Zenoss admin password", default=ZENOSS_PASSWORD) parser.add_option("-e", "--evid", dest="evid", help="Zenoss Event ID", default=EVID) parser.add_option("-V", "--zenver", dest="zenver", help="True if Zenoss version >= 4", action="store_false") # options for Pagerduty parser.add_option("-H", "--pdhost", dest="pdhost", help="Pagerduty hostname", default=PAGERDUTY_HOST) parser.add_option("-T", "--pdtoken", dest="pdtoken", help="Pagerduty token", default=PAGERDUTY_TOKEN) parser.add_option("-U", "--pduser", dest="pduser", help="Pagerduty User Key (for tracking auto updates)", default=PAGERDUTY_USER) parser.add_option("-S", "--servicekey", dest="servicekey", help="Pagerduty Service Key", default=PAGERDUTY_SERVICEKEY) # action to be performed parser.add_option("-a", "--action", dest="action", help="one of [create|update]", default="update") parser.add_option("-v", "--verbose", dest="verbose", help="Show additional output", action="store_true") # options for zenoss interaction (self.options, self.args) = parser.parse_args() def run(self): """ control script execution """ if self.options.action == 'create': self.messenger.newId(self.options.evid) self.createPagerDutyIncident() self.updateZenossMessages(self.options.evid) self.updateZenossStatus(self.options.evid) if self.options.action == 'update': self.correlateByPagerDuty() self.synchronize() self.updatePagerDuty() self.updateZenoss()
class Sync(): '''''' def __init__(self, zenhost, zenuser, zenpass, pdhost, pdtoken, pduser, verbose=False): '''''' self.zenhost = zenhost self.zenuser = zenuser self.zenpass = zenpass self.pdhost = pdhost self.pdtoken = pdtoken self.pduser = pduser self.logs = [] # array to hold log messages self.verbose = verbose self.buffersize = 100 self.statusData = { "ack": { "zenoss":"Acknowledged", "pagerduty":"acknowledged", "zenaction":"acknowledge", "num":1 }, "close":{ "zenoss":"Closed", "pagerduty":"resolved", "zenaction":"close", "num":2 }, "open":{ "zenoss":"New", "pagerduty":"triggered", "zenaction":"reopen", "num":0}, } self.zenver = self.isVersion4() if self.zenver is True: #zenoss 4 changed "unacknowledge" action to "reopen" self.statusData["open"]["zenaction"] = "reopen" self.messenger = MessageHandler() self.statusDict = {} self.commonkeys = [] def mapCurrentStatus(self, evid, data, pd=False): ''' map event/incident status to local dictionary ''' ZENMAP = {'New': 'open', 'Acknowledged': 'ack', 'Closed': 'close', 'Cleared': 'close', 'Aged': 'close'} PDMAP = {'triggered': 'open', 'acknowledged': 'ack', 'resolved': 'close'} if pd is False: self.statusDict[evid]['zenoss'] = data self.statusDict[evid]["zencurrent"] = ZENMAP[data['eventStateString']] else: self.statusDict[evid]['pagerduty'] = data self.statusDict[evid]["pdcurrent"] = PDMAP[data['status']] def initZenoss(self): '''initialize connection to Zenoss''' self.zenoss = ZenossHandler(self.zenhost, self.zenuser, self.zenpass, self.verbose) self.zenoss.buffersize = self.buffersize def initPagerDuty(self): '''initialize connection to PagerDuty''' self.pagerduty = PagerDutyHandler(self.pdhost, self.pdtoken, self.verbose) self.pagerduty.buffersize = self.buffersize def initialize(self): '''initialize connection to both Zenoss and PagerDuty''' self.initZenoss() self.initPagerDuty() def isVersion4(self): '''detect Zenoss version''' from Products.ZenUtils.Version import Version if Version.parse('Zenoss ' + ZENOSS_VERSION) >= Version.parse('Zenoss 4'): return True return False def getMaintenanceWindows(self, service): """ return list of maintenance windows (ongoing) for a given service """ if self.verbose is True: self.writelog("Finding PagerDuty Maintenance Windows for %s" % service['id']) output = [] windows = self.pagerduty.getMaintenanceWindows()["maintenance_windows"] for w in windows: services = w["services"] for s in services: if s["id"] == service["id"]: beg = self.messenger.getLocalTime(w["start_time"]) fin = self.messenger.getLocalTime(w["end_time"]) start = self.messenger.getTimestamp(beg) end = self.messenger.getTimestamp(fin) now = time.time() if start <= now and now <=end: output.append(w) return output def getZenEvent(self, evid, history=False): '''''' output = self.zenoss.getEventDetails(evid) if 'result' not in output.keys(): return None if 'event' not in output['result'].keys(): return None if len(output['result']['event']) != 1: return None return output["result"]["event"][0] def getPDIncident(self, evid): '''''' incident = None output = self.pagerduty.getIncidentByKey(evid) #if 'incidents' not in output.keys(): return incident if 'incidents' in output.keys(): count = len(output['incidents']) # if more than one, take the most recent if count >= 1: max = 0 for i in output['incidents']: pagerdutytime = i["last_status_change_on"] pagerdutytime_local = self.messenger.getPagerDutyTime(pagerdutytime) pagerdutytimestamp = self.messenger.getTimestamp(pagerdutytime_local) if pagerdutytimestamp > max: incident = i max = pagerdutytimestamp if incident is not None: self.addStatusDictEntry(evid, incident, True) return incident def getZenEventDict(self, evid): ''' standardize zenoss 3.x vs. 4.x output ''' if self.verbose is True: self.writelog("Finding Zenoss event details for %s" % evid) # map of string to numeric event status STATUSMAP3X = { 0: 'New', 1: 'Acknowledged', 2: 'Closed', } STATUSMAP4X = { 'New': 0, 'Acknowledged': 1, 'Closed': 2, 'Cleared': 2, 'Aged': 2, } data = {'history': False} # try getting from current console event = self.getZenEvent(evid) if self.zenver == False: # was not pulled from history # if nothing, try looking in history if event is None: event = self.getZenEvent(evid, True) data['eventState'] = 2 # setting to close since this is in history data['history'] = True # was pulled from history if event is None: return None details = event["properties"] for d in details: data[str(d["key"])] = str(d["value"]) data['eventStateInt'] = data['eventState'] data['eventStateString'] = STATUSMAP3X[data['eventState']] data['log'] = output['log'] else: # zenoss 4.x output if event is None: return None data.update(event) data['history'] = False data['eventStateString'] = data['eventState'] data['eventStateInt'] = STATUSMAP4X[data['eventState']] self.addStatusDictEntry(evid, data) return data def addStatusDictEntry(self, evid, data, pd=False): ''' add entry to status dict for this evid ''' if evid not in self.statusDict.keys(): self.statusDict[evid] = {'target': None, 'zencurrent': None, 'pdcurrent': None, 'zenoss' : None, 'pagerduty': None,} self.mapCurrentStatus(evid, data, pd) def createIncidentDetails(self, evid, servicekey): """ Retrieve event detail from Zenoss and format it for PagerDuty incident creation """ if self.verbose is True: self.writelog("Getting incident details") data = self.getZenEventDict(evid) info = { "service_key": servicekey, "incident_key": evid, "details" : self.statusDict[evid]['zenoss'], "description" : None, } self.statusDict[evid]["target"] = "open" info["details"] = data info["description" ] = ' | '.join([str(data['device']), str(data['component']), str(data['summary'])]) return info def createPagerDutyIncident(self, evid, servicekey): """ 1) check whether the destination service is in a maintenance window. a) If so, ack the local alert in zenoss b) and add a "in maintenance window" log message to the Zenoss event details. 2) if it is not in maintenance, proceed with the event submission. as usual a) send event to pagerduty b) update Zenoss console with submit status info c) update Zenoss Event with incident details (incident URL, service URL, oncall) 3) check issues in Zenoss w/pagerduty incidents: a) if acked in Zenoss, ack in PagerDuty b) if closed in Zenoss, resolve in PagerDuty """ if self.verbose is True: self.writelog("Creating PagerDuty Incident") info = self.createIncidentDetails(evid, servicekey) # first ensure that event is open in zenoss if self.statusDict[evid]["zencurrent"] == 'open': # first find the appropriate PD service definition if self.verbose is True: self.writelog("looking for service using key %s" % servicekey) service = self.pagerduty.findService(servicekey) if service is not None: if self.verbose is True: self.writelog("found service using key %s" % servicekey) # in maintenance, so ack the zenoss alert but note the window detail if self.pagerduty.inMaintenance(service) is True: if self.verbose is True: self.writelog("service in maintenance using key %s" % servicekey) self.statusDict[evid]["target"] = "ack" mws = self.getMaintenanceWindows(service) for mw in mws: self.messenger.serviceInMaintenance(evid, "Acknowledged", service, mw, self.pagerduty.weburl) # disabled, so leave event unacked in Zenoss, but that service is disabled elif self.pagerduty.isDisabled(service) is True: if self.verbose is True: self.writelog("service disabled using key %s" % servicekey) self.messenger.serviceIsDisabled(evid, "No Incident created", service, self.pagerduty.weburl) # assuming service is enabled, create PD incident, note output in zenoss event console. else: if self.verbose is True: self.writelog("Creating incident for %s using key %s" % (servicekey, evid)) output = self.pagerduty.manageIncident(info, "trigger") if 'errors' in output.keys(): errors = output['errors'] else: errors = None self.messenger.serviceIncidentCreated(evid, service, self.pagerduty.weburl, errors) else: if self.verbose is True: self.writelog("No service found using key %s" % servicekey) self.messenger.serviceNotFound(evid, servicekey) else: # if not open in zenoss, set the target to whatever is current if self.verbose is True: self.writelog("Event id %s not open in Zenoss" % evid) self.statusDict[evid]["target"] = self.statusDict[evid]["zencurrent"] def getPDKeys(self, open=False): '''get incident ids for last N incidents (buffersize)''' pdkeys = [] # get PagerDuty incidents incidents = self.pagerduty.getIncidentList(open=open) if 'incidents' not in incidents.keys(): return pdkeys # make list of event ids for both for i in incidents["incidents"]: pdkeys.append(str(i['incident_key'])) return pdkeys def getZenKeys(self, open=False): '''get the evids for the last N events (buffersize)''' zenkeys = [] try: events = self.zenoss.getEvents(open=open)["events"] except: events = [] # zenoss 3.x compat if self.zenver is False: try: events += self.zenoss.getEvents(history=True, open=open)["events"] except: pass # make list of event ids for both for e in events: zenkeys.append(str(e['evid'])) self.writelog('Found %s open Zenoss events' % len(zenkeys)) return zenkeys def correlate(self): ''' 1) get list of open PD incidents 2) correlate with open or closed Zenoss events 3) get list of open Zenoss incidents not in 2) 4) correlate with open or closed PD incidents build dictionary of Zenoss events matched to PagerDuty incidents 1) get list of zenoss events 2) get list of pagerduty incidents based on date range of zenoss events 3) match them by evid - incident_key ''' if self.verbose is True: self.writelog("Finding correlated Zenoss events and PagerDuty incidents") # look at all open PD incidents pdkeys = self.getPDKeys(True) # find the matching PD/zenoss data for id in pdkeys: zdata = self.getZenEventDict(id) # get zenoss data if zdata is None: continue pddata = self.getPDIncident(id) if pddata is None: continue if id not in self.commonkeys: self.commonkeys.append(id) # look at all open zenoss events zenkeys = self.getZenKeys(True) # find matching PD data for id in zenkeys: # no reason to do work twice if id in self.commonkeys: self.writelog('skipping already processed id: %s' % id) continue pddata = self.getPDIncident(id) if pddata is None: continue zdata = self.getZenEventDict(id) # get zenoss data if zdata is None: continue if id not in self.commonkeys: self.commonkeys.append(id) def synchronize(self): """ update pagerduty based on Zenoss event console activities (acknowledge, resolve, etc) 4) if acked in one, ack in the other 5) if closed in one, close in the other """ self.correlate() if self.verbose is True: self.writelog("Synchronizing correlated Zenoss events and PagerDuty incidents") for id, data in self.statusDict.items(): if self.verbose is True: self.writelog('Synchronizing %s' % self.eventInfoMsg(data)) # map current states of each to statusDict self.evalStatus(data) # determine which was updated last self.findNewest(data) # update pd and zenoss self.updateStatus(data) # gather/format any log messages in pagerduty self.buildMessages(data) # update Zenoss event logs self.updateZenossMessages(data) def evalStatus(self, data): '''update the status dictionary entry''' if self.verbose is True: self.writelog("Evaluating status data: %s" % self.eventInfoMsg(data)) for status, tdata in self.statusData.items(): if tdata["zenoss"] == data['zenoss']['eventStateString']: data['zencurrent'] = status if tdata["pagerduty"] == data['pagerduty']['status']: data['pdcurrent'] = status def buildMessages(self, data): ''' build the log messages that will show in the Zenoss console''' if self.verbose is True: self.writelog("Building messages for %s" % self.eventInfoMsg(data)) # gather/format any log messages in pagerduty evid = str(data['zenoss']['evid']) pdid = str(data['pagerduty']['id']) # basic incident creation message self.messenger.incidentCreated(evid, data['pagerduty']) # any others from pagerduty logs = self.pagerduty.getIncidentDetail(pdid)["log_entries"] if self.verbose is True: self.writelog("Found %s incident logs for %s" % (len(logs), self.eventInfoMsg(data))) self.messenger.incidentLogs(evid, logs, self.pagerduty.weburl) def findNewest(self, data): '''''' if self.verbose is True: self.writelog("finding newest source for %s" % self.eventInfoMsg(data)) # event was pulled from history if data['zenoss']['history'] is True: zenosstime = data['zenoss']['deletedTime'] # otherwise just the last time it changed else: zenosstime = data['zenoss']['stateChange'] pagerdutytime = data['pagerduty']["last_status_change_on"] # both are converted to local time zenosstime_local = self.messenger.getZenossTime(zenosstime) pagerdutytime_local = self.messenger.getPagerDutyTime(pagerdutytime) if self.verbose is True: self.writelog("%s updated in Zenoss at %s and PagerDuty at %s" % (self.eventInfoMsg(data), zenosstime_local, pagerdutytime_local)) # then both are converted to integers zenosstimestamp = self.messenger.getTimestamp(zenosstime_local) pagerdutytimestamp = self.messenger.getTimestamp(pagerdutytime_local) if zenosstimestamp < pagerdutytimestamp: # pagerduty updated last data['target'] = data['pdcurrent'] newest = "PAGERDUTY" else: data['target'] = data['zencurrent'] newest = "ZENOSS" if self.verbose is True: self.writelog("%s last updated in %s with %s status" % (self.eventInfoMsg(data), newest, data['target'].upper())) def updateStatus(self, data): ''' perform updates on Zenoss and Pagerduty ''' if self.verbose is True: self.writelog("Updating status for %s" % self.eventInfoMsg(data)) # zenoss event id evid = str(data['zenoss']['evid']) # pagerduty incident id pdid = data['pagerduty']['id'] # zenoss current state zencurrent = data['zencurrent'] # pagerduty current state pdcurrent = data['pdcurrent'] # target state target = data['target'] # check Zenoss status against target if zencurrent != target: # ze:enoss 3.x does not change the status of a closed event if target == "close" and data['zenoss']['history'] is True: if self.verbose is True: self.writelog("Cannot update historical event %s" % self.eventInfoMsg(data)) else: if self.verbose is True: self.writelog("Changing Zenoss event %s status from %s to %s" % (self.eventInfoMsg(data), zencurrent.upper(), target.upper())) self.zenoss.manageEventStatus([evid], self.statusData[target]['zenaction']) # check PagerDuty status against target if pdcurrent != target: if self.verbose is True: self.writelog("Changing PagerDuty incident %s status from %s to %s" % (self.eventInfoMsg(data), pdcurrent.upper(), target.upper())) try: pdData = {"id": pdid, "status": self.statusData[target]["pagerduty"]} # test individually update = {"incidents": [pdData], "requester_id": self.pduser} self.pagerduty.updateStatus(update) except: self.writelog("%s update pagerduty failed changing %s to %s" % (self.eventInfoMsg(data), pdcurrent.upper(), target.upper())) def updateZenossStatus(self, data): """ update Zenoss event status if target is not current """ if self.verbose is True: self.writelog("Checking Zenoss status for %s" % self.eventInfoMsg(data)) # zenoss event id evid = str(data['zenoss']['evid']) current = data["zencurrent"] target = data["target"] if current != target: if self.verbose is True: self.writelog("Changing %s status to %s" % (self.eventInfoMsg(data), target.upper())) self.zenoss.manageEventStatus([evid], self.statusData[target]['zenaction']) def updateZenossMessages(self, data): """ update Zenoss event console with messages if they don't exist """ if self.verbose is True: self.writelog("Checking Zenoss messages for %s" % self.eventInfoMsg(data)) # zenoss event id evid = str(data['zenoss']['evid']) # get list of current event log messages zlogs = [] for e in data['zenoss']['log']: zlogs.append(str(e[-1])) for msg in self.messenger.messages[evid]: #self.writelog('examining message: "%s"' % msg) if msg not in zlogs: if self.verbose is True: self.writelog('Adding message to %s: "%s"' % (self.eventInfoMsg(data), msg)) self.zenoss.addEventMessage(evid, msg) def updateCreatedIssue(self): ''' update zenoss status and messages for new issue''' if self.verbose is True: self.writelog("updating created issue") for id, data in self.statusDict.items(): self.updateZenossStatus(data) self.updateZenossMessages(data) def writelog(self,msg): ''' print log message and append to list for log handling''' self.logs.append(msg) print msg def eventInfoMsg(self, data): '''convenience for log messages''' try: return '%s (%s) "%s"' % (str(data['zenoss']['evid']), data['pagerduty']['id'], data['zenoss']['summary']) except: return '%s "%s"' % (str(data['zenoss']['evid']), data['zenoss']['summary'])
def initZenoss(self): '''initialize connection to Zenoss''' self.zenoss = ZenossHandler(self.zenhost, self.zenuser, self.zenpass, self.verbose) self.zenoss.buffersize = self.buffersize