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