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

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

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, 2010 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__ = '''CommandPerformanceConfig
     15
     16  Provides configuration to zencommand clients.
     17  '''
     18  import logging
     19  log = logging.getLogger('zen.HubService.CommandPerformanceConfig')
     20  import traceback
     21
     22  import Globals
     23  from ZODB.POSException import ConflictError
     24
     25  from Products.ZenCollector.services.config import CollectorConfigService
     26  from Products.ZenRRD.zencommand import Cmd, DataPointConfig
     27  from Products.DataCollector.Plugins import getParserLoader
     28  from Products.ZenEvents.ZenEventClasses import Error, Clear, Cmd_Fail
     29
     30
     31 -class CommandPerformanceConfig(CollectorConfigService):
     32      dsType = 'COMMAND'
     33
     34 -    def __init__(self, dmd, instance):
     35          deviceProxyAttributes = ('zCommandPort',
     36                                   'zCommandUsername',
     37                                   'zCommandPassword',
     38                                   'zCommandLoginTimeout',
     39                                   'zCommandCommandTimeout',
     40                                   'zKeyPath',
     41                                   'zSshConcurrentSessions',
     42                                  )
     43          CollectorConfigService.__init__(self, dmd, instance,
     44                                          deviceProxyAttributes)
     45
     46      # Use case: create a dummy device to act as a placeholder to execute commands
     47      #           So don't filter out devices that don't have IP addresses.
     48
     49 -    def _getDsDatapoints(self, comp, ds, ploader, perfServer):
     50          """
     51          Given a component a data source, gather its data points
     52          """
     53          parser = ploader.create()
     54          points = []
     55          component_name = ds.getComponent(comp)
     56          basepath = comp.rrdPath()
     57          for dp in ds.getRRDDataPoints():
     58              dpc = DataPointConfig()
     59              dpc.id = dp.id
     60              dpc.component = component_name
     61              dpc.rrdPath = "/".join((basepath, dp.name()))
     62              dpc.rrdType = dp.rrdtype
     63              dpc.rrdCreateCommand = dp.getRRDCreateCommand(perfServer)
     64              dpc.rrdMin = dp.rrdmin
     65              dpc.rrdMax = dp.rrdmax
     66              dpc.data = parser.dataForParser(comp, dp)
     67              points.append(dpc)
     68
     69          return points
     70
     71 -    def _getDsCycleTime(self, comp, templ, ds):
     72          cycleTime = 300
     73          try:
     74              cycleTime = int(ds.cycletime)
     75          except ValueError:
     76              message = "Unable to convert the cycle time '%s' to an " \
     77                            "integer for %s/%s on %s" \
     78                            " -- setting to 300 seconds" % (
     79                            ds.cycletime, templ.id, ds.id, comp.device().id)
     80              log.error(message)
     81              component = ds.getPrimaryUrlPath()
     82              dedupid = "Unable to convert cycletime for %s" % component
     83              self.sendEvent(dict(
     84                      device=comp.device().id, component=component,
     85                      eventClass='/Cmd', severity=Warning, summary=message,
     86                      dedupid=dedupid,
     87              ))
     88          return cycleTime
     89
     90 -    def _safeGetComponentConfig(self, comp, device, perfServer,
     91                                  commands, thresholds):
     92          """
     93          Catchall wrapper for things not caught at previous levels
     94          """
     95          if not comp.monitorDevice():
     96              return None
     97
     98          try:
     99              threshs = self._getComponentConfig(comp, device, perfServer, commands)
    100              if threshs:
    101                  thresholds.extend(threshs)
    102          except ConflictError: raise
    103          except Exception, ex:
    104              msg = "Unable to process %s datasource(s) for device %s -- skipping" % (
    105                                self.dsType, device.id)
    106              log.exception(msg)
    107              details = dict(traceback=traceback.format_exc(),
    108                             msg=msg)
    109              self._sendCmdEvent(device.id, details)
    110
    111 -    def _getComponentConfig(self, comp, device, perfServer, cmds):
    112          for templ in comp.getRRDTemplates():
    113              for ds in templ.getRRDDataSources(self.dsType):
    114                  if not ds.enabled:
    115                      continue
    116
    117                  # Ignore SSH datasources if no username set
    118                  useSsh = getattr(ds, 'usessh', False)
    119                  if useSsh and not device.zCommandUsername:
    120                      self._warnUsernameNotSet(device)
    121                      continue
    122
    123                  parserName = getattr(ds, "parser", "Auto")
    124                  ploader = getParserLoader(self.dmd, parserName)
    125                  if ploader is None:
    126                      log.error("Could not load %s plugin", parserName)
    127                      continue
    128
    129                  cmd = Cmd()
    130                  cmd.useSsh = useSsh
    131                  cmd.name = "%s/%s" % (templ.id, ds.id)
    132                  cmd.cycleTime = self._getDsCycleTime(comp, templ, ds)
    133                  cmd.component = ds.getComponent(comp)
    134                  cmd.eventClass = ds.eventClass
    135                  cmd.eventKey = ds.eventKey or ds.id
    136                  cmd.severity = ds.severity
    137                  cmd.parser = ploader
    138                  cmd.ds = ds.titleOrId()
    139                  cmd.points = self._getDsDatapoints(comp, ds, ploader, perfServer)
    140
    141                  # If the datasource supports an environment dictionary, use it
    142                  cmd.env = getattr(ds, 'env', None)
    143
    144                  try:
    145                      cmd.command = ds.getCommand(comp)
    146                  except ConflictError: raise
    147                  except Exception: # TALES error
    148                      msg = "TALES error for device %s datasource %s" % (
    149                                 device.id, ds.id)
    150                      details = dict(
    151                             msg=msg,
    152                             template=templ.id,
    153                             datasource=ds.id,
    154                             affected_device=device.id,
    155                             affected_component=comp.id,
    156                             resolution='Could not create a command to send to zencommand' \
    157                                        ' because TALES evaluation failed.  The most likely' \
    158                                        ' cause is unescaped special characters in the command.' \
    159                                        ' eg $ or %')
    160                      # This error might occur many, many times
    161                      self._sendCmdEvent('localhost', details)
    162                      continue
    163
    164                  self.enrich(cmd, templ, ds)
    165                  cmds.add(cmd)
    166
    167          return comp.getThresholdInstances(self.dsType)
    168
    169 -    def enrich(self, cmd, template, ds):
    170          """
    171          Hook routine available for subclassed services
    172          """
    173          pass
    174
    175 -    def _createDeviceProxy(self, device):
    176          proxy = CollectorConfigService._createDeviceProxy(self, device)
    177
    178          proxy.configCycleInterval = self._prefs.perfsnmpCycleInterval
    179          proxy.name = device.id
    180          proxy.device = device.id
    181          proxy.lastmodeltime = device.getLastChangeString()
    182          proxy.lastChangeTime = float(device.getLastChange())
    183
    184          # Only send one event per warning type
    185          self._sentNoUsernameSetWarning = False
    186          self._sentNoUsernameSetClear = False
    187
    188          perfServer = device.getPerformanceServer()
    189          commands = set()
    190
    191          # First for the device....
    192          proxy.thresholds = []
    193          self._safeGetComponentConfig(device, device, perfServer,
    194                                  commands, proxy.thresholds)
    195
    196          # And now for its components
    197          for comp in device.getMonitoredComponents(collector='zencommand'):
    198              self._safeGetComponentConfig(comp, device, perfServer,
    199                                  commands, proxy.thresholds)
    200
    201          if commands:
    202              proxy.datasources = list(commands)
    203              return proxy
    204          return None
    205
    206 -    def _sendCmdEvent(self, name, details=None):
    207          msg = 'zCommandUsername is not set so SSH-based commands will not run'
    208          ev = dict(
    209                  device=name,
    210                  eventClass=Cmd_Fail,
    211                  eventKey='zCommandUsername',
    212                  severity=Error,
    213                  component='zencommand',
    214                  summary=msg,
    215          )
    216          if details:
    217              ev.update(details)
    218          self.sendEvent(ev)
    219
    220 -    def _warnUsernameNotSet(self, device):
    221          """
    222          Warn that the username is not set for device and the SSH command cannot be
    223          executed.
    224          """
    225          if self._sentNoUsernameSetWarning:
    226              return
    227
    228          msg = 'zCommandUsername is not set so SSH-based commands will not run'
    229          name = device.titleOrId()
    230          log.error('%s for %s', msg, name)
    231          self._sendCmdEvent(name)
    232          self._sentNoUsernameSetWarning = True
    233
    234 -    def _clearUsernameNotSet(self, device):
    235          if self._sentNoUsernameSetClear:
    236              return
    237
    238          self._sendCmdEvent(device.titleOrId(), {'severity':Clear})
    239          self._sentNoUsernameSetClear = True
    240
    241  if __name__ == '__main__':
    242      from Products.ZenHub.ServiceTester import ServiceTester
    243      tester = ServiceTester(CommandPerformanceConfig)
    244 -    def printer(proxy):
    245          print '\t'.join([ '', 'Name', 'Use SSH?', 'CycleTime',
    246                           'Component', 'Points'])
    247          for cmd in sorted(proxy.datasources):
    248              print '\t'.join( map(str, [ '', cmd.name, cmd.useSsh,
    249                  cmd.cycleTime, cmd.component, cmd.points ]) )
    250      tester.printDeviceProxy = printer
    251      tester.showDeviceInfo()
    252