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

# Source Code for Module Products.DataCollector.zendisc

      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  __doc__ = """zendisc
     15  Scan networks and routes looking for devices to add to the ZODB
     16  """
     17  import socket
     18
     19  # IMPORTANT! The import of the pysamba.twisted.reactor module should come before
     20  # any other libraries that might possibly use twisted. This will ensure that
     21  # the proper WmiReactor is installed before anyone else grabs a reference to
     22  # the wrong reactor.
     23  import pysamba.twisted.reactor
     24
     25  from ipaddr import IPAddress
     26
     27  # Zenoss custom ICMP library
     28  from icmpecho.Ping import Ping4, Ping6
     29
     30  import Globals
     31  from optparse import SUPPRESS_HELP
     32
     33  from Products.DataCollector.zenmodeler import ZenModeler
     34  from Products.ZenUtils.Exceptions import ZentinelException
     35  from Products.ZenUtils.Utils import unused
     36  from Products.ZenUtils.Driver import drive
     37  from Products.ZenUtils.IpUtil import asyncNameLookup, isip, parse_iprange, \
     38                                       getHostByName, ipunwrap
     39  from Products.ZenUtils.NJobs import NJobs
     40  from Products.ZenUtils.snmp import SnmpV1Config, SnmpV2cConfig
     41  from Products.ZenUtils.snmp import SnmpAgentDiscoverer
     42  from Products.ZenModel.Exceptions import NoIPAddress
     43  from Products.ZenEvents.ZenEventClasses import Status_Snmp
     44  from Products.ZenEvents.Event import Info
     45  from Products.ZenStatus.PingService import PingService
     46  from Products.ZenHub.PBDaemon import FakeRemote, PBDaemon
     47  from Products.ZenHub.services  import DiscoverService, ModelerService
     48  unused(DiscoverService, ModelerService) # for pb
     49
     50
     51
     52  from twisted.internet.defer import succeed
     53  from twisted.python.failure import Failure
     54  from twisted.internet import reactor
     55  from twisted.names.error import DNSNameError
     56
     57
     58 -class ZenDisc(ZenModeler):
     59      """
     60      Scan networks and routes looking for devices to add to the ZODB
     61      """
     62
     63      initialServices = PBDaemon.initialServices + ['DiscoverService']
     64      name = 'zendisc'
     65      scanned = 0
     66
     67 -    def __init__(self, single=True ):
     68          """
     69          Initalizer
     70
     71          @param single: collect from a single device?
     72          @type single: boolean
     73          """
     74          ZenModeler.__init__(self, single )
     75          self.discovered = []
     76
     77          # pyraw inserts IPV4_SOCKET and IPV6_SOCKET globals
     78          if IPV4_SOCKET is None:
     79              self._pinger4 = None
     80          else:
     81              protocol = Ping4(IPV4_SOCKET)
     82              self._pinger4 = PingService(protocol,
     83                                          timeout=self.options.timeout,
     84                                          defaultTries=self.options.tries)
     85
     86          if IPV6_SOCKET is None:
     87              self._pinger6 = None
     88          else:
     89              protocol = Ping6(IPV6_SOCKET)
     90              self._pinger6 = PingService(protocol,
     91                                          timeout=self.options.timeout,
     92                                          defaultTries=self.options.tries)
     93
     94 -    def ping(self, ip):
     95          """
     96          Given an IP address, return a deferred that pings the address.
     97          """
     98          self.log.debug("Using ipaddr module to convert %s" % ip)
     99          ipObj = IPAddress(ip)
    100
    101          if ipObj.version == 6:
    102              if self._pinger6 is None:
    103                  retval = Failure()
    104              else:
    105                  retval = self._pinger6.ping(ip)
    106          else:
    107              if self._pinger4 is None:
    108                  retval = Failure()
    109              else:
    110                  retval = self._pinger4.ping(ip)
    111
    112          return retval
    113
    114 -    def config(self):
    115          """
    116          Get the DiscoverService
    117
    118          @return: a DiscoverService from zenhub
    119          @rtype: function
    120          """
    121          return self.services.get('DiscoverService', FakeRemote())
    122
    123
    124 -    def discoverIps(self, nets):
    125          """
    126          Ping all ips, create entries in the network if necessary.
    127
    128          @param nets: list of networks to discover
    129          @type nets: list
    130          @return: successful result is a list of IPs that were added
    131          @rtype: Twisted deferred
    132          """
    133          def inner(driver):
    134              """
    135              Twisted driver class to iterate through devices
    136
    137              @param driver: Zenoss driver
    138              @type driver: Zenoss driver
    139              @return: successful result is a list of IPs that were added
    140              @rtype: Twisted deferred
    141              """
    142              ips = []
    143              goodCount = 0
    144              # it would be nice to interleave ping/discover
    145              for net in nets:
    146                  if self.options.subnets and len(net.children()) > 0:
    147                      continue
    148                  if not getattr(net, "zAutoDiscover", False):
    149                      self.log.info(
    150                          "Skipping network %s because zAutoDiscover is False"
    151                          % net.getNetworkName())
    152                      continue
    153                  self.log.info("Discover network '%s'", net.getNetworkName())
    154                  yield NJobs(self.options.chunkSize,
    155                              self.ping,
    156                              net.fullIpList()).start()
    157                  results = driver.next()
    158                  goodips = [
    159                      v.ipaddr for v in results if not isinstance(v, Failure)]
    160                  badips = [
    161                      v.value.ipaddr for v in results if isinstance(v, Failure)]
    162                  goodCount += len(goodips)
    163                  self.log.debug("Got %d good IPs and %d bad IPs",
    164                                 len(goodips), len(badips))
    165                  yield self.config().callRemote('pingStatus',
    166                                                 net,
    167                                                 goodips,
    168                                                 badips,
    169                                                 self.options.resetPtr,
    170                                                 self.options.addInactive)
    171                  ips += driver.next()
    172                  self.log.info("Discovered %s active ips", goodCount)
    173              # make sure this is the return result for the driver
    174              yield succeed(ips)
    175              driver.next()
    176
    177          d = drive(inner)
    178          return d
    179
    180 -    def discoverRanges(self, driver):
    181          """
    182          Ping all IPs in the range and create devices for the ones that come
    183          back.
    184
    185          @param ranges: list of ranges to discover
    186          @type ranges: list
    187          """
    188          if isinstance(self.options.range, basestring):
    189              self.options.range = [self.options.range]
    190          # in case someone uses 10.0.0.0-5,192.168.0.1-5 instead of
    191          # --range 10.0.0.0-5 --range 192.168.0.1-5
    192          if (isinstance(self.options.range, list) and
    193              self.options.range[0].find(",") > -1):
    194              self.options.range = [n.strip() for n in
    195                                    self.options.range[0].split(',')]
    196          ips = []
    197          goodCount = 0
    198          for iprange in self.options.range:
    199              # Parse to find ips included
    200              ips.extend(parse_iprange(iprange))
    201          yield NJobs(self.options.chunkSize,
    202                      self.ping,
    203                      ips).start()
    204          results = driver.next()
    205          goodips = [v.ipaddr for v in results if not isinstance(v, Failure)]
    206          badips = [v.value.ipaddr for v in results if isinstance(v, Failure)]
    207          goodCount += len(goodips)
    208          self.log.debug("Got %d good IPs and %d bad IPs",
    209                         len(goodips), len(badips))
    210          yield self.discoverDevices(goodips)
    211          yield succeed("Discovered %d active IPs" % goodCount)
    212          driver.next()
    213
    214
    215 -    def discoverRouters(self, rootdev, seenips=None):
    216          """
    217          Discover all default routers based on DMD configuration.
    218
    219          @param rootdev: device root in DMD
    220          @type rootdev: device class
    221          @param seenips: list of IP addresses
    222          @type seenips: list of strings
    223          @return: Twisted/Zenoss Python iterable
    224          @rtype: Python iterable
    225          """
    226          if not seenips:
    227              seenips = []
    228
    229          def inner(driver):
    230              """
    231              Twisted driver class to iterate through devices
    232
    233              @param driver: Zenoss driver
    234              @type driver: Zenoss driver
    235              @return: successful result is a list of IPs that were added
    236              @rtype: Twisted deferred
    237              """
    238              yield self.config().callRemote('followNextHopIps', rootdev.id)
    239              for ip in driver.next():
    240                  if ip in seenips:
    241                      continue
    242                  self.log.info("device '%s' next hop '%s'", rootdev.id, ip)
    243                  seenips.append(ip)
    244                  yield self.discoverDevice(ip, devicepath="/Network/Router")
    245                  router = driver.next()
    246                  if not router:
    247                      continue
    248                  yield self.discoverRouters(router, seenips)
    249                  driver.next()
    250
    251          return drive(inner)
    252
    253
    254 -    def sendDiscoveredEvent(self, ip, dev=None, sev=2):
    255          """
    256          Send a 'device discovered' event through zenhub
    257
    258          @param ip: IP addresses
    259          @type ip: strings
    260          @param dev: remote device name
    261          @type dev: device object
    262          @param sev: severity
    263          @type sev: integer
    264          """
    265          devname = comp = ip
    266          if dev:
    267              devname = dev.id
    268          msg = "'Discovered device name '%s' for ip '%s'" % (devname, ip)
    269          evt = dict(device=devname,ipAddress=ip,eventKey=ip,
    270                     component=comp,eventClass=Status_Snmp,
    271                     summary=msg, severity=sev,
    272                     agent="Discover")
    273          self.sendEvent(evt)
    274
    275
    276 -    def discoverDevices(self,
    277                          ips,
    278                          devicepath="/Discovered",
    279                          prodState=1000):
    280          """
    281          Discover devices by active ips that are not associated with a device.
    282
    283          @param ips: list of IP addresses
    284          @type ips: list of strings
    285          @param devicepath: where in the DMD to put any discovered devices
    286          @type devicepath: string
    287          @param prodState: production state (see Admin Guide for a description)
    288          @type prodState: integer
    289          @return: Twisted/Zenoss Python iterable
    290          @rtype: Python iterable
    291          """
    292          def discoverDevice(ip):
    293              """
    294              Discover a particular device
    295              NB: Wrapper around self.discoverDevice()
    296
    297              @param ip: IP address
    298              @type ip: string
    299              @return: Twisted/Zenoss Python iterable
    300              @rtype: Python iterable
    301              """
    302              return self.discoverDevice(ip, devicepath, prodState)
    303
    304          return NJobs(self.options.parallel, discoverDevice, ips).start()
    305
    306
    307 -    def findRemoteDeviceInfo(self, ip, devicePath, deviceSnmpCommunities=None):
    308          """
    309          Scan a device for ways of naming it: PTR DNS record or a SNMP name
    310
    311          @param ip: IP address
    312          @type ip: string
    313          @param devicePath: where in the DMD to put any discovered devices
    314          @type devicePath: string
    315          @param deviceSnmpCommunities: Optional list of SNMP community strings
    316              to try, overriding those set on the device class
    317          @type deviceSnmpCommunities: list
    318          @return: result is None or a tuple containing
    319              (community, port, version, snmp name)
    320          @rtype: deferred: Twisted deferred
    321          """
    322          from pynetsnmp.twistedsnmp import AgentProxy
    323
    324          def inner(driver):
    325              """
    326              Twisted driver class to iterate through devices
    327
    328              @param driver: Zenoss driver
    329              @type driver: Zenoss driver
    330              @return: successful result is a list of IPs that were added
    331              @rtype: Twisted deferred
    332              """
    333              self.log.debug("findRemoteDeviceInfo.inner: Doing SNMP lookup on device %s", ip)
    334              yield self.config().callRemote('getSnmpConfig', devicePath)
    335              communities, port, version, timeout, retries = driver.next()
    336              self.log.debug("findRemoteDeviceInfo.inner: override acquired community strings")
    337              # Override the device class communities with the ones set on
    338              # this device, if they exist
    339              if deviceSnmpCommunities is not None:
    340                  communities = deviceSnmpCommunities
    341
    342              # Reverse the communities so that ones earlier in the list have a
    343              # higher weight.
    344              communities.reverse()
    345
    346              configs = []
    347              for i, community in enumerate(communities):
    348                  configs.append(SnmpV1Config(
    349                      ip, weight=i, port=port, timeout=timeout,
    350                      retries=retries, community=community))
    351                  configs.append(SnmpV2cConfig(
    352                      ip, weight=i+100, port=port, timeout=timeout,
    353                      retries=retries, community=community))
    354
    355              yield SnmpAgentDiscoverer().findBestConfig(configs)
    356              driver.next()
    357              self.log.debug("Finished SNMP lookup on device %s", ip)
    358
    359          return drive(inner)
    360
    361
    362 -    def discoverDevice(self, ip, devicepath="/Discovered", prodState=1000):
    363          """
    364          Discover a device based on its IP address.
    365
    366          @param ip: IP address
    367          @type ip: string
    368          @param devicepath: where in the DMD to put any discovered devices
    369          @type devicepath: string
    370          @param prodState: production state (see Admin Guide for a description)
    371          @type prodState: integer
    372          @return: Twisted/Zenoss Python iterable
    373          @rtype: Python iterable
    374          """
    375          self.scanned += 1
    376          if self.options.maxdevices:
    377              if self.scanned >= self.options.maxdevices:
    378                  self.log.info("Limit of %d devices reached" %
    379                                self.options.maxdevices)
    380                  return succeed(None)
    381
    382          def inner(driver):
    383              """
    384              Twisted driver class to iterate through devices
    385
    386              @param driver: Zenoss driver
    387              @type driver: Zenoss driver
    388              @return: successful result is a list of IPs that were added
    389              @rtype: Twisted deferred
    390              @todo: modularize this function (130+ lines is ridiculous)
    391              """
    392              try:
    393                  kw = dict(deviceName=ip,
    394                            discoverProto=None,
    395                            devicePath=devicepath,
    396                            performanceMonitor=self.options.monitor)
    397
    398                  # If zProperties are set via a job, get them and pass them in
    399                  if self.options.job:
    400                      yield self.config().callRemote('getJobProperties',
    401                                                     self.options.job)
    402                      job_props = driver.next()
    403                      if job_props is not None:
    404                          # grab zProperties from Job
    405                          kw['zProperties'] = job_props.get('zProperties', {})
    406                          # grab other Device properties from jobs
    407                          #deviceProps = job_props.get('deviceProps', {})
    408                          #kw.update(deviceProps)
    409                          #@FIXME we are not getting deviceProps, check calling
    410                          # chain for clues. twisted upgrade heartburn perhaps?
    411
    412                  snmpDeviceInfo = None
    413                  # if we are using SNMP, lookup the device SNMP info and use the
    414                  # name defined there for deviceName
    415                  if not self.options.nosnmp:
    416                      self.log.debug("Scanning device with address %s", ip)
    417                      snmpCommunities = kw.get('zProperties', {}).get(
    418                          'zSnmpCommunities', None)
    419                      yield self.findRemoteDeviceInfo(ip, devicepath,
    420                                                      snmpCommunities)
    421                      snmp_config = driver.next()
    422                      if snmp_config:
    423                          if snmp_config.sysName:
    424                              kw['deviceName'] = snmp_config.sysName
    425
    426                          if snmp_config.version:
    427                              kw['zSnmpVer'] = snmp_config.version
    428
    429                          if snmp_config.port:
    430                              kw['zSnmpPort'] = snmp_config.port
    431
    432                          if snmp_config.community:
    433                              kw['zSnmpCommunity'] = snmp_config.community
    434
    435                      # if we are using SNMP, did not find any snmp info,
    436                      # and we are in strict discovery mode, do not
    437                      # create a device
    438                      elif self.options.zSnmpStrictDiscovery:
    439                          self.log.info('zSnmpStrictDiscovery is True.  ' +
    440                                        'Not creating device for %s.'
    441                                        % ip )
    442                          return
    443
    444                  # RULES FOR DEVICE NAMING:
    445                  # 1. If zPreferSnmpNaming is true:
    446                  #        If snmp name is returned, use snmp name. Otherwise,
    447                  #        use the passed device name.  If no device name was passed,
    448                  #        do a dns lookup on the ip.
    449                  # 2. If zPreferSnmpNaming is false:
    450                  #        If we are discovering a single device and a name is
    451                  #        passed in instead of an IP, use the passed-in name.
    452                  #        Otherwise, do a dns lookup on the ip.
    453                  if self.options.zPreferSnmpNaming and \
    454                     not isip( kw['deviceName'] ):
    455                      # In this case, we want to keep kw['deviceName'] as-is,
    456                      # because it is what we got from snmp
    457                      pass
    458                  elif self.options.device and not isip(self.options.device):
    459                      kw['deviceName'] = self.options.device
    460                  else:
    461                      # An IP was passed in so we do a reverse lookup on it to get
    462                      # deviceName
    463                      yield asyncNameLookup(ip)
    464                      try:
    465                          kw.update(dict(deviceName=driver.next()))
    466                      except Exception, ex:
    467                          self.log.debug("Failed to lookup %s (%s)" % (ip, ex))
    468
    469                  # If it's discovering a particular device,
    470                  # ignore zAutoDiscover limitations
    471                  forceDiscovery = bool(self.options.device)
    472
    473
    474                  # now create the device by calling zenhub
    475                  yield self.config().callRemote('createDevice', ipunwrap(ip),
    476                                     force=forceDiscovery, **kw)
    477
    478                  result = driver.next()
    479                  self.log.debug("ZenDisc.discoverDevice.inner: got result from remote_createDevice")
    480                  if isinstance(result, Failure):
    481                      raise ZentinelException(result.value)
    482                  dev, created = result
    483
    484                  # if no device came back from createDevice we assume that it
    485                  # was told to not auto-discover the device.  This seems very
    486                  # dubious to me! -EAD
    487                  if not dev:
    488                      self.log.info("IP '%s' on no auto-discover, skipping",ip)
    489                      return
    490                  else:
    491                      # A device came back and it already existed.
    492                      if not created and not dev.temp_device:
    493                          # if we shouldn't remodel skip the device by returning
    494                          # at the end of this block
    495                          if not self.options.remodel:
    496                              self.log.info("Found IP '%s' on device '%s';"
    497                                            " skipping discovery", ip, dev.id)
    498                              if self.options.device:
    499                                  self.setExitCode(3)
    500                              yield succeed(dev)
    501                              driver.next()
    502                              return
    503                          else:
    504                          # we continue on to model the device.
    505                              self.log.info("IP '%s' on device '%s' remodel",
    506                                            ip, dev.id)
    507                      self.sendDiscoveredEvent(ip, dev)
    508
    509                  # use the auto-allocate flag to change the device class
    510                  # FIXME - this does not currently work
    511                  newPath = self.autoAllocate(dev)
    512                  if newPath:
    513                      yield self.config().callRemote('moveDevice', dev.id,
    514                                                     newPath)
    515                      driver.next()
    516
    517                  # the device that we found/created or that should be remodeled
    518                  # is added to the list of devices to be modeled later
    519                  if not self.options.nosnmp:
    520                      self.discovered.append(dev.id)
    521                  yield succeed(dev)
    522                  driver.next()
    523              except ZentinelException, e:
    524                  self.log.exception(e)
    525                  evt = dict(device=ip,
    526                             component=ip,
    527                             ipAddress=ip,
    528                             eventKey=ip,
    529                             eventClass=Status_Snmp,
    530                             summary=str(e),
    531                             severity=Info,
    532                             agent="Discover")
    533                  if self.options.snmpMissing:
    534                      self.sendEvent(evt)
    535              except Exception, e:
    536                  self.log.exception("Failed device discovery for '%s'", ip)
    537
    538              else:
    539                  yield self.config().callRemote('succeedDiscovery', dev.id)
    540                  driver.next()
    541                  #device needs to be the last thing yielded so that
    542                  #calling methods will get the deviceproxy
    543                  yield succeed(dev)
    544                  driver.next()
    545
    546              self.log.debug("Finished scanning device with address %s", ip)
    547
    548          return drive(inner)
    549
    550
    551 -    def collectNet(self, driver):
    552          """
    553          Twisted driver class to iterate through networks
    554
    555          @param driver: Zenoss driver
    556          @type driver: Zenoss driver
    557          @return: successful result is a list of IPs that were added
    558          @rtype: Twisted deferred
    559          """
    560
    561          # net option from the config file is a string
    562          if isinstance(self.options.net, basestring):
    563              self.options.net = [self.options.net]
    564          # in case someone uses 10.0.0.0,192.168.0.1 instead of
    565          # --net 10.0.0.0 --net 192.168.0.1
    566          if isinstance(self.options.net, (list,tuple)) and ',' in self.options.net[0]:
    567              self.options.net = [
    568                  n.strip() for n in self.options.net[0].split(',')
    569                  ]
    570          count = 0
    571          devices = []
    572          if not self.options.net:
    573              yield self.config().callRemote('getDefaultNetworks')
    574              self.options.net = driver.next()
    575
    576          if not self.options.net:
    577              self.log.warning("No networks configured")
    578              return
    579
    580          for net in self.options.net:
    581              try:
    582                  yield self.config().callRemote('getNetworks',
    583                                                 net,
    584                                                 self.options.subnets)
    585                  nets = driver.next()
    586                  if not nets:
    587                      self.log.warning("No networks found for %s" % (net,))
    588                      continue
    589                  yield self.discoverIps(nets)
    590                  ips = driver.next()
    591                  devices += ips
    592                  count += len(ips)
    593              except Exception, ex:
    594                  self.log.exception("Error performing net discovery on %s", ex)
    595          def discoverDevice(ip):
    596              """
    597              Discover a particular device
    598              NB: Wrapper around self.discoverDevice()
    599
    600              @param ip: IP address
    601              @type ip: string
    602              @return: Twisted/Zenoss Python iterable
    603              @rtype: Python iterable
    604              """
    605              return self.discoverDevice(ip,
    606                                         self.options.deviceclass,
    607                                         self.options.productionState)
    608          yield NJobs(self.options.parallel, discoverDevice, devices).start()
    609          yield succeed("Discovered %d devices" % count)
    610          driver.next()
    611
    612
    613 -    def printResults(self, results):
    614          """
    615          Display the results that we've obtained
    616
    617          @param results: what we've discovered
    618          @type results: string
    619          """
    620          if isinstance(results, Failure):
    621              self.log.error("Error: %s", results)
    622          else:
    623              self.log.info("Result: %s", results)
    624          self.main()
    625
    626
    627 -    def createDevice(self, driver):
    628          """
    629          Add a device to the system by name or IP.
    630
    631          @param driver: driver object
    632          @type driver: Twisted/Zenoss object
    633          @return: Twisted deferred
    634          @rtype: Twisted deferred
    635          """
    636          deviceName = self.options.device
    637          self.log.info("Looking for %s" % deviceName)
    638          ip = None
    639          if isip(ipunwrap(deviceName)):
    640              ip = ipunwrap(deviceName)
    641          else:
    642              try:
    643                  # FIXME ZenUtils.IpUtil.asyncIpLookup is probably a better tool
    644                  # for this, but it hasn't been tested, so it's for another day
    645                  self.log.debug("getHostByName")
    646                  ip = getHostByName(deviceName)
    647              except socket.error:
    648                  ip = ""
    649          if not ip:
    650              raise NoIPAddress("No IP found for name %s" % deviceName)
    651          else:
    652              self.log.debug("Found IP %s for device %s" % (ip, deviceName))
    653              yield self.config().callRemote('getDeviceConfig', [deviceName])
    654              me, = driver.next() or [None]
    655              if not me or me.temp_device or self.options.remodel:
    656                  yield self.discoverDevice(ip,
    657                                       devicepath=self.options.deviceclass,
    658                                       prodState=self.options.productionState)
    659                  yield succeed("Discovered device %s." % deviceName)
    660                  driver.next()
    661
    662
    663 -    def walkDiscovery(self, driver):
    664          """
    665          Python iterable to go through discovery
    666
    667          @return: Twisted deferred
    668          @rtype: Twisted deferred
    669          """
    670          myname = socket.getfqdn()
    671          self.log.debug("My hostname = %s", myname)
    672          myip = None
    673          try:
    674              myip = getHostByName(myname)
    675              self.log.debug("My IP address = %s", myip)
    676          except (socket.error, DNSNameError):
    677              raise SystemExit("Failed lookup of my IP for name %s", myname)
    678
    679          yield self.config().callRemote('getDeviceConfig', [myname])
    680          me, = driver.next() or [None]
    681          if not me or self.options.remodel:
    682              yield self.discoverDevice(myip,
    683                                        devicepath=self.options.deviceclass,
    684                                        prodState=self.options.productionState)
    685              me = driver.next()
    686          if not me:
    687              raise SystemExit("SNMP discover of self '%s' failed" % myname)
    688          if not myip:
    689              myip = me.manageIp
    690          if not myip:
    691              raise SystemExit("Can't find my IP for name %s" % myname)
    692
    693          yield self.discoverRouters(me, [myip])
    694
    695          driver.next()
    696          if self.options.routersonly:
    697              self.log.info("Only routers discovered, skipping ping sweep.")
    698          else:
    699              yield self.config().callRemote('getSubNetworks')
    700              yield self.discoverIps(driver.next())
    701              ips = driver.next()
    702              if not self.options.nosnmp:
    703                  yield self.discoverDevices(ips)
    704                  driver.next()
    705
    706
    707 -    def getDeviceList(self):
    708          """
    709          Our device list comes from our list of newly discovered devices
    710
    711          @return: list of discovered devices
    712          @rtype: Twisted succeed() object
    713          """
    714          return succeed(self.discovered)
    715
    716
    717 -    def connected(self):
    718          """
    719          Called by Twisted once a connection has been established.
    720          """
    721          d = self.configure()
    722          d.addCallback(self.startDiscovery)
    723          d.addErrback(self.reportError)
    724
    725
    726
    727 -    def startDiscovery(self, data):
    728          if self.options.walk:
    729              d = drive(self.walkDiscovery)
    730
    731          elif self.options.device:
    732              d = drive(self.createDevice)
    733
    734          elif self.options.range:
    735              d = drive(self.discoverRanges)
    736
    737          else:
    738              d = drive(self.collectNet)
    739
    740          d.addBoth(self.printResults)
    741
    742
    743 -    def autoAllocate(self, device=None):
    744          """
    745          Execute a script that will auto allocate devices into their
    746          Device Classes
    747
    748          @param device: device object
    749          @type device: device object
    750          @return: Device class path to put the new device
    751          @rtype: string
    752          @todo: make it actually work
    753          """
    754          self.log.debug("trying to auto-allocate device %s" % device.id )
    755          if not device:
    756              return
    757          script = getattr(device, "zAutoAllocateScript", None)
    758          self.log.debug("no auto-allocation script found")
    759          if script:
    760              script = '\n'.join(script)
    761              self.log.debug("using script\n%s" % script)
    762              try:
    763                  compile(script, "zAutoAllocateScript", "exec")
    764              except:
    765                  self.log.error("zAutoAllocateScript contains error")
    766                  return
    767              vars = {'dev': device, 'log': self.log}
    768              try:
    769                  exec(script, vars)
    770              except:
    771                  self.log.error(
    772                      "error executing zAutoAllocateScript:\n%s" % script)
    773              return vars.get('devicePath', None)
    774          return
    775
    776
    777 -    def buildOptions(self):
    778          """
    779          Command-line option builder for optparse
    780          """
    781          ZenModeler.buildOptions(self)
    782          self.parser.add_option('--net', dest='net', action="append",
    783                      help="Discover all device on this network")
    784          self.parser.add_option('--range', dest='range', action='append',
    785                      help="Discover all IPs in this range")
    786          self.parser.add_option('--deviceclass', dest='deviceclass',
    787                      default="/Discovered",
    788                      help="Default device class for discovered devices")
    789          self.parser.add_option('--prod_state', dest='productionState',
    790                      default=1000,
    791                      help="Initial production state for discovered devices")
    792          self.parser.add_option('--remodel', dest='remodel',
    793                      action="store_true", default=False,
    794                      help="Remodel existing objects")
    795          self.parser.add_option('--routers', dest='routersonly',
    796                      action="store_true", default=False,
    797                      help="Only discover routers")
    798          self.parser.add_option('--tries', dest='tries', default=1, type="int",
    799                      help="How many ping tries")
    800          self.parser.add_option('--timeout', dest='timeout',
    801                      default=2, type="float",
    802                      help="ping timeout in seconds")
    803          self.parser.add_option('--chunk', dest='chunkSize',
    804                      default=10, type="int",
    805                      help="Number of in-flight ping packets")
    806          self.parser.add_option('--snmp-missing', dest='snmpMissing',
    807                      action="store_true", default=False,
    808                      help="Send an event if SNMP is not found on the device")
    809          self.parser.add_option('--add-inactive', dest='addInactive',
    810                      action="store_true", default=False,
    811                      help="Add all IPs found, even if they are unresponsive")
    812          self.parser.add_option('--reset-ptr', dest='resetPtr',
    813                      action="store_true", default=False,
    814                      help="Reset all IP PTR records")
    815          self.parser.add_option('--no-snmp', dest='nosnmp',
    816                      action="store_true", default=False,
    817                      help="Skip SNMP discovery on found IP addresses")
    818          self.parser.add_option('--subnets', dest='subnets',
    819                      action="store_true", default=False,
    820                      help="Recurse into subnets for discovery")
    821          self.parser.add_option('--assign-devclass-script', dest='autoAllocate',
    822                      action="store_true", default=False,
    823                      help="have zendisc auto allocate devices after discovery")
    824          self.parser.add_option('--walk', dest='walk', action='store_true',
    825                      default=False,
    826                      help="Walk the route tree, performing discovery on all networks")
    827          self.parser.add_option('--max-devices', dest='maxdevices',
    828                      default=0,
    829                      type='int',
    830                      help="Collect a maximum number of devices. Default is no limit.")
    831          self.parser.add_option('--snmp-strict-discovery',
    832                      dest='zSnmpStrictDiscovery',
    833                      action="store_true", default=False,
    834                      help="Only add devices that can be modeled via SNMP." )
    835          self.parser.add_option('--prefer-snmp-naming',
    836                      dest='zPreferSnmpNaming',
    837                      action="store_true", default=False,
    838                      help="Prefer SNMP name to DNS name when modeling via SNMP." )
    839          # --job: a development-only option that jobs will use to communicate
    840          # their existence to zendisc. Not for users, so help is suppressed.
    841          self.parser.add_option('--job', dest='job', help=SUPPRESS_HELP )
    842
    843
    844
    845  if __name__ == "__main__":
    846      d = ZenDisc()
    847      d.processOptions()
    848      reactor.run = d.reactorLoop
    849      d.run()
    850