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