Package Products :: Package ZenHub :: Package services :: Module DiscoverService |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenHub.services.DiscoverService

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2008, 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 math
     12  import re
     13  from ipaddr import IPNetwork
     14  import logging
     15
     16  log = logging.getLogger('zen.DiscoverService')
     17
     18  import Globals
     19  import transaction
     20  from twisted.spread import pb
     21  from ZODB.transact import transact
     22
     23  from Products.Jobber.exceptions import NoSuchJobException
     24  from Products.ZenUtils.IpUtil import strip, ipwrap, ipunwrap, isip
     25  from Products.ZenEvents.Event import Event
     26  from Products.ZenEvents.ZenEventClasses import Status_Ping
     27  from Products.ZenModel.Device import manage_createDevice
     28  from Products.ZenHub.PBDaemon import translateError
     29  from Products.ZenModel.Exceptions import DeviceExistsError
     30  from Products.ZenRelations.ZenPropertyManager import iszprop
     31
     32  from .ModelerService import ModelerService
     33
     34
     35  DEFAULT_PING_THRESH = 168
     36
     37
     38 -class JobPropertiesProxy(pb.Copyable, pb.RemoteCopy):
     39 -    def __init__(self, jobrecord):
     40          self.zProperties = {}
     41          for prop in jobrecord.__dict__:
     42              if iszprop(prop):
     43                  self.zProperties[prop] = getattr(jobrecord, prop)
     44
     45  pb.setUnjellyableForClass(JobPropertiesProxy, JobPropertiesProxy)
     46
     47 -class IpNetProxy(pb.Copyable, pb.RemoteCopy):
     48      "A class that will represent a ZenModel/IpNetwork in zendisc"
     49
     50      id = ''
     51      _children = None
     52      netmask = None
     53
     54 -    def __init__(self, ipnet):
     55          self.id = ipnet.id
     56          self._children = map(IpNetProxy, ipnet.children())
     57          self.netmask = ipnet.netmask
     58          for prop in 'zAutoDiscover zDefaultNetworkTree zPingFailThresh'.split():
     59              if hasattr(ipnet, prop):
     60                  setattr(self, prop, getattr(ipnet, prop))
     61
     62 -    def children(self):
     63          return self._children
     64
     65 -    def fullIpList(self):
     66          "copied from IpNetwork"
     67          log.debug("fullIpList: using ipaddr IPNetwork on %s (%s)" % (self.id, ipunwrap(self.id)))
     68          net = IPNetwork(ipunwrap(self.id))
     69          if self.netmask == net.max_prefixlen: return [ipunwrap(self.id)]
     70          ipnumb = long(int(net))
     71          maxip = math.pow(2, net.max_prefixlen - self.netmask)
     72          start = int(ipnumb+1)
     73          end = int(ipnumb+maxip-1)
     74          return map(strip, range(start,end))
     75
     76 -    def getNetworkName(self):
     77          return "%s/%d" % (ipunwrap(self.id), self.netmask)
     78
     79  pb.setUnjellyableForClass(IpNetProxy, IpNetProxy)
     80
     81
     82 -class DiscoverService(ModelerService):
     83
     84      @translateError
     85 -    def remote_getNetworks(self, net, includeSubNets):
     86          "Get network objects to scan networks should be in CIDR form 1.1.1.0/24"
     87          netObj = self.dmd.Networks.getNetworkRoot().findNet(net)
     88          if not netObj:
     89              return None
     90          nets = [netObj]
     91          if includeSubNets:
     92              nets += netObj.getSubNetworks()
     93          return map(IpNetProxy, nets)
     94
     95      @translateError
     96 -    def remote_pingStatus(self, net, goodips, badips, resetPtr, addInactive):
     97          "Create objects based on ping results"
     98          net = self.dmd.Networks.getNetworkRoot().findNet(net.id, net.netmask)
     99          pingthresh = getattr(net, "zPingFailThresh", DEFAULT_PING_THRESH)
    100          ips = []
    101          for ip in goodips:
    102              ipobj = net.createIp(ip, net.netmask)
    103              if resetPtr:
    104                  ipobj.setPtrName()
    105              if not ipobj.device():
    106                  ips.append(ip)
    107              self.sendIpStatusEvent(ipobj, sev=0)
    108          for ip in badips:
    109              ipobj = self.dmd.Networks.getNetworkRoot().findIp(ip)
    110              if not ipobj and addInactive:
    111                  ipobj = net.createIp(ip, net.netmask)
    112              if ipobj:
    113                  if resetPtr:
    114                      ipobj.setPtrName()
    115                  elif ipobj.getStatus(Status_Ping) > pingthresh:
    116                      net.ipaddresses.removeRelation(ipobj)
    117                  if ipobj:
    118                      self.sendIpStatusEvent(ipobj)
    119          transaction.commit()
    120          return ips
    121
    122
    123 -    def sendIpStatusEvent(self, ipobj, sev=2):
    124          """Send an ip down event.  These are used to cleanup unused ips.
    125          """
    126          ip = ipobj.id
    127          dev = ipobj.device()
    128          if sev == 0:
    129              msg = "ip %s is up" % ip
    130          else:
    131              msg = "ip %s is down" % ip
    132          if dev:
    133              devname = dev.id
    134              comp = ipobj.interface().id
    135          else:
    136              devname = comp = ip
    137          self.sendEvent(dict(device=devname, ipAddress=ip, eventKey=ip,
    138              component=comp, eventClass=Status_Ping, summary=msg, severity=sev,
    139              agent="Discover"))
    140
    141
    142      @translateError
    143 -    def remote_createDevice(self, ip, force=False, **kw):
    144          """Create a device.
    145
    146          @param ip: The manageIp of the device
    147          @param kw: The args to manage_createDevice.
    148          """
    149          # During discovery, if the device
    150          # shares an id with another device
    151          # with a different ip, set the device
    152          # title to the supplied id and replace
    153          # the id with the ip
    154          deviceName = kw['deviceName']
    155          if deviceName:
    156              device = self.dmd.Devices.findDeviceByIdExact(deviceName)
    157              if device and ip != device.manageIp:
    158                  kw['deviceName'] = ip
    159                  kw['title'] = deviceName
    160
    161          from Products.ZenModel.Device import getNetworkRoot
    162          @transact
    163          def _doDbWork():
    164              """
    165              return device object (either new or existing), and flag indicating
    166              whether device was newly created, or just updated
    167              """
    168              try:
    169                  netroot = getNetworkRoot(self.dmd,
    170                      kw.get('performanceMonitor', 'localhost'))
    171                  netobj = netroot.getNet(ip)
    172                  netmask = 24
    173                  if netobj is not None:
    174                      netmask = netobj.netmask
    175                  else:
    176                      defaultNetmasks = getattr(netroot, 'zDefaultNetworkTree', [])
    177                      if defaultNetmasks:
    178                          netmask = defaultNetmasks[0]
    179                  netroot.createIp(ip, netmask)
    180                  autoDiscover = getattr(netobj, 'zAutoDiscover', True)
    181                  # If we're not supposed to discover this IP, return None
    182                  if not force and not autoDiscover:
    183                      return None, False
    184                  kw['manageIp'] = ipunwrap(ip)
    185                  dev = manage_createDevice(self.dmd, **kw)
    186                  return dev, True
    187              except DeviceExistsError, e:
    188                  # Update device with latest info from zendisc
    189                  e.dev.setManageIp(kw['manageIp'])
    190
    191                  # only overwrite title if it has not been set
    192                  if not e.dev.title or isip(e.dev.title):
    193                      if not isip(kw.get('deviceName')):
    194                          e.dev.setTitle(kw['deviceName'])
    195
    196                  # copy kw->updateAttributes, to keep kw intact in case
    197                  # we need to retry transaction
    198                  updateAttributes = {}
    199                  for k,v in kw.items():
    200                      if k not in ('manageIp', 'deviceName', 'devicePath',
    201                              'discoverProto'):
    202                          updateAttributes[k] = v
    203                  # use updateDevice so we don't clobber existing device properties.
    204                  e.dev.updateDevice(**updateAttributes)
    205                  return e.dev, False
    206              except Exception, ex:
    207                  log.exception("IP address %s (kw = %s) encountered error", ipunwrap(ip), kw)
    208                  raise pb.CopyableFailure(ex)
    209
    210          dev, deviceIsNew = _doDbWork()
    211          if dev is not None:
    212              return self.createDeviceProxy(dev), deviceIsNew
    213          else:
    214              return None, False
    215
    216      @translateError
    217 -    def remote_getJobProperties(self, jobid):
    218          try:
    219              jobrecord = self.dmd.JobManager.getJob(jobid)
    220              if jobrecord:
    221                  return JobPropertiesProxy(jobrecord)
    222          except NoSuchJobException:
    223              pass
    224
    225      @translateError
    226 -    def remote_succeedDiscovery(self, id):
    227          dev = self.dmd.Devices.findDeviceByIdOrIp(id)
    228          if dev:
    229              dev._temp_device = False
    230          transaction.commit()
    231          return True
    232
    233      @translateError
    234 -    def remote_followNextHopIps(self, device):
    235          """
    236          Return the ips that the device's indirect routes point to
    237          which aren't currently connected to devices.
    238          """
    239          dev = self.getPerformanceMonitor().findDevice(device)
    240          ips = []
    241          for r in dev.os.routes():
    242              ipobj = r.nexthop()
    243              if ipobj: ips.append(ipobj.id)
    244          return ips
    245
    246
    247      @translateError
    248 -    def remote_getSubNetworks(self):
    249          "Fetch proxies for all the networks"
    250          return map(IpNetProxy,
    251                  self.dmd.Networks.getNetworkRoot().getSubNetworks())
    252
    253
    254      @translateError
    255 -    def remote_getSnmpConfig(self, devicePath):
    256          "Get the snmp configuration defaults for scanning a device"
    257          devroot = self.dmd.Devices.createOrganizer(devicePath)
    258          return (devroot.zSnmpCommunities,
    259                  devroot.zSnmpPort,
    260                  devroot.zSnmpVer,
    261                  devroot.zSnmpTimeout,
    262                  devroot.zSnmpTries)
    263
    264
    265      @translateError
    266 -    def remote_moveDevice(self, dev, path):
    267          self.dmd.Devices.moveDevices(path, [dev])
    268          transaction.commit()
    269
    270      @translateError
    271 -    def remote_getDefaultNetworks(self):
    272          monitor = self.dmd.Monitors.Performance._getOb(self.instance)
    273          return [net for net in monitor.discoveryNetworks]
    274