Below is a set of event transforms I have setup for our Cisco gear. These events use auto-clear, they translate SNMP state numbers to a more intelligable name, they do reverse DNS lookup where possible, and some other clean up/formatting. They also separate out events so like if Router1 has 3 BGP nbr state changes, they are 3 separate events and are cleared separately.
Important Note: A Lot of this is combined info from the forums and wiki with some other changes I made for our particular needs. i.e. I make no claim that these are my own scripts - they are more rolled up tips from the forums with modifications. You can search some of this and find the original authors.
Another Note: I'm a network engineer and not a developer or regular script writer so any further improvement suggestions are welcome! These do all work nicely for our needs though as is. Be careful with cut and paste as Python is very picky about indentations - so you may need to fix this.
Yet Another Note: In our email alerts I include evt.message and evt.summary as part of the message (called as %(summary)s and %(message)s in the alerting message).
This is in the admin guide - but to get this started I first created new event sub-categories as noted at the top of each transform. Then with the first sample event that comes in (configured traps on Cisco gear) I mapped it to the new category.
OSPF Neighbor State Changes ( ospfNbrStateChange trap )
Created: events/Cisco/Switch/OSPF Neighbor State Change
===============================================================
#Required for reverse lookup
from socket import gethostbyaddr
#Translate ospfNbrState number into readable format
X = evt.ospfNbrState
if X == 1 :
evt.TranslatedState = "down"
elif X == 2 :
evt.TranslatedState = "attempt"
elif X == 3 :
evt.TranslatedState = "init"
elif X == 4 :
evt.TranslatedState = "twoWay"
elif X == 5 :
evt.TranslatedState = " exchangeStart"
elif X == 6 :
evt.TranslatedState = "exchange"
elif X == 7:
evt.TranslatedState = "loading"
elif X == 8:
evt.TranslatedState = "full"
else:
evt.TranslatedState = evt.ospfNbrState
#Set summary and message - try to do reverse DNS lookup
#Also set component
try:
evt.ospfNbrName = gethostbyaddr(evt.ospfNbrRtrId)[0]
evt.summary = "OSPF Nbr state change - " + evt.ospfNbrName + " " + evt.TranslatedState
evt.message = "Neighbor " + evt.ospfNbrName + "(RID " + evt.ospfNbrRtrId + ")" + " has changed state to: " + evt.TranslatedState
evt.component = evt.ospfNbrName
except Exception, ex:
evt.exceptionString = str(ex)
evt.summary ="OSPF Nbr state change - " + evt.ospfNbrRtrId + " " + evt.TranslatedState
evt.message = "Neighbor " + evt.ospfNbrRtrId + " has changed state to: " + evt.TranslatedState
evt.component = evt.ospfNbrRtrId
#Auto-clear when state is full
if evt.TranslatedState == "full" :
evt.severity = 0
===============================================================
OSPF Interface State Change ( ospfIfStateChange trap )
Created: events/cisco/switch/ospf ifstate change transform
===============================================================
#Determine state - translate ospfIfState to readable form
X = evt.ospfIfState
if X == 1 :
evt.TranslatedState = "down"
elif X == 2 :
evt.TranslatedState = "loopback"
elif X == 3 :
evt.TranslatedState = "waiting"
elif X == 4 :
evt.TranslatedState = "pointToPoint (up)"
elif X == 5 :
evt.TranslatedState = "designatedRouter (up)"
elif X == 6 :
evt.TranslatedState = "backupDesignatedRouter (up)"
elif X == 7:
evt.TranslatedState = "otherDesignatedRouter (up)"
else:
evt.TranslateState = evt.ospfIfState
#Set summary and message
evt.summary = "OSPF If " + evt.ospfIfIpAddress + " is " + evt.TranslatedState
evt.message = "Interface ip: " + evt.ospfIfIpAddress + " has changed OSPF state to: " + evt.TranslatedState
#Set Component
evt.component = evt.ospfIfIpAddress
#Auto Clear up states
if evt.TranslatedState.endswith("(up)") :
evt.severity = 0
===============================================================
BGP Peer state changes (bgpBackwardTransition trap)
===============================================================
#Required for reverse DNS lookup
from socket import gethostbyaddr
#Translate BGP Peer state to readable format
X = evt.bgpPeerState
if X == 1 :
evt.TranslatedState = "idle"
elif X == 2 :
evt.TranslatedState = "connect"
elif X == 3 :
evt.TranslatedState = "active"
elif X == 4 :
evt.TranslatedState = "opensent"
elif X == 5 :
evt.TranslatedState = "openconfirm"
elif X == 6 :
evt.TranslatedState = "established"
else:
evt.TranslatedState = evt.bgpPeerState
#Pull out bgp peer IP and attempt a DNS reverse lookup for a name
#Then set message and summary using DNS name if possible, IP if not
for attr in dir(evt):
if attr.startswith('bgpPeerState.'):
evt.bgpPeerIp = attr.replace('bgpPeerState.', '')
try:
evt.bgpPeerName = gethostbyaddr(evt.bgpPeerIp)[0]
evt.summary = "BGP neighbor change " + evt.bgpPeerName + " (" + evt.bgpPeerIp + ")" + " is: " + evt.TranslatedState
except Exception, ex:
evt.exceptionString = str(ex)
evt.summary = "BGP neighbor change " + evt.bgpPeerIp + " is: " + evt.TranslatedState
evt.component = evt.bgpPeerIp
#Auto-clear when state is established
if evt.TranslatedState == "established" :
evt.severity = 0
===============================================================
Linkup/down traps
Created events/cisco/switch/linkdown transform:
===============================================================
#Pull interface description from Zenoss
try:
for iface in device.os.interfaces():
if evt.ifIndex == iface.ifindex:
evt.Description = iface.description
#Case where the index/interface was found but without a description
if evt.Description == "":
evt.Description = "Unknown (in Zenoss but no description found)"
#Case where the interface was not found in Zenoss - I call this New
#You can use this to supress or filter alerts for 'new' switches that have not been modeled yet
except Exception,ex:
evt.Description = "A new link not yet in Zenoss"
#Trim interface names
#I do this so TenGigabitEthernet2/1 is replaced with Ten2/1 (mobile device friendly)
interface = evt.ifDescr
if "TenGigabit" in interface:
interface = interface.replace("GigabitEthernet", "")
elif "GigabitEthernet" in interface:
interface = interface.replace("abitEthernet", "")
elif "Port-channel" in interface:
interface = interface.replace("rt-channel","")
#Set summary, message and component
evt.summary = interface + " is " + evt.locIfReason
evt.message = "This interface is connected to: " + evt.Description
evt.component = interface
===============================================================
Interface Utilization Alerts
events/perf/Interface
================================================
#Trim interface names (mobile friendly)
#ie Port-channel50 becomes Po50
interface = evt.component
if "TenGigabit" in interface:
interface = interface.replace("GigabitEthernet", "")
elif "GigabitEthernet" in interface:
interface = interface.replace("abitEthernet", "")
elif "Port-channel" in interface:
interface = interface.replace("rt-channel","")
#Transform interface usage into readable format
#This is totally stolen from the forums by the way
import re
m = re.search("threshold of [^:]+: current value ([\d\.]+)", evt.message)
Usage = (float(m.group(1))) * 8
evtKey = evt.eventKey
if evtKey == "ifHCInOctets_ifHCInOctets|high utilization":
evtNewKey = "Input"
elif evtKey == "ifHCOutOctets_ifHCOutOctets|high utilization":
evtNewKey = "Output"
if Usage > 1000000000:
Usage = Usage / 1000000000
evt.usage = "%.2f Gbps" % (Usage)
elif Usage > 1000000:
Usage = Usage / 1000000
evt.usage = "%.2f Mbps" % (Usage)
elif Usage > 1000:
Usage = Usage / 1000
evt.usage = "%.2f Kbps" % (Usage)
evt.summary = interface + " Utilization Alert"
evt.message = evtNewKey + " of " + interface + " is currently at " + evt.usage
================================================================
Firewall Traps (ASA/FWSM) (clogMessageGenerated)
I Configured "logging lists" on our firewalls to send syslog messages as traps (only the messages/level/classes I want)
Created /Events/Cisco/FW/syslog-trap
===============================================================
import re
#Set event message to syslog message
evt.message = evt.clogHistMsgText
#Get a Cisco Format ID for the event, and use it in the summary
#Also store evt.CiscoID
regexresult = re.search( r'%((ASA|FWSM)-\d+-\d+)' , evt.message)
evt.CiscoID = regexresult.group(1)
evt.summary = "Event: " + evt.CiscoID + " triggered"
#I create evt.CiscoID above and use it to take actions on certain messages
#For example to supress alerts with ID ASA-3-713123 plus "Username" in the log
if "ASA-3-713123" in evt.CiscoID and "Username" in evt.message:
evt.eventState = 2
===============================================================