Package ZenEvents :: Module zentrap
[hide private]
[frames] | no frames]

Source Code for Module ZenEvents.zentrap

  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  #! /usr/bin/env python  
 14   
 15  __doc__='''zentrap 
 16   
 17  Creates events from SNMP Traps. 
 18   
 19  $Id$ 
 20  ''' 
 21   
 22  __version__ = "$Revision$"[11:-2] 
 23   
 24  from twisted.python import threadable 
 25  threadable.init() 
 26   
 27  from Queue import Queue 
 28   
 29  import time 
 30  import socket 
 31   
 32  import Globals 
 33   
 34  from EventServer import EventServer 
 35  from Event import Event, EventHeartbeat 
 36   
 37  from ZenEventClasses import Status_Snmp 
 38  from Products.ZenModel.IpAddress import findIpAddress 
 39   
 40  from twisted.internet import reactor 
 41  from twisted.internet.protocol import DatagramProtocol 
 42  from twistedsnmp import snmpprotocol 
 43   
 44  TRAP_PORT = 162 
 45  try: 
 46      TRAP_PORT = socket.getservbyname('snmptrap', 'udp') 
 47  except socket.error: 
 48      pass 
 49   
50 -def grind(obj):
51 '''Chase an object down to its value. 52 53 Example: getting a timeticks value: 54 55 ticks = obj['value']['application_syntax']['timeticks_value'].get() 56 57 becomes: 58 59 ticks = grind(obj) 60 61 ''' 62 if hasattr(obj, 'keys'): 63 return grind(obj.values()[0]) 64 return obj.get()
65
66 -def extract(obj, path, default = None):
67 parts = path.split('/') 68 for p in parts: 69 try: 70 obj = obj[p] 71 except KeyError: 72 return default 73 return obj
74 75
76 -class ZenTrap(EventServer, snmpprotocol.SNMPProtocol):
77 'Listen for SNMP traps and turn them into events' 78 79 totalTime = 0. 80 totalEvents = 0 81 maxTime = 0. 82 83 name = 'zentrap' 84
85 - def __init__(self):
86 EventServer.__init__(self) 87 snmpprotocol.SNMPProtocol.__init__(self, self.options.trapport) 88 if self.options.useFileDescriptor is not None: 89 self.useUdpFileDescriptor(int(self.options.useFileDescriptor)) 90 else: 91 reactor.listenUDP(self.port, self)
92 93
94 - def handleTrap(self, data, addr):
95 'Traps are processed asynchronously in a thread' 96 self.q.put( (data, addr, time.time()) )
97 98
99 - def _findDevice(self, addr):
100 'Find a device by its IP address' 101 device = None 102 ipObject = findIpAddress(self.dmd, addr[0]) 103 if ipObject: 104 device = ipObject.device() 105 if not device: 106 device = self.dmd.Devices.findDevice(addr[0]) 107 if device: 108 return device.id 109 try: 110 return socket.gethostbyaddr(addr[0])[0] 111 except socket.herror: 112 pass 113 return addr[0]
114
115 - def _oid2name(self, oid):
116 'short hand to get names from oids' 117 return self.dmd.Mibs.oid2name(oid)
118
119 - def oid2name(self, oid):
120 "get oids, even if we're handed slightly wrong values" 121 oid = oid.lstrip('.') 122 name = self._oid2name(oid) 123 if not name: 124 name = self._oid2name('.'.join(oid.split('.')[:-1])) 125 if not name: 126 return oid 127 return name
128
129 - def doHandleRequest(self, data, addr, ts):
130 eventType = 'unknown' 131 result = {} 132 if data['version'].get() == 1: 133 # SNMP v2 134 pdu = data['pdu'] 135 bindings = extract(data, 'pdu/snmpV2_trap/variable_bindings', []) 136 bindings = extract(data, 'pdu/inform_request/variable_bindings', 137 bindings) 138 for binding in bindings: 139 oid = grind(binding['name']) 140 value = grind(binding['value']) 141 # SNMPv2-MIB/snmpTrapOID 142 if oid.lstrip('.') == '1.3.6.1.6.3.1.1.4.1.0': 143 eventType = self.oid2name(value) 144 result[self.oid2name(oid)] = value 145 else: 146 # SNMP v1 147 addr = grind(extract(data, 'pdu/trap/agent_addr')), addr[1] 148 enterprise = grind(extract(data, 'pdu/trap/enterprise')) 149 eventType = self.oid2name(enterprise) 150 generic = grind(extract(data, 'pdu/trap/generic_trap')) 151 specific = grind(extract(data, 'pdu/trap/specific_trap')) 152 eventType = { 0 : 'snmp_coldStart', 153 1 : 'snmp_warmStart', 154 2 : 'snmp_linkDown', 155 3 : 'snmp_linkUp', 156 4 : 'snmp_authenticationFailure', 157 5 : 'snmp_egpNeighorLoss', 158 6 : self.oid2name('%s.0.%d' % (enterprise, specific)) 159 }.get(generic, eventType + "_%d" % specific) 160 for binding in extract(data, 'pdu/trap/variable_bindings'): 161 oid = grind(binding['name']) 162 value = grind(binding['value']) 163 result[self.oid2name(oid)] = value 164 165 device = self._findDevice(addr) 166 summary = 'snmp trap %s from %s' % (eventType, device) 167 self.log.debug(summary) 168 community = data['community'].get() 169 result.setdefault('agent', 'zentrap') 170 result.setdefault('component', '') 171 result.setdefault('device', device) 172 result.setdefault('eventClassKey', eventType) 173 result.setdefault('eventGroup', 'trap') 174 result.setdefault('rcvtime', ts) 175 result.setdefault('severity', 3) 176 result.setdefault('summary', summary) 177 result.setdefault('community', community) 178 result['ipAddress'] = addr[0] 179 self.sendEvent(result) 180 181 diff = time.time() - ts 182 self.totalTime += diff 183 self.totalEvents += 1 184 self.maxTime = max(diff, self.maxTime) 185 186 if data['pdu'].has_key('inform_request'): 187 r = snmpprotocol.v2c.Response() 188 extract(r, 'pdu/response/request_id').set( 189 extract(data, 'pdu/inform_request/request_id').get()) 190 r['community'].set(data['community'].get()) 191 reactor.callFromThread(self.informResponse, r.berEncode(), addr)
192 193
194 - def informResponse(self, data, addr):
195 self.transport.socket.sendto(data, addr)
196 197
198 - def buildOptions(self):
199 EventServer.buildOptions(self) 200 self.parser.add_option('--trapport', '-t', 201 help='Listen for SNMP traps on this port rather than the default', 202 dest='trapport', type='int', default=TRAP_PORT) 203 self.parser.add_option('--useFileDescriptor', 204 dest='useFileDescriptor', 205 type='int', 206 help="Read from an existing connection rather opening a new port.", 207 default=None)
208 209 210 if __name__ == '__main__': 211 z = ZenTrap() 212 z.main() 213