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

# Source Code for Module Products.DataCollector.SnmpClient

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