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