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