Package Products :: Package ZenModel :: Module LinkManager |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenModel.LinkManager

      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  from itertools import groupby
     15
     16  from Acquisition import aq_base
     17
     18  from Globals import InitializeClass
     19  from AccessControl import ClassSecurityInfo
     20
     21  from OFS.Folder import Folder
     22
     23  from json import dumps
     24  from Products.CMFCore.utils import getToolByName
     25  from Products.ZCatalog.ZCatalog import manage_addZCatalog
     26  from Products.ZenModel.Device import Device
     27  from Products.ZenUtils.Search import makeCaseInsensitiveFieldIndex
     28  from Products.ZenUtils.NetworkTree import NetworkLink
     29  from Products.Zuul import getFacade
     30  from Products.ZenEvents.events2.processing import Manager
     31  from zenoss.protocols.protobufs.zep_pb2 import (SEVERITY_CRITICAL, SEVERITY_ERROR,
     32                                                  SEVERITY_WARNING, SEVERITY_INFO,
     33                                                  SEVERITY_DEBUG, SEVERITY_CLEAR)
     34  from zenoss.protocols.protobufs.zep_pb2 import STATUS_NEW, STATUS_ACKNOWLEDGED
     35
     36  security = ClassSecurityInfo()
     37
     38  NODE_IDS = dict(
     39      layer_3 = {'IpNetwork':'networkId', 'Device':'deviceId'},
     40      layer_2 = {'LAN':'lanId', 'Device':'deviceId'}
     41  )
     42
     43 -def _getComplement(context, layer=3):
     44      key = 'layer_%d' % layer
     45      nodestuff = NODE_IDS[key]
     46      if not isinstance(context, basestring):
     47          try:
     48              context = nodestuff[context.meta_type]
     49          except KeyError:
     50              return None
     51      first, second = nodestuff.values()
     52      if context==first:
     53          return second
     54      else:
     55          return first
     56
     57 -def manage_addLinkManager(context, id="ZenLinkManager"):
     58      """ Make a LinkManager """
     59      mgr = LinkManager(id)
     60      context._setObject(mgr.id, mgr)
     61      mgr = context._getOb(id)
     62      _create_catalogs(mgr)
     63
     64
     65 -def _create_layer2_catalog(mgr):
     66      layer_2_indices = (
     67          ('lanId', makeCaseInsensitiveFieldIndex),
     68          ('macaddress', makeCaseInsensitiveFieldIndex),
     69          ('deviceId', makeCaseInsensitiveFieldIndex),
     70          ('interfaceId', makeCaseInsensitiveFieldIndex)
     71      )
     72      mgr._addLinkCatalog('layer2_catalog', layer_2_indices)
     73
     74
     75 -def _create_layer3_catalog(mgr):
     76      layer_3_indices = (
     77          ('networkId', makeCaseInsensitiveFieldIndex),
     78          ('ipAddressId', makeCaseInsensitiveFieldIndex),
     79          ('deviceId', makeCaseInsensitiveFieldIndex),
     80          ('interfaceId', makeCaseInsensitiveFieldIndex)
     81      )
     82      mgr._addLinkCatalog('layer3_catalog', layer_3_indices)
     83
     84
     85 -def _create_catalogs(mgr):
     86      _create_layer2_catalog(mgr)
     87      _create_layer3_catalog(mgr)
     88
     89
     90 -class Layer3Link(object):
     91      """
     92      Provides an API for navigating paired groups of brains.
     93      """
     94 -    def __init__(self, dmd, twokeydict):
     95          a, b = twokeydict.items()
     96          aid, self.abrains = a
     97          bid, self.bbrains = b
     98          self.a = dmd.unrestrictedTraverse(aid)
     99          self.b = dmd.unrestrictedTraverse(bid)
    100          self.zep = getFacade('zep', dmd)
    101          self.idmgr = Manager(dmd)
    102
    103 -    def _getComponentUuid(self, devuuid, compid):
    104          try:
    105              dev = self.idmgr.getElementByUuid(devuuid)
    106              compuuid = self.idmgr.getElementUuidById(dev, Device, compid)
    107              return compuuid
    108          except Exception:
    109              return None
    110
    111 -    def getStatus(self):
    112          brains = self.abrains + self.bbrains
    113
    114          # lookup all device uuids, make sure at least one exists
    115          devUuids = [self.idmgr.findDeviceUuid(a.deviceId, None) for a in brains]
    116          validDevUuids = filter(None, devUuids)
    117          if not validDevUuids:
    118              return SEVERITY_CLEAR
    119
    120          # if there is any open /Status/Ping event on any device, return CRITICAL severity
    121          statusPingFilter = self.zep.createEventFilter(
    122              tags = validDevUuids,
    123              event_class = '/Status/Ping/',
    124              status = (STATUS_NEW, STATUS_ACKNOWLEDGED),
    125              severity = (SEVERITY_WARNING, SEVERITY_ERROR, SEVERITY_CRITICAL)
    126          )
    127          maxpingrec = self.zep.getEventSummaries(0, filter=statusPingFilter, sort=(('count','desc'),), limit=1)
    128          if maxpingrec and maxpingrec['total'] > 0:
    129              return SEVERITY_CRITICAL
    130
    131          # no /Status/Ping events found, just return worst severity of all events on all interface components
    132          devCompPairs = zip(devUuids, (a.interfaceId for a in brains))
    133          compUuids = (self._getComponentUuid(devuuid, compid)
    134                          for devuuid, compid in devCompPairs
    135                          if devuuid is not None)
    136          components = filter(None, compUuids)
    137          if components:
    138              sev = self.zep.getWorstSeverity(components)
    139              return sev
    140
    141          return SEVERITY_CLEAR
    142
    143 -    def getAddresses(self):
    144          return (self.a.address, self.b.address)
    145
    146
    147 -class LinkManager(Folder):
    148      """
    149      A tool that keeps track of OSI layer links between objects.
    150      """
    151 -    def __init__(self, id, *args, **kwargs):
    152          Folder.__init__(self, id, *args, **kwargs)
    153          self.id = id
    154
    155 -    def _getCatalog(self, layer=3):
    156          try:
    157              return getToolByName(self, 'layer%d_catalog' % layer)
    158          except AttributeError:
    159              return None
    160
    161 -    def _addLinkCatalog(self, id, indices):
    162          manage_addZCatalog(self, id, id)
    163          zcat = self._getOb(id)
    164          cat = zcat._catalog
    165          for index, factory in indices:
    166              cat.addIndex(index, factory(index))
    167              zcat.addColumn(index)
    168
    169 -    def getLinkedNodes(self, meta_type, id, layer=3, visited=None):
    170          cat = self._getCatalog(layer)
    171          col = NODE_IDS['layer_%d' % layer][meta_type]
    172          nextcol = _getComplement(col, layer)
    173          brains = cat(**{col:id})
    174          gen1ids = set(getattr(brain, nextcol) for brain in brains)
    175          if visited:
    176              gen1ids = gen1ids - visited # Don't go places we've been!
    177          gen2 = cat(**{nextcol:list(gen1ids)})
    178          return gen2, gen1ids
    179
    180 -    def getChildLinks(self, organizer):
    181          catalog = getToolByName(self.dmd.Devices, 'deviceSearch')
    182          result = {}
    183          locs = organizer.children()
    184          locpaths = ['/'.join(loc.getPrimaryPath()) for loc in locs]
    185          locpaths.reverse() # otherwise the "startswith" call gets too eager
    186          path = '/'.join(organizer.getPhysicalPath())
    187          subdevs = catalog(path=path)
    188          subids = dict((x.id, x.path) for x in subdevs)
    189
    190          def _whichorg(brain):
    191              for path in locpaths:
    192                  try:
    193                      brainpath = subids[brain.deviceId]
    194                  except KeyError:
    195                      return '__outside'
    196                  if filter(lambda x:'/'.join(x).startswith(path), brainpath):
    197                      return path
    198              return '__outside'
    199
    200          def _whichnet(brain):
    201              return brain.networkId
    202
    203          def _whichdev(brain):
    204              return brain.deviceId
    205
    206          links, nets = self.getLinkedNodes('Device', subids.keys())
    207          links = map(aq_base, links) # For comparison, can't be ImplicitAcq
    208
    209          byloc = {}
    210          for k, g in groupby(links, _whichorg):
    211              byloc.setdefault(k, []).extend(g)
    212          if '__outside' in byloc: del byloc['__outside']
    213
    214          bynet = {}
    215          for k, g in groupby(links, _whichnet):
    216              if getattr(self.dmd.unrestrictedTraverse(k), 'zDrawMapLinks', True):
    217                  bynet.setdefault(k, []).extend(g)
    218
    219          final = {}
    220          linkobs = []
    221
    222          inverted_loc = {}
    223          for loc in byloc:
    224              for dev in byloc[loc]:
    225                  inverted_loc[dev.deviceId] = loc
    226          for net in bynet:
    227              devs = bynet[net]
    228              alllocs = set()
    229              for dev in devs:
    230                  if dev.deviceId and dev.deviceId in inverted_loc:
    231                      alllocs.add(inverted_loc[dev.deviceId])
    232              if len(alllocs)>=2:
    233                  for dev in devs:
    234                      if dev.deviceId:
    235                          loc = inverted_loc.get(dev.deviceId, None)
    236                          if loc:
    237                              final.setdefault(loc, []).append(dev)
    238          def haslink(locs1, locs2):
    239              for l in locs1:
    240                  for b in locs2:
    241                      if l.networkId==b.networkId:
    242                          return True
    243          locs = final.keys()
    244          while locs:
    245              loc = locs.pop()
    246              for loc2 in locs:
    247                  first = final[loc]
    248                  second = final[loc2]
    249                  if haslink(first, second):
    250                      link = Layer3Link(self.dmd, {loc:first, loc2:second})
    251                      linkobs.append(link)
    252          return dumps([(x.getAddresses(), x.getStatus()) for x in linkobs])
    253
    254 -    def getChildLinks_recursive(self, context):
    255          """ Returns all links under a given Organizer, aggregated """
    256          result = set()
    257          severities = {}
    258          links = self.getNetworkLinks(context)
    259          for x in links:
    260              geomapdata = x.getGeomapData(context)
    261              severities[geomapdata] = max(
    262                  x.getStatus(),
    263                  severities.get(geomapdata, 0)
    264              )
    265              result.add(geomapdata)
    266          addresses = [x for x in list(result) if x]
    267          severities = [severities[x] for x in addresses]
    268          return map(list, zip(map(list, addresses), severities))
    269
    270 -    def getNetworkLinks(self, context):
    271          """
    272          An alternate way to get links under an Organizer.
    273          """
    274          result = set()
    275          networks = filter(lambda x:x.zDrawMapLinks,
    276                            self.dmd.Networks.getSubNetworks())
    277          siblings = [x.getPrimaryId() for x in context.children()]
    278          for net in networks:
    279              locdict = {}
    280              def addToDict(iface):
    281                  loc = iface.device().location()
    282                  if not loc: return
    283                  here = loc.getPrimaryId()
    284                  matched = False
    285                  for sib in siblings:
    286                      if here.startswith(sib):
    287                          locdict.setdefault(sib, []).append(iface)
    288                          matched = True
    289                          break
    290                  if not matched:
    291                      locdict.setdefault(here, []).append(iface)
    292              for ip in net.ipaddresses.objectValuesGen():
    293                  iface = ip.interface()
    294                  if iface: addToDict(iface)
    295              if len(locdict)<=1: continue
    296              locgroups = locdict.values()
    297              while locgroups:
    298                  lg = locgroups.pop()
    299                  targets = []
    300                  for g in locgroups: targets.extend(g)
    301                  for l in lg:
    302                      for t in targets:
    303                          n = NetworkLink()
    304                          n.setEndpoints(l, t)
    305                          result.add(n)
    306          return result
    307  InitializeClass(LinkManager)
    308