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

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

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