Package Products :: Package DataCollector :: Module SnmpClient
[hide private]
[frames] | no frames]

Source Code for Module Products.DataCollector.SnmpClient

  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 or (at your 
  8  # option) any later version as published by the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  import sys 
 15  import logging 
 16  log = logging.getLogger("zen.SnmpClient") 
 17   
 18  from twisted.internet import reactor, error, defer 
 19  from twisted.python import failure 
 20  from twisted.internet.error import TimeoutError 
 21   
 22  from Products.ZenUtils.snmp import SnmpV1Config, SnmpV2cConfig 
 23  from Products.ZenUtils.snmp import SnmpAgentDiscoverer 
 24   
 25  from pynetsnmp.twistedsnmp import snmpprotocol, Snmpv3Error 
 26   
 27  import Globals 
 28   
 29  from Products.ZenUtils.Driver import drive 
 30   
 31  global defaultTries, defaultTimeout 
 32  defaultTries = 2 
 33  defaultTimeout = 1 
 34  defaultSnmpCommunity = 'public' 
 35   
 36  DEFAULT_MAX_OIDS_BACK = 40 
 37   
 38  from BaseClient import BaseClient 
 39   
40 -class SnmpClient(BaseClient):
41
42 - def __init__(self, hostname, ipaddr, options=None, device=None, 43 datacollector=None, plugins=[]):
44 BaseClient.__init__(self, device, datacollector) 45 global defaultTries, defaultTimeout 46 self.hostname = hostname 47 self.device = device 48 self.options = options 49 self.datacollector = datacollector 50 self.plugins = plugins 51 52 self._getdata = {} 53 self._tabledata = {} 54 55 from Products.ZenHub.services.PerformanceConfig import SnmpConnInfo 56 self.connInfo = SnmpConnInfo(device) 57 self.proxy = None
58
59 - def initSnmpProxy(self):
60 if self.proxy is not None: self.proxy.close() 61 srcport = snmpprotocol.port() 62 self.proxy = self.connInfo.createSession(srcport.protocol) 63 self.proxy.open()
64
65 - def run(self):
66 """Start snmp collection. 67 """ 68 log.debug("Starting %s", self.connInfo.summary()) 69 self.initSnmpProxy() 70 drive(self.doRun).addBoth(self.clientFinished)
71 72 73 # FIXME: cleanup --force option #2660
74 - def checkCiscoChange(self, driver):
75 """Check to see if a cisco box has changed. 76 """ 77 device = self.device 78 yield self.proxy.get(['.1.3.6.1.4.1.9.9.43.1.1.1.0']) 79 lastpolluptime = device.getLastPollSnmpUpTime() 80 log.debug("lastpolluptime = %s", lastpolluptime) 81 result = True 82 try: 83 lastchange = driver.next().values()[0] 84 log.debug("lastchange = %s", lastchange) 85 if lastchange <= lastpolluptime: 86 log.info("skipping cisco device %s no change detected", 87 device.id) 88 result = False 89 else: 90 device.setLastPollSnmpUpTime(lastchange) 91 except Exception: 92 pass 93 yield defer.succeed(result)
94 95
96 - def doRun(self, driver):
97 # test snmp connectivity 98 log.debug("Testing SNMP configuration") 99 yield self.proxy.walk('.1.3') 100 try: 101 driver.next() 102 except TimeoutError, ex: 103 log.info("Device timed out: " + self.connInfo.summary()) 104 if self.options.discoverCommunity: 105 yield self.findSnmpCommunity() 106 snmp_config = driver.next() 107 if not snmp_config: 108 log.warn( 109 'Failed to rediscover the SNMP connection info for %s', 110 self.device.manageIp) 111 return 112 if snmp_config.version: 113 self.connInfo.zSnmpVer = snmp_config.version 114 if snmp_config.port: 115 self.connInfo.zSnmpPort = snmp_config.port 116 if snmp_config.community: 117 self.connInfo.zSnmpCommunity = snmp_config.community 118 self.connInfo.changed = True 119 self.initSnmpProxy() 120 else: 121 return 122 except Snmpv3Error, ex: 123 log.info("Cannot connect to SNMP agent: {0}".format(self.connInfo.summary())) 124 return 125 except Exception, ex: 126 log.exception("Unable to talk: " + self.connInfo.summary()) 127 return 128 129 changed = True 130 # FIXME: cleanup --force option #2660 131 if not self.options.force and self.device.snmpOid.startswith(".1.3.6.1.4.1.9"): 132 yield drive(self.checkCiscoChange) 133 changed = driver.next() 134 if changed: 135 yield drive(self.collect)
136
137 - def findSnmpCommunity(self):
138 def inner(driver): 139 """ 140 Twisted driver class to iterate through devices 141 142 @param driver: Zenoss driver 143 @type driver: Zenoss driver 144 @return: successful result is a list of IPs that were added 145 @rtype: Twisted deferred 146 """ 147 log.info("Rediscovering SNMP connection info for %s", 148 self.device.id) 149 150 communities = list(self.device.zSnmpCommunities) 151 communities.reverse() 152 153 configs = [] 154 weight = 0 155 for community in communities: 156 for port in self.device.zSnmpPorts: 157 weight+=1 158 port = int(port) 159 configs.append(SnmpV1Config( 160 self.device.manageIp, weight=weight, 161 port=port, 162 timeout=self.connInfo.zSnmpTimeout, 163 retries=self.connInfo.zSnmpTries, 164 community=community)) 165 configs.append(SnmpV2cConfig( 166 self.device.manageIp, weight=weight+1000, port=port, 167 timeout=self.connInfo.zSnmpTimeout, 168 retries=self.connInfo.zSnmpTries, 169 community=community)) 170 171 yield SnmpAgentDiscoverer().findBestConfig(configs) 172 driver.next()
173 return drive(inner)
174 175
176 - def collect(self, driver):
177 maxOidsPerRequest = getattr(self.device, 'zMaxOIDPerRequest', DEFAULT_MAX_OIDS_BACK) 178 log.debug("Using a max of %s OIDs per request", maxOidsPerRequest) 179 for plugin in self.plugins: 180 try: 181 log.debug('running %s', plugin) 182 pname = plugin.name() 183 self._tabledata[pname] = {} 184 log.debug("sending queries for plugin %s", pname) 185 if plugin.snmpGetMap: 186 results = {} 187 for oid in plugin.snmpGetMap.getoids(): 188 yield self.proxy.get([oid]) 189 results.update(driver.next()) 190 self._getdata[pname] = results 191 for tmap in plugin.snmpGetTableMaps: 192 rowSize = len(tmap.getoids()) 193 maxRepetitions = max(maxOidsPerRequest / rowSize, 1) 194 yield self.proxy.getTable(tmap.getoids(), 195 maxRepetitions=maxRepetitions, 196 limit=sys.maxint) 197 self._tabledata[pname][tmap] = driver.next() 198 except Exception, ex: 199 if not isinstance( ex, error.TimeoutError ): 200 log.exception("device %s plugin %s unexpected error", 201 self.hostname, pname)
202 203
204 - def getResults(self):
205 """Return data for this client in the form 206 ((plugin, (getdata, tabledata),) 207 getdata = {'.1.2.4.5':"value",} 208 tabledata = {tableMap : {'.1.2.3.4' : {'.1.2.3.4.1': "value",...}}} 209 """ 210 data = [] 211 for plugin in self.plugins: 212 pname = plugin.name() 213 getdata = self._getdata.get(pname,{}) 214 tabledata = self._tabledata.get(pname,{}) 215 if getdata or tabledata: 216 data.append((plugin, (getdata, tabledata))) 217 return data
218 - def clientFinished(self, result):
219 log.info("snmp client finished collection for %s" % self.hostname) 220 if isinstance(result, failure.Failure): 221 from twisted.internet import error 222 if isinstance(result.value, error.TimeoutError): 223 log.warning("Device %s timed out: are " 224 "your SNMP settings correct?", self.hostname) 225 elif isinstance(result.value, Snmpv3Error): 226 log.warning("Connection to device {0.hostname} failed: {1.value.message}".format(self, result)) 227 else: 228 log.exception("Device %s had an error: %s",self.hostname,result) 229 self.proxy.close() 230 """tell the datacollector that we are all done""" 231 if self.datacollector: 232 self.datacollector.clientFinished(self) 233 else: 234 reactor.stop()
235
236 - def stop(self):
237 self.proxy.close()
238
239 -def buildOptions(parser=None, usage=None):
240 "build options list that both telnet and ssh use" 241 if not usage: 242 usage = "%prog [options] hostname[:port] oids" 243 if not parser: 244 from optparse import OptionParser 245 parser = OptionParser(usage=usage) 246 247 parser.add_option('--snmpCommunity', 248 dest='snmpCommunity', 249 default=defaultSnmpCommunity, 250 help='Snmp Community string')
251 252 253 if __name__ == "__main__": 254 import pprint 255 logging.basicConfig() 256 log = logging.getLogger() 257 log.setLevel(20) 258 import sys 259 sys.path.append("plugins") 260 from plugins.zenoss.snmp.InterfaceMap import InterfaceMap 261 ifmap = InterfaceMap() 262 sc = SnmpClient("gate.confmon.loc", community="zentinel", plugins=[ifmap,]) 263 reactor.run() 264 pprint.pprint(sc.getResults()) 265