| Trees | Indices | Help |
|
|---|
|
|
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
30
32 id, name, ignoreParams, alertOnRestart, failSeverity = \
33 context.getOSProcessConf()
34 return dict(processName=name,
35 ignoreParams=ignoreParams,
36 alertOnRestart=alertOnRestart,
37 failSeverity=failSeverity)
38
40 results.events.append(dict(
41 eventClass=Status_OSProcess,
42 eventGroup='Process',
43 **kwargs))
44
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
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
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
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1.1812 on Mon Jul 30 17:11:16 2012 | http://epydoc.sourceforge.net |