Package Products :: Package ZenRRD :: Package parsers :: Module ps |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenRRD.parsers.ps

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2009, 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__ = """ps
     15  Interpret the output from the ps command and provide performance data for
     16  CPU utilization, total RSS and the number of processes that match the
     17  /Process tree definitions.
     18  """
     19
     20  import re
     21  import logging
     22  log = logging.getLogger("zen.ps")
     23
     24  import Globals
     25  from Products.ZenRRD.CommandParser import CommandParser
     26  from Products.ZenEvents.ZenEventClasses import Status_OSProcess
     27
     28  # Keep track of state between runs
     29  AllPids = {} # (device, processName)
     30  emptySet = set()
     31
     32 -class ps(CommandParser):
     33
     34 -    def dataForParser(self, context, datapoint):
     35          id, name, ignoreParams, alertOnRestart, failSeverity = \
     36              context.getOSProcessConf()
     37          return dict(processName=name,
     38                      ignoreParams=ignoreParams,
     39                      alertOnRestart=alertOnRestart,
     40                      failSeverity=failSeverity)
     41
     42 -    def sendEvent(self, results, **kwargs):
     43          results.events.append(dict(
     44                      eventClass=Status_OSProcess,
     45                      eventGroup='Process',
     46                      **kwargs))
     47
     48 -    def getMatches(self, matchers, procName, cmdAndArgs):
     49          """
     50          Get regex matches of processes running on the machine
     51          """
     52          matches = []
     53          for dp, procRegex in matchers.items():
     54              if dp.data['ignoreParams']:
     55                  name = procName
     56              else:
     57                  name = cmdAndArgs
     58
     59              if procRegex.search(name):
     60                  matches.append(dp)
     61          return matches
     62
     63 -    def getProcInfo(self, line):
     64          """
     65          Process the non-empyt ps and return back the
     66          standard info.
     67
     68          @parameter line: one line of ps output
     69          @type line: text
     70          @return: pid, rss, cpu, cmdAndArgs (ie full process name)
     71          @rtype: tuple
     72          """
     73          pid, rss, cpu, cmdAndArgs = line.split(None, 3)
     74          return pid, rss, cpu, cmdAndArgs
     75
     76 -    def groupProcs(self, matchers, output):
     77          """
     78          Group processes per datapoint
     79          """
     80          dpsToProcs = {}
     81          for line in output.split('\n')[1:]:
     82              if not line:
     83                  continue
     84
     85              try:
     86                  pid, rss, cpu, cmdAndArgs = self.getProcInfo(line)
     87                  log.debug("line '%s' -> pid=%s " \
     88                                "rss=%s cpu=%s cmdAndArgs=%s",
     89                                 line, pid, rss, cpu, cmdAndArgs)
     90
     91              except (SystemExit, KeyboardInterrupt): raise
     92              except:
     93                  log.warn("Unable to parse entry '%s'", line)
     94                  continue
     95
     96              try:
     97                  procName = cmdAndArgs.split()[0]
     98                  matches = self.getMatches(matchers, procName, cmdAndArgs)
     99
    100                  if not matches:
    101                      continue
    102
    103                  days = 0
    104                  if cpu.find('-') > -1:
    105                      days, cpu = cpu.split('-')
    106                      days = int(days)
    107                  cpu = map(int, cpu.split(':'))
    108                  if len(cpu) == 3:
    109                      cpu = (days * 24 * 60 * 60 +
    110                         cpu[0] * 60 * 60 +
    111                         cpu[1] * 60 +
    112                         cpu[2])
    113                  elif len(cpu) == 2:
    114                      cpu = (days * 24 * 60 * 60 +
    115                         cpu[0] * 60 +
    116                         cpu[1])
    117
    118                  rss = int(rss)
    119                  pid = int(pid)
    120
    121                  for dp in matches:
    122                      procInfo = dict(procName=procName,
    123                         cmdAndArgs=cmdAndArgs, rss=0.0, cpu=0.0,
    124                         pids=set())
    125                      procInfo = dpsToProcs.setdefault(dp, procInfo)
    126                      procInfo['rss'] += rss
    127                      procInfo['cpu'] += cpu
    128                      procInfo['pids'].add(pid)
    129
    130              except (SystemExit, KeyboardInterrupt): raise
    131              except:
    132                  log.exception("Unable to convert entry data pid=%s " \
    133                                "rss=%s cpu=%s cmdAndArgs=%s",
    134                                 pid, rss, cpu, cmdAndArgs)
    135                  continue
    136          return dpsToProcs
    137
    138
    139 -    def processResults(self, cmd, results):
    140
    141          # map data points by procesName
    142          matchers = {}
    143          for dp in cmd.points:
    144              matchers[dp] = re.compile(re.escape(dp.data['processName']))
    145
    146          dpsToProcs = self.groupProcs(matchers, cmd.result.output)
    147
    148          # report any processes that are missing, and post perf data
    149          for dp in cmd.points:
    150              process = dp.data['processName']
    151              failSeverity = dp.data['failSeverity']
    152              procInfo = dpsToProcs.get(dp, None)
    153              if not procInfo:
    154                  self.sendEvent(results,
    155                      summary='Process not running: ' + process,
    156                      component=process,
    157                      severity=failSeverity)
    158                  log.debug("device:%s, command: %s, procInfo: %r, failSeverity: %r, process: %s, dp: %r",
    159                              cmd.deviceConfig.device,
    160                              cmd.command,
    161                              procInfo,
    162                              failSeverity,
    163                              process,
    164                              dp)
    165              else:
    166                  if 'cpu' in dp.id:
    167                      results.values.append( (dp, procInfo['cpu']) )
    168                  if 'mem' in dp.id:
    169                      results.values.append( (dp, procInfo['rss']) )
    170                  if 'count' in dp.id:
    171                      results.values.append( (dp, len(procInfo['pids'])) )
    172
    173              # Report process changes
    174              # Note that we can't tell the difference between a
    175              # reconfiguration from zenhub and process that goes away.
    176              device = cmd.deviceConfig.device
    177              before = AllPids.get( (device, process), emptySet)
    178              after = set()
    179              if procInfo:
    180                  after = procInfo['pids']
    181
    182              alertOnRestart = dp.data['alertOnRestart']
    183
    184              if before != after:
    185                  if len(before) > len(after) and alertOnRestart:
    186                      pids = ', '.join(map(str, before - after))
    187                      self.sendEvent(results,
    188                          summary='Pid(s) %s stopped: %s' % (pids, process),
    189                          component=process,
    190                          severity=failSeverity)
    191                  if len(before) == len(after) and alertOnRestart:
    192                      # process restarted
    193                      pids = ', '.join(map(str, before - after))
    194                      self.sendEvent(results,
    195                          summary='Pid(s) %s restarted: %s' % (pids, process),
    196                          component=process,
    197                          severity=failSeverity)
    198                  if len(before) < len(after):
    199                      if len(before) == 0:
    200                          self.sendEvent(results,
    201                              summary='Process running: %s' % process,
    202                              component=process,
    203                              severity=0)
    204
    205              AllPids[device, process] = after
    206