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

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

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