diff options
Diffstat (limited to 'web/attachments/427640-nagios-plugins-openvz-beans.diff')
-rw-r--r-- | web/attachments/427640-nagios-plugins-openvz-beans.diff | 1771 |
1 files changed, 1771 insertions, 0 deletions
diff --git a/web/attachments/427640-nagios-plugins-openvz-beans.diff b/web/attachments/427640-nagios-plugins-openvz-beans.diff new file mode 100644 index 0000000..47a8f40 --- /dev/null +++ b/web/attachments/427640-nagios-plugins-openvz-beans.diff | |||
@@ -0,0 +1,1771 @@ | |||
1 | diff -urN nothing/check_openvz_beans.py nagios-plugins-openvz-beans/check_openvz_beans.py | ||
2 | --- nothing/check_openvz_beans.py 1970-01-01 01:00:00.000000000 +0100 | ||
3 | +++ nagios-plugins-openvz-beans/check_openvz_beans.py 2011-11-01 19:04:11.133266362 +0100 | ||
4 | @@ -0,0 +1,589 @@ | ||
5 | +#!/usr/bin/python | ||
6 | +# | ||
7 | +# Nagios check for OpenVZ bean counters | ||
8 | +# | ||
9 | +# usage: | ||
10 | +# check_openvz_beans.py [options] | ||
11 | +# | ||
12 | +# see "check_openvz_beans.py -h" for detailed options | ||
13 | +# | ||
14 | +# Licensed under GPLv2 (or later) | ||
15 | +# | ||
16 | +# Copyright (C) 2011 NOKIA | ||
17 | +# Written by Andreas Kotes <andreas.kotes@nokia.com> | ||
18 | +# | ||
19 | +# Reviewed-by: Pascal Hahn <pascal.hahn@nokia.com> | ||
20 | +# Signed-Off-by: Pascal Hahn <pascal.hahn@nokia.com> | ||
21 | +# Signed-Off-by: Jessvin Thomas <jessvin.thomas@nokia.com> | ||
22 | +# | ||
23 | + | ||
24 | +import os | ||
25 | +import re | ||
26 | +import errno | ||
27 | +import sys | ||
28 | +import socket | ||
29 | + | ||
30 | +# using 'optparse' for Python 2.4 compatibility | ||
31 | +import optparse | ||
32 | +# using subprocess for clean sudo handling | ||
33 | +import subprocess | ||
34 | + | ||
35 | +__author__ = ('Andreas Kotes <andreas.kotes@nokia.com>') | ||
36 | + | ||
37 | +OPENVZ_CONFPATH = '/etc/vz/conf' | ||
38 | +OPENVZ_UBCSTORE = '/tmp/vzubc.store' | ||
39 | + | ||
40 | +class Error(Exception): | ||
41 | + """Module level error.""" | ||
42 | + pass | ||
43 | + | ||
44 | + | ||
45 | +class NoBeanCountersFoundError(Error): | ||
46 | + "No BeanCounters could be found in output.""" | ||
47 | + pass | ||
48 | + | ||
49 | + | ||
50 | +class BeanCounter(object): | ||
51 | + """Object storing OpenVZ beancounter information""" | ||
52 | + FIND_BEAN_COUNTER_RE = r'''(?xms) # verbose, multiline, special | ||
53 | + ^ # begin entry | ||
54 | + \s*(?P<resource>\S+) # resource name | ||
55 | + \s*(?P<held>\d+) # held count | ||
56 | + \s*(?P<maxheld>\d+) # maximum held | ||
57 | + \s*(?P<barrier>\d+) # barrier | ||
58 | + \s*(?P<limit>\d+) # limit | ||
59 | + \s*(?P<failcnt>\d+) # failure counter | ||
60 | + ''' | ||
61 | + def __init__(self, uid, resource_txt, use_absolute_failcnt=False, storepath=OPENVZ_UBCSTORE): | ||
62 | + """Create object storing OpenVZ beancounter information | ||
63 | + | ||
64 | + Args | ||
65 | + uid: int - OpenVZ context identifier | ||
66 | + resource_txt: str - OpenVZ resource | ||
67 | + use_absolute_failcnt: | ||
68 | + bool - Whether to use delta failcnts or not | ||
69 | + storepath: str - Path to OpenVZ's vzubc's data storage | ||
70 | + """ | ||
71 | + self.uid = uid | ||
72 | + | ||
73 | + # extract data from text line | ||
74 | + resourcedata = re.match(BeanCounter.FIND_BEAN_COUNTER_RE, resource_txt).groupdict() | ||
75 | + self.resource = resourcedata['resource'] | ||
76 | + self.held = int(resourcedata['held']) | ||
77 | + self.maxheld = int(resourcedata['maxheld']) | ||
78 | + self.barrier = int(resourcedata['barrier']) | ||
79 | + self.limit = int(resourcedata['limit']) | ||
80 | + self.failcnt = int(resourcedata['failcnt']) | ||
81 | + | ||
82 | + if not use_absolute_failcnt: | ||
83 | + prev_failcnt = self.read_vzubc_failcnt(storepath) | ||
84 | + # don't update from stale / lower value | ||
85 | + if (prev_failcnt <= self.failcnt): | ||
86 | + self.failcnt -= prev_failcnt | ||
87 | + | ||
88 | + def read_vzubc_failcnt(self, storepath=OPENVZ_UBCSTORE): | ||
89 | + """Read data stored by vzubc -r. | ||
90 | + | ||
91 | + Args | ||
92 | + uid: int - OpenVZ context identifier | ||
93 | + storepath: str - Path to OpenVZ's vzubc's data storage | ||
94 | + | ||
95 | + Returns: | ||
96 | + int - stored value or 0 | ||
97 | + """ | ||
98 | + filename = os.path.join(storepath, "ubc.%d.%s.failcnt" % (self.uid, self.resource)) | ||
99 | + if os.path.exists(filename): | ||
100 | + return int(open(filename, "r").read()) | ||
101 | + else: | ||
102 | + return 0 | ||
103 | + | ||
104 | + def __repr__(self): | ||
105 | + return ('<BeanCounter for uid %d resource %s: held %d maxheld %d barrier %d limit %d failcnt %d>' % | ||
106 | + (self.uid, self.resource, self.held, self.maxheld, self.barrier, self.limit, self.failcnt)) | ||
107 | + | ||
108 | + def __eq__(self, other): | ||
109 | + if (self.uid == other.uid and | ||
110 | + self.resource == other.resource and | ||
111 | + self.held == other.held and | ||
112 | + self.maxheld == other.maxheld and | ||
113 | + self.barrier == other.barrier and | ||
114 | + self.limit == other.limit and | ||
115 | + self.failcnt == other.failcnt): | ||
116 | + return True | ||
117 | + else: | ||
118 | + return False | ||
119 | + | ||
120 | + | ||
121 | +class BeanCounterParser(object): | ||
122 | + """Parse bean counter information.""" | ||
123 | + FIND_BEAN_COUNTER_GROUP_RE = r'''(?xms) # verbose, multline, special | ||
124 | + ^ # begin entry | ||
125 | + \s*(?P<uid>\d+): # group / context number | ||
126 | + (?P<beancounters>[^:]*)$ # everything up to next group/EOF | ||
127 | + ''' | ||
128 | + | ||
129 | + def parse_beancounters(self, beancounters_data, use_absolute_failcnt=False, storepath=OPENVZ_UBCSTORE): | ||
130 | + """Populate BeanCounter objects with data. | ||
131 | + | ||
132 | + Args: | ||
133 | + beancounters_data: str - containing the unparsed | ||
134 | + use_absolute_failcnt: | ||
135 | + bool - Whether to use delta failcnts or not | ||
136 | + storepath: str - Path to OpenVZ's vzubc's data storage | ||
137 | + | ||
138 | + Raises: | ||
139 | + NoBeanCountersFoundError - in case no counters where found | ||
140 | + """ | ||
141 | + # find all beans blocks | ||
142 | + result = re.findall(BeanCounterParser.FIND_BEAN_COUNTER_GROUP_RE, beancounters_data) | ||
143 | + if not result: | ||
144 | + raise NoBeanCountersFoundError | ||
145 | + self.beancounters = [] | ||
146 | + for uid, resource_txt in result: | ||
147 | + for line in resource_txt.split("\n"): | ||
148 | + if line: | ||
149 | + self.beancounters.append(BeanCounter(int(uid), line, use_absolute_failcnt, storepath)) | ||
150 | + return self.beancounters | ||
151 | + | ||
152 | + def __init__(self, beancounters_data, use_absolute_failcnt=False, storepath=OPENVZ_UBCSTORE): | ||
153 | + """Create a new BeanCounter object | ||
154 | + | ||
155 | + Args: | ||
156 | + beancounters_data: str - containing the unparsed | ||
157 | + contents of /proc/user_beancounters | ||
158 | + use_absolute_failcnt: | ||
159 | + bool - Whether to use delta failcnts or not | ||
160 | + storepath: str - Path to OpenVZ's vzubc's data storage | ||
161 | + """ | ||
162 | + self.parse_beancounters(beancounters_data, use_absolute_failcnt, storepath) | ||
163 | + | ||
164 | + def get_beancounters(self): | ||
165 | + return self.beancounters | ||
166 | + | ||
167 | + | ||
168 | +class BeanCounterThreshold(object): | ||
169 | + """Holds threshold values for BeanCounters""" | ||
170 | + THRESHOLD_KEYS = { | ||
171 | + # relevant threshold limiting in OpenVZ | ||
172 | + # see http://wiki.openvz.org/UBC_parameter_properties | ||
173 | + # NOTE: custom/missing beancounters are treated limited by limit | ||
174 | + 'limit' : ['lockedpages', 'numfile' , 'numiptent', 'numothersock', 'numproc', 'numpty', 'numsiginfo', 'numtcpsock', 'shmpages'], | ||
175 | + 'barrier': ['dcachesize', 'dgramrcvbuf', 'kmemsize', 'numflock', 'oomguarpages', 'othersockbuf', 'privvmpages', 'tcprcvbuf', 'tcpsndbuf', 'vmguarpages'], | ||
176 | + 'none' : ['physpages'] | ||
177 | + } | ||
178 | + | ||
179 | + def __init__(self, resource=None, critical=95, warning=90): | ||
180 | + """Create a new verifier. | ||
181 | + | ||
182 | + Args: | ||
183 | + resource: str - resource name (None = wildcard match all resources) | ||
184 | + critical: int - critical level (percentage of limit) | ||
185 | + warning: int - critical level (percentage of limit) | ||
186 | + """ | ||
187 | + self.resource = resource | ||
188 | + self.critical = critical | ||
189 | + self.warning = warning | ||
190 | + | ||
191 | + def __repr__(self): | ||
192 | + if not self.resource: | ||
193 | + resource = '*' | ||
194 | + else: | ||
195 | + resource = self.resource | ||
196 | + return ('<BeanCounterThreshold for resource %s: critical %d warning %d>' % | ||
197 | + (resource, self.critical, self.warning)) | ||
198 | + | ||
199 | + def check(self, beancounter, use_maxheld=False): | ||
200 | + """Check a threshold | ||
201 | + | ||
202 | + Args: | ||
203 | + beancounter: BeanCounter object | ||
204 | + use_maxheld: bool - use maxheld instead of held counter for limits | ||
205 | + | ||
206 | + Returns: | ||
207 | + None or BeanCounterViolation (or subclass) | ||
208 | + """ | ||
209 | + if not self.resource or beancounter.resource == self.resource: | ||
210 | + if beancounter.failcnt: | ||
211 | + return BeanCounterFailure(beancounter.resource, beancounter.uid, beancounter.failcnt) | ||
212 | + | ||
213 | + # what are we measuring against? | ||
214 | + if beancounter.resource in BeanCounterThreshold.THRESHOLD_KEYS['barrier']: | ||
215 | + quota = beancounter.barrier | ||
216 | + else: | ||
217 | + quota = beancounter.limit | ||
218 | + | ||
219 | + # what are we measuring? | ||
220 | + if use_maxheld: | ||
221 | + value = beancounter.maxheld | ||
222 | + else: | ||
223 | + value = beancounter.held | ||
224 | + | ||
225 | + if value > quota: | ||
226 | + return BeanCounterOvershoot(beancounter.resource, beancounter.uid, value - quota) | ||
227 | + | ||
228 | + # check for critical / warning by comparing our value with the relevant | ||
229 | + # percentage of our quota. return an object with information how much | ||
230 | + # over quota we are. | ||
231 | + if quota and self.critical >= 0 and value >= (quota * (float(self.critical)/100)): | ||
232 | + return BeanCounterCritical(beancounter.resource, beancounter.uid, value - int((quota * (float(self.critical)/100)))) | ||
233 | + if quota and self.warning >= 0 and value >= (quota * (float(self.warning)/100)): | ||
234 | + return BeanCounterWarning(beancounter.resource, beancounter.uid, value - int((quota * (float(self.warning)/100)))) | ||
235 | + return None | ||
236 | + | ||
237 | + | ||
238 | +class BeanCounterViolation(object): | ||
239 | + """Alert containing a BeanCounter violation.""" | ||
240 | + def __init__(self, resource, uid, excess): | ||
241 | + """Create a new BeanCounter violation. | ||
242 | + | ||
243 | + Args: | ||
244 | + resource: str - resource name | ||
245 | + uid: int - context uid | ||
246 | + excess: int - how much the limit was exceeded | ||
247 | + """ | ||
248 | + self.tag = 'VIOL' | ||
249 | + self.resource = resource | ||
250 | + self.uid = uid | ||
251 | + self.excess = excess | ||
252 | + | ||
253 | + def __repr__(self): | ||
254 | + return ('<%s %s: resource %s in context %d by %d>' % | ||
255 | + (self.__class__.__name__, self.tag, self.resource, self.uid, self.excess)) | ||
256 | + | ||
257 | + | ||
258 | +class BeanCounterFailure(BeanCounterViolation): | ||
259 | + """Alert containing a BeanCounter failure.""" | ||
260 | + def __init__(self, resource, uid, excess): | ||
261 | + """Create a new BeanCounter failure. | ||
262 | + | ||
263 | + Args: | ||
264 | + resource: str - resource name | ||
265 | + uid: int - context uid | ||
266 | + excess: int - how much the limit was exceeded | ||
267 | + """ | ||
268 | + super(BeanCounterFailure, self).__init__(resource, uid, excess) | ||
269 | + self.tag = 'FAIL' | ||
270 | + | ||
271 | + | ||
272 | +class BeanCounterOvershoot(BeanCounterViolation): | ||
273 | + """Alert containing a BeanCounter overshoot.""" | ||
274 | + def __init__(self, resource, uid, excess): | ||
275 | + """Create a new BeanCounter overshoot. | ||
276 | + | ||
277 | + Args: | ||
278 | + resource: str - resource name | ||
279 | + uid: int - context uid | ||
280 | + excess: int - how much the limit was exceeded | ||
281 | + """ | ||
282 | + super(BeanCounterOvershoot, self).__init__(resource, uid, excess) | ||
283 | + self.tag = 'OVER' | ||
284 | + | ||
285 | + | ||
286 | +class BeanCounterCritical(BeanCounterViolation): | ||
287 | + """Alert containing a BeanCounter critical.""" | ||
288 | + def __init__(self, resource, uid, excess): | ||
289 | + """Create a new BeanCounter critical alert | ||
290 | + | ||
291 | + Args: | ||
292 | + resource: str - resource name | ||
293 | + uid: int - context uid | ||
294 | + excess: int - how much the limit was exceeded | ||
295 | + """ | ||
296 | + super(BeanCounterCritical, self).__init__(resource, uid, excess) | ||
297 | + self.tag = 'CRIT' | ||
298 | + | ||
299 | + | ||
300 | +class BeanCounterWarning(BeanCounterViolation): | ||
301 | + """Alert containing a BeanCounter warning.""" | ||
302 | + def __init__(self, resource, uid, excess): | ||
303 | + """Create a new BeanCounter warning alert | ||
304 | + | ||
305 | + Args: | ||
306 | + resource: str - resource name | ||
307 | + uid: int - context uid | ||
308 | + excess: int - how much the limit was exceeded | ||
309 | + """ | ||
310 | + super(BeanCounterWarning, self).__init__(resource, uid, excess) | ||
311 | + self.tag = 'WARN' | ||
312 | + | ||
313 | + | ||
314 | +class BeanCounterVerifier(object): | ||
315 | + """Verifier wrapper for bean counters.""" | ||
316 | + def __init__(self, options): | ||
317 | + """Create a new set of verifiers | ||
318 | + | ||
319 | + Args: | ||
320 | + options: optparse.Values, options used: | ||
321 | + warning: int - generic warning level | ||
322 | + critical: int - generic critical level | ||
323 | + res_warning: dict - resource name:int level | ||
324 | + res_critical: dict - resource name:int level | ||
325 | + """ | ||
326 | + # add common thresholds | ||
327 | + self.thresholds = [BeanCounterThreshold(None, options.critical, options.warning)] | ||
328 | + for resource, level in options.res_warning.items(): | ||
329 | + # has both critical and warning values? | ||
330 | + if resource in options.res_critical: | ||
331 | + self.thresholds.append(BeanCounterThreshold(resource, options.res_critical[resource], level)) | ||
332 | + else: | ||
333 | + self.thresholds.append(BeanCounterThreshold(resource, -1, level)) | ||
334 | + for resource, level in options.res_critical.iteritems(): | ||
335 | + # already defined during warning value loop? | ||
336 | + if not resource in options.res_warning: | ||
337 | + self.thresholds.append(BeanCounterThreshold(resource, level, -1)) | ||
338 | + | ||
339 | + def verify(self, beancounters, use_maxheld=False): | ||
340 | + """Verify BeanCounters | ||
341 | + | ||
342 | + Args: | ||
343 | + beancounters: list of BeanCounters | ||
344 | + use_maxheld: bool - use maxheld instead of held counter for limits | ||
345 | + | ||
346 | + Returns: | ||
347 | + list of lists (see BeanCounterThreshold.check) | ||
348 | + """ | ||
349 | + results = [] | ||
350 | + for beancounter in beancounters: | ||
351 | + for threshold in self.thresholds: | ||
352 | + result = threshold.check(beancounter, use_maxheld) | ||
353 | + if result: | ||
354 | + results.append(result) | ||
355 | + return results | ||
356 | + | ||
357 | + | ||
358 | +def shorten_hostname(hostname, parts=2): | ||
359 | + """Shorten hostname by taking only certain parts from the beginning of it. | ||
360 | + | ||
361 | + Args: | ||
362 | + hostname: str - FQDN | ||
363 | + parts: int - number of parts to keep | ||
364 | + """ | ||
365 | + # return at least the hostname | ||
366 | + if (parts < 1): | ||
367 | + parts = 1 | ||
368 | + if hostname: | ||
369 | + return '.'.join(hostname.split('.')[:parts]) | ||
370 | + return None | ||
371 | + | ||
372 | + | ||
373 | +def get_vm_hostname(uid, confpath=OPENVZ_CONFPATH): | ||
374 | + """Get hostname for a vm by getting the hostname from config file | ||
375 | + | ||
376 | + Args: | ||
377 | + uid: int - uid / container context number of vm | ||
378 | + confpath: str - folder in which to find <uid>.conf | ||
379 | + | ||
380 | + Returns: | ||
381 | + str: FQDN as configured, or None | ||
382 | + """ | ||
383 | + try: | ||
384 | + result = re.search(r'HOSTNAME="(.*)"', open(confpath+"/"+str(uid)+".conf", "r").read()) | ||
385 | + if result: | ||
386 | + return result.group(1) | ||
387 | + except IOError: | ||
388 | + return None | ||
389 | + | ||
390 | + | ||
391 | +def get_local_fqdn(): | ||
392 | + """Get local hostname.""" | ||
393 | + return socket.gethostname() | ||
394 | + | ||
395 | + | ||
396 | +def get_hostname(uid, confpath=OPENVZ_CONFPATH, parts=2): | ||
397 | + """Get and shorten OpenVZ vm hostname | ||
398 | + | ||
399 | + Args: | ||
400 | + uid: ints - uid / container context number | ||
401 | + confpath: str - folder in which to find <uid>.conf | ||
402 | + parts: int - number of hostname parts to return | ||
403 | + | ||
404 | + Returns: | ||
405 | + str: hostname | ||
406 | + """ | ||
407 | + # the hypervisor itself, or a VM -> use local hostname | ||
408 | + # NOTE: this assumes a correct confpath and non-existence of it on VMs. | ||
409 | + if uid == 0 or not os.path.exists(confpath): | ||
410 | + hostname = get_local_fqdn() | ||
411 | + else: | ||
412 | + hostname = get_vm_hostname(uid, confpath) | ||
413 | + # drop ending domain parts of FQDN | ||
414 | + return shorten_hostname(hostname, parts) | ||
415 | + | ||
416 | + | ||
417 | +def read_data(options): | ||
418 | + """Read OpenVZ counter data from system | ||
419 | + | ||
420 | + Args: | ||
421 | + options: optparse object: | ||
422 | + sudo: bool - use sudo to read file or not | ||
423 | + filename: str - filename to read | ||
424 | + | ||
425 | + Return: | ||
426 | + str: verbatim file contents | ||
427 | + | ||
428 | + Sideeffects: | ||
429 | + May exit to system directly. | ||
430 | + """ | ||
431 | + # read into a string directly, or with a "sudo cat" | ||
432 | + if not options.sudo: | ||
433 | + try: | ||
434 | + data = open(options.filename, "r").read() | ||
435 | + except IOError, (errorcode, errorstr): | ||
436 | + # file not found? not an OpenVZ system! | ||
437 | + if (errorcode == errno.ENOENT): | ||
438 | + print "OK: Not an OpenVZ system, no need to worry about beancounters." | ||
439 | + sys.exit(0) | ||
440 | + elif (errorcode == errno.EACCES): | ||
441 | + print "UNKNOWN: Not permitted to read beancounter information." | ||
442 | + sys.exit(3) | ||
443 | + else: | ||
444 | + print "CRITICAL: Unknown problem reading beancounters." | ||
445 | + sys.exit(2) | ||
446 | + else: | ||
447 | + # call a non-interactive sudo to read the file | ||
448 | + # needs to be configured via e.g. /etc/sudoers | ||
449 | + try: | ||
450 | + data = subprocess.Popen(["/usr/bin/sudo", "-n", "cat", options.filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] | ||
451 | + except OSError, (errorcode, errorstr): | ||
452 | + print "CRITICAL: Can't execute sudo to read beancounters. (%d - %s)" % (errorcode, errorstr) | ||
453 | + sys.exit(2) | ||
454 | + if not data: | ||
455 | + print "CRITICAL: Trying to read beancounters with sudo didn't yield data." | ||
456 | + sys.exit(2) | ||
457 | + return data | ||
458 | + | ||
459 | + | ||
460 | +def create_optionsparser(): | ||
461 | + """Create optparse.OptionParser object for us | ||
462 | + | ||
463 | + Returns: | ||
464 | + optparse.OptionParser object | ||
465 | + """ | ||
466 | + usage = "usage: %prog [options]" | ||
467 | + parser = optparse.OptionParser(usage=usage, option_class=ExtOption) | ||
468 | + parser.add_option("-w", "--warning", dest="warning", default=90, | ||
469 | + action="store_int", help="default warning threshold percentage for everything (default: %default)") | ||
470 | + parser.add_option("-c", "--critical", dest="critical", default=95, | ||
471 | + action="store_int", help="default critical threshold percentage for everything (default: %default)") | ||
472 | + parser.add_option("-W", "--resource-warning", dest="res_warning", default={}, | ||
473 | + action="keyvalgroup", help="resource specific warning percentage (e.g. foo:50,bar:60)") | ||
474 | + parser.add_option("-C", "--resource-critical", dest="res_critical", default={}, | ||
475 | + action="keyvalgroup", help="resource-specific critical percentage (e.g. foo:90,bar:95)") | ||
476 | + parser.add_option("-f", "--file", dest="filename", default='/proc/user_beancounters', | ||
477 | + action="store", help="read beancounters from file (default: %default)") | ||
478 | + parser.add_option("-s", "--sudo", dest="sudo", default=False, | ||
479 | + action="store_true", help="use 'sudo cat filename' to read file as root (filename see -f)") | ||
480 | + parser.add_option("-p", "--configs", dest="confpath", default=OPENVZ_CONFPATH, | ||
481 | + action="store", help="path for OpenVZ config path (to get hostnames, default: %default)") | ||
482 | + parser.add_option("-d", "--domainparts", dest="domainparts", default=2, | ||
483 | + action="store_int", help="how many domain parts of the hostname to keep (default: %default)") | ||
484 | + parser.add_option("-u", "--ubc-store-path", dest="storepath", default=OPENVZ_UBCSTORE, | ||
485 | + action="store", help="path for vzubc relative information (default: %default)") | ||
486 | + parser.add_option("-m", "--use-maxheld", dest="use_maxheld", default=False, | ||
487 | + action="store_true", help="use resource's maxheld instead of current_held (default: %default)") | ||
488 | + parser.add_option("-a", "--absolute-fails", dest="use_absolute_failcnt", default=False, | ||
489 | + action="store_true", help="don't use vzubc's relative fail counters (default: %default)") | ||
490 | + return parser | ||
491 | + | ||
492 | + | ||
493 | +class ExtOption(optparse.Option): | ||
494 | + """Specialized Option class which parses key:val,key:val parameters and integers | ||
495 | + See optparse documentation for detailed explanation of how this works.""" | ||
496 | + ACTIONS = optparse.Option.ACTIONS + ("keyvalgroup", "store_int",) | ||
497 | + STORE_ACTIONS = optparse.Option.STORE_ACTIONS + ("keyvalgroup", "store_int",) | ||
498 | + TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + ("keyvalgroup", "store_int",) | ||
499 | + ALWAYS_TYPED_ACTIONS = optparse.Option.ALWAYS_TYPED_ACTIONS + ("keyvalgroup", "store_int",) | ||
500 | + | ||
501 | + def take_action(self, action, dest, opt, value, values, parser): | ||
502 | + if action == "keyvalgroup": | ||
503 | + keyvals = value.split(",") | ||
504 | + for keyval in keyvals: | ||
505 | + key, val = keyval.split(":") | ||
506 | + if re.match(r'\d+', val): | ||
507 | + val = int(val) | ||
508 | + values.ensure_value(dest, {}).update({key:val}) | ||
509 | + elif action == "store_int": | ||
510 | + setattr(values, dest, int(value)) | ||
511 | + else: | ||
512 | + optparse.Option.take_action(self, action, dest, opt, value, values, parser) | ||
513 | + | ||
514 | + | ||
515 | +def __main__(): | ||
516 | + optionsparser = create_optionsparser() | ||
517 | + # args parsing will exit the program if options are used wrong or help is | ||
518 | + # requested. optionsparser.error exists to system as well - we call it when | ||
519 | + # extra arguments are given. none are expected, we use options only. | ||
520 | + options, args = optionsparser.parse_args() | ||
521 | + if args: | ||
522 | + optionsparser.error("incorrect number of arguments") | ||
523 | + | ||
524 | + # NOTE: read_data itself may exit to system in some cases | ||
525 | + data = read_data(options) | ||
526 | + if not data: | ||
527 | + print "CRITICAL: No data given while reading beancounters." | ||
528 | + sys.exit(2) | ||
529 | + | ||
530 | + # parse beancounters, create verifiers, verify beancounters | ||
531 | + try: | ||
532 | + beancounterparser = BeanCounterParser(data, options.use_absolute_failcnt, options.storepath) | ||
533 | + except NoBeanCountersFoundError: | ||
534 | + print "CRITICAL: No beancounters found in data read." | ||
535 | + sys.exit(2) | ||
536 | + | ||
537 | + beancounterverifier = BeanCounterVerifier(options) | ||
538 | + beancounteralerts = beancounterverifier.verify(beancounterparser.get_beancounters(), options.use_maxheld) | ||
539 | + | ||
540 | + # find uids and combine alert groups | ||
541 | + fail = {} | ||
542 | + over = {} | ||
543 | + crit = {} | ||
544 | + warn = {} | ||
545 | + for alert in beancounteralerts: | ||
546 | + if isinstance (alert, BeanCounterFailure): | ||
547 | + fail.setdefault(alert.uid, {}) | ||
548 | + fail[alert.uid][alert.resource] = alert.excess | ||
549 | + elif isinstance (alert, BeanCounterOvershoot): | ||
550 | + over.setdefault(alert.uid, {}) | ||
551 | + over[alert.uid][alert.resource] = alert.excess | ||
552 | + elif isinstance (alert, BeanCounterCritical): | ||
553 | + crit.setdefault(alert.uid, {}) | ||
554 | + crit[alert.uid][alert.resource] = alert.excess | ||
555 | + elif isinstance (alert, BeanCounterWarning): | ||
556 | + warn.setdefault(alert.uid, {}) | ||
557 | + warn[alert.uid][alert.resource] = alert.excess | ||
558 | + | ||
559 | + # default message & exitcode if everything is fine | ||
560 | + message = "OK: all beancounters below configured thresholds" | ||
561 | + perfdata = "" | ||
562 | + exitcode = 0 | ||
563 | + | ||
564 | + # set message prefix and errocode accordingly | ||
565 | + if fail or over or crit: | ||
566 | + message = "CRITICAL:" | ||
567 | + exitcode = 2 | ||
568 | + elif warn: | ||
569 | + message = "WARNING:" | ||
570 | + exitcode = 1 | ||
571 | + | ||
572 | + # combine specific Nagios message(s) | ||
573 | + for level, tag in (fail, 'FAIL'), (over, 'OVER'), (crit, 'CRIT'), (warn, 'WARN'): | ||
574 | + if level: | ||
575 | + message += " %s:" % tag | ||
576 | + perfdata += " %s:" % tag | ||
577 | + for uid, counters in level.items(): | ||
578 | + hostname = get_hostname(uid, options.confpath, options.domainparts) | ||
579 | + message += " %s(%d)" % (hostname, uid) | ||
580 | + perfdata += " HOST %s(%d):" % (hostname, uid) | ||
581 | + for resource, counter in counters.items(): | ||
582 | + perfdata += " %s(+%d)" % (resource, counter) | ||
583 | + | ||
584 | + # output message & exit | ||
585 | + if len(perfdata): | ||
586 | + print "%s|%s" % (message, perfdata) | ||
587 | + else: | ||
588 | + print message | ||
589 | + sys.exit(exitcode) | ||
590 | + | ||
591 | + | ||
592 | +if __name__ == "__main__": | ||
593 | + __main__() | ||
594 | Binary files nothing/check_openvz_beans.pyc and nagios-plugins-openvz-beans/check_openvz_beans.pyc differ | ||
595 | diff -urN nothing/check_openvz_beans-test.py nagios-plugins-openvz-beans/check_openvz_beans-test.py | ||
596 | --- nothing/check_openvz_beans-test.py 1970-01-01 01:00:00.000000000 +0100 | ||
597 | +++ nagios-plugins-openvz-beans/check_openvz_beans-test.py 2011-11-01 18:28:47.867345265 +0100 | ||
598 | @@ -0,0 +1,775 @@ | ||
599 | +#!/usr/bin/python | ||
600 | +"""check_openvz_beans unit tests.""" | ||
601 | + | ||
602 | +__author__ = 'Andreas Kotes <andreas.kotes@nokia.com>' | ||
603 | + | ||
604 | +import __builtin__ | ||
605 | +import os | ||
606 | +import sys | ||
607 | +import unittest | ||
608 | +import mox | ||
609 | +import optparse | ||
610 | +import StringIO | ||
611 | +import subprocess | ||
612 | +import check_openvz_beans | ||
613 | + | ||
614 | +class BeanCounterTest(mox.MoxTestBase): | ||
615 | + """Test the BeanCounter class.""" | ||
616 | + def testCreate(self): | ||
617 | + beancounter = check_openvz_beans.BeanCounter(123, "dummy 1 2 3 4 5") | ||
618 | + self.assertEquals(beancounter.uid, 123) | ||
619 | + self.assertEquals(beancounter.resource, 'dummy') | ||
620 | + self.assertEquals(beancounter.held, 1) | ||
621 | + self.assertEquals(beancounter.maxheld, 2) | ||
622 | + self.assertEquals(beancounter.barrier, 3) | ||
623 | + self.assertEquals(beancounter.limit, 4) | ||
624 | + self.assertEquals(beancounter.failcnt, 5) | ||
625 | + self.assertEquals(beancounter.__repr__(), | ||
626 | + '<BeanCounter for uid 123 resource dummy: held 1 maxheld 2 barrier 3 limit 4 failcnt 5>') | ||
627 | + self.assertTrue(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 2 3 4 5")) | ||
628 | + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(124, "dummy 1 2 3 4 5")) | ||
629 | + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "yummy 1 2 3 4 5")) | ||
630 | + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 2 2 3 4 5")) | ||
631 | + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 3 3 4 5")) | ||
632 | + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 2 4 4 5")) | ||
633 | + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 2 3 5 5")) | ||
634 | + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 2 3 4 6")) | ||
635 | + | ||
636 | + | ||
637 | +class BeanCounterParserTest(mox.MoxTestBase): | ||
638 | + """Test the BeanCounterParser class.""" | ||
639 | + def setUp(self): | ||
640 | + super(BeanCounterParserTest, self).setUp() | ||
641 | + | ||
642 | + def test_init_and_parsebeancounters(self): | ||
643 | + beancounters = check_openvz_beans.BeanCounterParser("123: dummy1 1 2 3 4 5\n dummy2 54 63 62 13 53\n234: dummy3 9 8 7 6 5\n") | ||
644 | + self.assertEquals(beancounters.get_beancounters(), [ | ||
645 | + check_openvz_beans.BeanCounter(123, ' dummy1 1 2 3 4 5'), | ||
646 | + check_openvz_beans.BeanCounter(123, ' dummy2 54 63 62 13 53'), | ||
647 | + check_openvz_beans.BeanCounter(234, ' dummy3 9 8 7 6 5')]) | ||
648 | + | ||
649 | + def test_parsefail(self): | ||
650 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
651 | + self.assertRaises(check_openvz_beans.NoBeanCountersFoundError, check_openvz_beans.BeanCounterParser, "nonsense") | ||
652 | + | ||
653 | + | ||
654 | +class BeanCounterThresholdTest(mox.MoxTestBase): | ||
655 | + """Test the BeanCounterThreshold class.""" | ||
656 | + def setUp(self): | ||
657 | + super(BeanCounterThresholdTest, self).setUp() | ||
658 | + # Mock BeanCounters | ||
659 | + self.beancounter_mock = self.mox.CreateMock(check_openvz_beans.BeanCounter) | ||
660 | + self.beancounter_mock.uid = 123 | ||
661 | + self.beancounter_mock.failcnt = 0 | ||
662 | + | ||
663 | + def testCreateSimple(self): | ||
664 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
665 | + self.assertEquals(threshold.resource, None) | ||
666 | + self.assertEquals(threshold.critical, 95) | ||
667 | + self.assertEquals(threshold.warning, 90) | ||
668 | + self.assertEquals(threshold.__repr__(), | ||
669 | + '<BeanCounterThreshold for resource *: critical 95 warning 90>') | ||
670 | + | ||
671 | + def testCreateCustom(self): | ||
672 | + threshold = check_openvz_beans.BeanCounterThreshold('dummy', 70, 60) | ||
673 | + self.assertEquals(threshold.resource, 'dummy') | ||
674 | + self.assertEquals(threshold.critical, 70) | ||
675 | + self.assertEquals(threshold.warning, 60) | ||
676 | + self.assertEquals(threshold.__repr__(), | ||
677 | + '<BeanCounterThreshold for resource dummy: critical 70 warning 60>') | ||
678 | + | ||
679 | + def testCheckSimpleOkay(self): | ||
680 | + self.beancounter_mock.resource = 'dummy_okay' | ||
681 | + self.beancounter_mock.held = 1 | ||
682 | + self.beancounter_mock.maxheld = 2 | ||
683 | + self.beancounter_mock.barrier = 3 | ||
684 | + self.beancounter_mock.limit = 4 | ||
685 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
686 | + self.assertFalse(threshold.check(self.beancounter_mock)) | ||
687 | + | ||
688 | + def testCheckSimpleOkayNegWarn(self): | ||
689 | + self.beancounter_mock.resource = 'dummy_okay' | ||
690 | + self.beancounter_mock.held = 1 | ||
691 | + self.beancounter_mock.maxheld = 2 | ||
692 | + self.beancounter_mock.barrier = 3 | ||
693 | + self.beancounter_mock.limit = 4 | ||
694 | + threshold = check_openvz_beans.BeanCounterThreshold(warning=-1) | ||
695 | + self.assertEqual(threshold.check(self.beancounter_mock), None) | ||
696 | + | ||
697 | + def testCheckSimpleOkayNegCrit(self): | ||
698 | + self.beancounter_mock.resource = 'dummy_okay' | ||
699 | + self.beancounter_mock.held = 1 | ||
700 | + self.beancounter_mock.maxheld = 2 | ||
701 | + self.beancounter_mock.barrier = 3 | ||
702 | + self.beancounter_mock.limit = 4 | ||
703 | + threshold = check_openvz_beans.BeanCounterThreshold(critical=-1) | ||
704 | + self.assertEqual(threshold.check(self.beancounter_mock), None) | ||
705 | + | ||
706 | + def testCheckSimpleFail(self): | ||
707 | + # failcnt engaged | ||
708 | + self.beancounter_mock.resource = 'dummy_fail' | ||
709 | + self.beancounter_mock.held = 1 | ||
710 | + self.beancounter_mock.maxheld = 2 | ||
711 | + self.beancounter_mock.barrier = 3 | ||
712 | + self.beancounter_mock.limit = 4 | ||
713 | + self.beancounter_mock.failcnt = 1 | ||
714 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
715 | + result = threshold.check(self.beancounter_mock) | ||
716 | + self.assertTrue(result) | ||
717 | + self.assertEquals(result.tag, 'FAIL') | ||
718 | + | ||
719 | + def testCheckSimpleEqual(self): | ||
720 | + # eq Limit | ||
721 | + self.beancounter_mock.resource = 'dummy_equal' | ||
722 | + self.beancounter_mock.held = 50 | ||
723 | + self.beancounter_mock.maxheld = 100 | ||
724 | + self.beancounter_mock.barrier = 100 | ||
725 | + self.beancounter_mock.limit = 100 | ||
726 | + threshold = check_openvz_beans.BeanCounterThreshold(critical=101, warning=101) | ||
727 | + self.assertFalse(threshold.check(self.beancounter_mock)) | ||
728 | + | ||
729 | + def testCheckSimpleOverGt(self): | ||
730 | + # over Limit | ||
731 | + self.beancounter_mock.resource = 'dummy_over' | ||
732 | + self.beancounter_mock.held = 4 | ||
733 | + self.beancounter_mock.maxheld = 4 | ||
734 | + self.beancounter_mock.barrier = 3 | ||
735 | + self.beancounter_mock.limit = 3 | ||
736 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
737 | + result = threshold.check(self.beancounter_mock) | ||
738 | + self.assertTrue(result) | ||
739 | + self.assertEquals(result.tag, 'OVER') | ||
740 | + | ||
741 | + def testCheckSimpleCritEq(self): | ||
742 | + # eq critical value | ||
743 | + self.beancounter_mock.resource = 'dummy_crit_eq' | ||
744 | + self.beancounter_mock.held = 95 | ||
745 | + self.beancounter_mock.maxheld = 95 | ||
746 | + self.beancounter_mock.barrier = 100 | ||
747 | + self.beancounter_mock.limit = 100 | ||
748 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
749 | + result = threshold.check(self.beancounter_mock) | ||
750 | + self.assertTrue(result) | ||
751 | + self.assertEquals(result.tag, 'CRIT') | ||
752 | + | ||
753 | + def testCheckSimpleCritGt(self): | ||
754 | + # gt critical value | ||
755 | + self.beancounter_mock.resource = 'dummy_crit_gt' | ||
756 | + self.beancounter_mock.held = 96 | ||
757 | + self.beancounter_mock.maxheld = 96 | ||
758 | + self.beancounter_mock.barrier = 100 | ||
759 | + self.beancounter_mock.limit = 100 | ||
760 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
761 | + result = threshold.check(self.beancounter_mock) | ||
762 | + self.assertTrue(result) | ||
763 | + self.assertEquals(result.tag, 'CRIT') | ||
764 | + | ||
765 | + def testCheckSimpleWarnEq(self): | ||
766 | + # eq warning value | ||
767 | + self.beancounter_mock.resource = 'dummy_warn_eq' | ||
768 | + self.beancounter_mock.held = 90 | ||
769 | + self.beancounter_mock.maxheld = 90 | ||
770 | + self.beancounter_mock.barrier = 100 | ||
771 | + self.beancounter_mock.limit = 100 | ||
772 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
773 | + result = threshold.check(self.beancounter_mock) | ||
774 | + self.assertTrue(result) | ||
775 | + self.assertEquals(result.tag, 'WARN') | ||
776 | + | ||
777 | + def testCheckSimpleWarnGt(self): | ||
778 | + # gt warning value | ||
779 | + self.beancounter_mock.resource = 'dummy_warn_gt' | ||
780 | + self.beancounter_mock.held = 91 | ||
781 | + self.beancounter_mock.maxheld = 91 | ||
782 | + self.beancounter_mock.barrier = 100 | ||
783 | + self.beancounter_mock.limit = 100 | ||
784 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
785 | + result = threshold.check(self.beancounter_mock) | ||
786 | + self.assertTrue(result) | ||
787 | + self.assertEquals(result.tag, 'WARN') | ||
788 | + | ||
789 | + def testCheckBarrierQuota(self): | ||
790 | + # limited by barrier -> would not warn if limited by limit | ||
791 | + self.beancounter_mock.resource = 'numflock' | ||
792 | + self.beancounter_mock.held = 45 | ||
793 | + self.beancounter_mock.maxheld = 45 | ||
794 | + self.beancounter_mock.barrier = 50 | ||
795 | + self.beancounter_mock.limit = 100 | ||
796 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
797 | + result = threshold.check(self.beancounter_mock) | ||
798 | + self.assertTrue(result) | ||
799 | + self.assertEquals(result.tag, 'WARN') | ||
800 | + | ||
801 | + def testCheckLimitQuota(self): | ||
802 | + # limited by limit -> would be over if limited by barrier | ||
803 | + self.beancounter_mock.resource = 'numproc' | ||
804 | + self.beancounter_mock.held = 50 | ||
805 | + self.beancounter_mock.maxheld = 56 | ||
806 | + self.beancounter_mock.barrier = 50 | ||
807 | + self.beancounter_mock.limit = 100 | ||
808 | + threshold = check_openvz_beans.BeanCounterThreshold() | ||
809 | + self.assertFalse(threshold.check(self.beancounter_mock)) | ||
810 | + | ||
811 | + def testCheckNotResponsible(self): | ||
812 | + # gt critical value | ||
813 | + self.beancounter_mock.resource = 'dummy_crit_gt' | ||
814 | + self.beancounter_mock.held = 50 | ||
815 | + self.beancounter_mock.maxheld = 96 | ||
816 | + self.beancounter_mock.barrier = 100 | ||
817 | + self.beancounter_mock.limit = 100 | ||
818 | + threshold = check_openvz_beans.BeanCounterThreshold('differentresource') | ||
819 | + self.assertFalse(threshold.check(self.beancounter_mock)) | ||
820 | + | ||
821 | + | ||
822 | +class BeanCounterViolationTest(unittest.TestCase): | ||
823 | + """Test the BeanCounterViolation class.""" | ||
824 | + def testCreate(self): | ||
825 | + violation = check_openvz_beans.BeanCounterViolation('dummy', 123, 10) | ||
826 | + self.assertEquals(violation.uid, 123) | ||
827 | + self.assertEquals(violation.resource, 'dummy') | ||
828 | + self.assertEquals(violation.tag, 'VIOL') | ||
829 | + self.assertEquals(violation.excess, 10) | ||
830 | + self.assertEquals(violation.__repr__(), | ||
831 | + '<BeanCounterViolation VIOL: resource dummy in context 123 by 10>') | ||
832 | + | ||
833 | + | ||
834 | +class BeanCounterFailureTest(unittest.TestCase): | ||
835 | + """Test the BeanCounterFailure class.""" | ||
836 | + def testCreate(self): | ||
837 | + violation = check_openvz_beans.BeanCounterFailure('dummy', 123, 10) | ||
838 | + self.assertEquals(violation.uid, 123) | ||
839 | + self.assertEquals(violation.resource, 'dummy') | ||
840 | + self.assertEquals(violation.tag, 'FAIL') | ||
841 | + self.assertEquals(violation.excess, 10) | ||
842 | + self.assertEquals(violation.__repr__(), | ||
843 | + '<BeanCounterFailure FAIL: resource dummy in context 123 by 10>') | ||
844 | + | ||
845 | + | ||
846 | +class BeanCounterOvershootTest(unittest.TestCase): | ||
847 | + """Test the BeanCounterOvershoot class.""" | ||
848 | + def testCreate(self): | ||
849 | + violation = check_openvz_beans.BeanCounterOvershoot('dummy', 123, 10) | ||
850 | + self.assertEquals(violation.uid, 123) | ||
851 | + self.assertEquals(violation.resource, 'dummy') | ||
852 | + self.assertEquals(violation.tag, 'OVER') | ||
853 | + self.assertEquals(violation.excess, 10) | ||
854 | + self.assertEquals(violation.__repr__(), | ||
855 | + '<BeanCounterOvershoot OVER: resource dummy in context 123 by 10>') | ||
856 | + | ||
857 | + | ||
858 | +class BeanCounterCritical(unittest.TestCase): | ||
859 | + """Test the BeanCounterCritical class.""" | ||
860 | + def testCreate(self): | ||
861 | + violation = check_openvz_beans.BeanCounterCritical('dummy', 123, 10) | ||
862 | + self.assertEquals(violation.uid, 123) | ||
863 | + self.assertEquals(violation.resource, 'dummy') | ||
864 | + self.assertEquals(violation.tag, 'CRIT') | ||
865 | + self.assertEquals(violation.excess, 10) | ||
866 | + self.assertEquals(violation.__repr__(), | ||
867 | + '<BeanCounterCritical CRIT: resource dummy in context 123 by 10>') | ||
868 | + | ||
869 | + | ||
870 | +class BeanCounterWarningTest(unittest.TestCase): | ||
871 | + """Test the BeanCounterWarning class.""" | ||
872 | + def testCreate(self): | ||
873 | + violation = check_openvz_beans.BeanCounterWarning('dummy', 123, 10) | ||
874 | + self.assertEquals(violation.uid, 123) | ||
875 | + self.assertEquals(violation.resource, 'dummy') | ||
876 | + self.assertEquals(violation.tag, 'WARN') | ||
877 | + self.assertEquals(violation.excess, 10) | ||
878 | + self.assertEquals(violation.__repr__(), | ||
879 | + '<BeanCounterWarning WARN: resource dummy in context 123 by 10>') | ||
880 | + | ||
881 | + | ||
882 | +class BeanCounterVerifierInitTest(mox.MoxTestBase): | ||
883 | + """Test the BeanCounterVerifier class (1/2).""" | ||
884 | + def setUp(self): | ||
885 | + super(BeanCounterVerifierInitTest, self).setUp() | ||
886 | + # Mock BeanCounterThresholds | ||
887 | + self.values_mock = self.mox.CreateMock(optparse.Values) | ||
888 | + self.threshold_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterThreshold) | ||
889 | + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterThreshold') | ||
890 | + | ||
891 | + # check with common thresholds | ||
892 | + self.values_mock.critical = 95 | ||
893 | + self.values_mock.warning = 90 | ||
894 | + check_openvz_beans.BeanCounterThreshold(None, 95, 90) | ||
895 | + | ||
896 | + # check with resource-specific thresholds | ||
897 | + self.values_mock.res_critical = {'foo':20, 'bar':30} | ||
898 | + self.values_mock.res_warning = {'foo':10, 'baz':20} | ||
899 | + check_openvz_beans.BeanCounterThreshold('foo', 20, 10) | ||
900 | + check_openvz_beans.BeanCounterThreshold('baz', -1, 20) | ||
901 | + check_openvz_beans.BeanCounterThreshold('bar', 30, -1) | ||
902 | + | ||
903 | + self.mox.ReplayAll() | ||
904 | + | ||
905 | + def test_init(self): | ||
906 | + beancounterverifier = check_openvz_beans.BeanCounterVerifier(self.values_mock) | ||
907 | + | ||
908 | + | ||
909 | +class BeanCounterVerifierVerifyTest(mox.MoxTestBase): | ||
910 | + """Test the BeanCounterVerifier class (2/2).""" | ||
911 | + def setUp(self): | ||
912 | + super(BeanCounterVerifierVerifyTest, self).setUp() | ||
913 | + # Mock BeanCounterThresholds | ||
914 | + self.values_mock = self.mox.CreateMock(optparse.Values) | ||
915 | + self.threshold_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterThreshold) | ||
916 | + self.beancounter_mock = self.mox.CreateMock(check_openvz_beans.BeanCounter) | ||
917 | + self.violation_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterViolation) | ||
918 | + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterThreshold') | ||
919 | + self.mox.StubOutWithMock(check_openvz_beans.BeanCounterThreshold, 'check') | ||
920 | + | ||
921 | + # check with common thresholds | ||
922 | + self.values_mock.critical = 95 | ||
923 | + self.values_mock.warning = 90 | ||
924 | + self.values_mock.res_critical = {} | ||
925 | + self.values_mock.res_warning = {} | ||
926 | + check_openvz_beans.BeanCounterThreshold(None, 95, 90).AndReturn(self.threshold_mock) | ||
927 | + self.threshold_mock.check(mox.IsA(check_openvz_beans.BeanCounter), False) | ||
928 | + self.threshold_mock.check(mox.IsA(check_openvz_beans.BeanCounter), False).AndReturn(self.violation_mock) | ||
929 | + self.threshold_mock.check(mox.IsA(check_openvz_beans.BeanCounter), False).AndReturn(self.violation_mock) | ||
930 | + self.threshold_mock.check(mox.IsA(check_openvz_beans.BeanCounter), False).AndReturn(self.violation_mock) | ||
931 | + | ||
932 | + self.mox.ReplayAll() | ||
933 | + | ||
934 | + def test_verify(self): | ||
935 | + beancounterverifier = check_openvz_beans.BeanCounterVerifier(self.values_mock) | ||
936 | + beancounter_mock = self.mox.CreateMock(check_openvz_beans.BeanCounter) | ||
937 | + # first one is okay | ||
938 | + self.assertEquals(len(beancounterverifier.verify([beancounter_mock,], False)), 0) | ||
939 | + # second one has a violation | ||
940 | + self.assertEquals(len(beancounterverifier.verify([beancounter_mock,], False)), 1) | ||
941 | + # third + fourth each have a violation | ||
942 | + self.assertEquals(len(beancounterverifier.verify([beancounter_mock, beancounter_mock], False)), 2) | ||
943 | + | ||
944 | + | ||
945 | +class ExtOptionTest(mox.MoxTestBase): | ||
946 | + """Test the ExtOption class.""" | ||
947 | + def setUp(self): | ||
948 | + super(ExtOptionTest, self).setUp() | ||
949 | + # Mock optparse objects | ||
950 | + self.values1 = self.mox.CreateMockAnything() | ||
951 | + self.values1.ensure_value('foobar', {}).AndReturn(self.values1) | ||
952 | + self.values1.update({'key1': 'val1'}) | ||
953 | + self.values1.ensure_value('foobar', {}).AndReturn(self.values1) | ||
954 | + self.values1.update({'key2': 'val2'}) | ||
955 | + | ||
956 | + self.values2 = self.mox.CreateMockAnything() | ||
957 | + self.values2.intval = 0 | ||
958 | + | ||
959 | + self.values3 = self.mox.CreateMockAnything() | ||
960 | + | ||
961 | + self.mox.StubOutWithMock(optparse.Option, '__init__') | ||
962 | + self.mox.StubOutWithMock(optparse.Option, 'take_action') | ||
963 | + optparse.Option.__init__() | ||
964 | + optparse.Option.take_action(mox.IsA(optparse.Option), 'something_else', None, None, None, None, None) | ||
965 | + | ||
966 | + self.mox.ReplayAll() | ||
967 | + | ||
968 | + def test_take_action(self): | ||
969 | + option = check_openvz_beans.ExtOption() | ||
970 | + option.take_action('keyvalgroup', 'foobar', 'dunno', 'key1:val1,key2:val2', self.values1, 'dunno') | ||
971 | + | ||
972 | + self.assertEquals(self.values2.intval, 0) | ||
973 | + option.take_action('store_int', 'intval', 'dunno', '10', self.values2, 'dunno') | ||
974 | + self.assertEquals(self.values2.intval, 10) | ||
975 | + | ||
976 | + option.take_action('something_else', None, None, None, None, None) | ||
977 | + | ||
978 | + | ||
979 | +class ReadDataTest(mox.MoxTestBase): | ||
980 | + """Check read_data functions without sudo.""" | ||
981 | + def setUp(self): | ||
982 | + super(ReadDataTest, self).setUp() | ||
983 | + # Mock optparse objects | ||
984 | + self.options_mock = self.mox.CreateMock(optparse.Values) | ||
985 | + self.popen_mock = self.mox.CreateMock(subprocess.Popen) | ||
986 | + self.mox.StubOutWithMock(subprocess, 'Popen') | ||
987 | + self.saved_stdout = sys.stdout | ||
988 | + sys.stdout = StringIO.StringIO() | ||
989 | + | ||
990 | + def tearDown(self): | ||
991 | + super(ReadDataTest, self).tearDown() | ||
992 | + sys.stdout = self.saved_stdout | ||
993 | + | ||
994 | + def test_read_data(self): | ||
995 | + """Test reading OpenVZ counter data from system without sudo - existing file.""" | ||
996 | + self.options_mock.sudo = False | ||
997 | + self.options_mock.filename = 'testdata/hostcrit.bcs' | ||
998 | + self.mox.ReplayAll() | ||
999 | + | ||
1000 | + result = check_openvz_beans.read_data(self.options_mock) | ||
1001 | + | ||
1002 | + self.assertTrue(isinstance(result, str)) | ||
1003 | + self.assertEquals(len(result), os.path.getsize(self.options_mock.filename)) | ||
1004 | + | ||
1005 | + def test_read_data_missing_file(self): | ||
1006 | + """Test reading OpenVZ counter data from system without sudo - missing file.""" | ||
1007 | + self.options_mock.sudo = False | ||
1008 | + self.options_mock.filename = '/nonexisting/foobar' | ||
1009 | + self.mox.ReplayAll() | ||
1010 | + | ||
1011 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1012 | + self.assertRaises(SystemExit, check_openvz_beans.read_data, self.options_mock) | ||
1013 | + self.assertEqual(sys.stdout.getvalue(), 'OK: Not an OpenVZ system, no need to worry about beancounters.\n') | ||
1014 | + | ||
1015 | + def test_read_data_missing_file(self): | ||
1016 | + """Test reading OpenVZ counter data from system without sudo - unreadable file.""" | ||
1017 | + self.options_mock.sudo = False | ||
1018 | + self.options_mock.filename = 'testdata/unreadable.bcs' | ||
1019 | + try: | ||
1020 | + os.unlink(self.options_mock.filename) | ||
1021 | + except OSError: | ||
1022 | + pass | ||
1023 | + | ||
1024 | + open(self.options_mock.filename, 'w').close() | ||
1025 | + os.chmod(self.options_mock.filename, 0000) | ||
1026 | + | ||
1027 | + self.mox.ReplayAll() | ||
1028 | + | ||
1029 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1030 | + self.assertRaises(SystemExit, check_openvz_beans.read_data, self.options_mock) | ||
1031 | + self.assertEqual(sys.stdout.getvalue(), 'UNKNOWN: Not permitted to read beancounter information.\n') | ||
1032 | + | ||
1033 | + try: | ||
1034 | + os.unlink(self.options_mock.filename) | ||
1035 | + except OSError: | ||
1036 | + pass | ||
1037 | + | ||
1038 | + # FIXME: not all code pathes completely tested due to unmockability of open() | ||
1039 | + | ||
1040 | + def test_read_data_sudo(self): | ||
1041 | + """Test reading OpenVZ counter data from system with sudo - sucess.""" | ||
1042 | + self.options_mock.sudo = True | ||
1043 | + self.options_mock.filename = 'irrelevant' | ||
1044 | + subprocess.Popen(['/usr/bin/sudo', '-n', 'cat', 'irrelevant'], stderr=-1, stdout=-1).AndReturn(self.popen_mock) | ||
1045 | + self.popen_mock.communicate().AndReturn(['somedata']) | ||
1046 | + self.mox.ReplayAll() | ||
1047 | + | ||
1048 | + result = check_openvz_beans.read_data(self.options_mock) | ||
1049 | + | ||
1050 | + self.assertTrue(isinstance(result, str)) | ||
1051 | + self.assertEquals(result, 'somedata') | ||
1052 | + | ||
1053 | + def test_read_data_sudo_oserror(self): | ||
1054 | + """Test reading OpenVZ counter data from system with sudo - OSError.""" | ||
1055 | + self.options_mock.sudo = True | ||
1056 | + self.options_mock.filename = 'irrelevant' | ||
1057 | + subprocess.Popen(['/usr/bin/sudo', '-n', 'cat', 'irrelevant'], stderr=-1, stdout=-1).AndRaise(OSError(42, 'mocketimock')) | ||
1058 | + self.mox.ReplayAll() | ||
1059 | + | ||
1060 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1061 | + self.assertRaises(SystemExit, check_openvz_beans.read_data, self.options_mock) | ||
1062 | + self.assertEqual(sys.stdout.getvalue(), "CRITICAL: Can't execute sudo to read beancounters. (42 - mocketimock)\n") | ||
1063 | + | ||
1064 | + def test_read_data_sudo_nodata(self): | ||
1065 | + """Test reading OpenVZ counter data from system with sudo - no data.""" | ||
1066 | + self.options_mock.sudo = True | ||
1067 | + self.options_mock.filename = 'irrelevant' | ||
1068 | + | ||
1069 | + subprocess.Popen(['/usr/bin/sudo', '-n', 'cat', 'irrelevant'], stderr=-1, stdout=-1).AndReturn(self.popen_mock) | ||
1070 | + self.popen_mock.communicate().AndReturn(['']) | ||
1071 | + self.mox.ReplayAll() | ||
1072 | + | ||
1073 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1074 | + self.assertRaises(SystemExit, check_openvz_beans.read_data, self.options_mock) | ||
1075 | + self.assertEqual(sys.stdout.getvalue(), "CRITICAL: Trying to read beancounters with sudo didn't yield data.\n") | ||
1076 | + | ||
1077 | + | ||
1078 | +class GlobalsTest(mox.MoxTestBase): | ||
1079 | + """Check global functions.""" | ||
1080 | + def setUp(self): | ||
1081 | + super(GlobalsTest, self).setUp() | ||
1082 | + self.saved_stdout, sys.stdout = sys.stdout, StringIO.StringIO() | ||
1083 | + | ||
1084 | + def tearDown(self): | ||
1085 | + super(GlobalsTest, self).tearDown() | ||
1086 | + sys.stdout = self.saved_stdout | ||
1087 | + | ||
1088 | + def test_shorten_hostname(self): | ||
1089 | + """Test shortening hostnames.""" | ||
1090 | + self.testfqdn = "hostname.with.long.domain.name" | ||
1091 | + # the (short) hostname should be returned in any case | ||
1092 | + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 0), "hostname") | ||
1093 | + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 1), "hostname") | ||
1094 | + # various lengths should be returned properly | ||
1095 | + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 2), "hostname.with") | ||
1096 | + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 5), "hostname.with.long.domain.name") | ||
1097 | + # if more is requested than is there, just give all there is | ||
1098 | + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 42), "hostname.with.long.domain.name") | ||
1099 | + | ||
1100 | + def test_get_vm_hostname(self): | ||
1101 | + """Test getting vm hostnames.""" | ||
1102 | + # two known existing hosts | ||
1103 | + self.assertEqual(check_openvz_beans.get_vm_hostname(10, 'testdata'), 'vmokay.pr.foo.test.your.do.main') | ||
1104 | + self.assertEqual(check_openvz_beans.get_vm_hostname(60, 'testdata'), 'vmover1.st.foo.test.your.do.main') | ||
1105 | + # one host whose config file doesn't contain a hostname | ||
1106 | + self.assertEqual(check_openvz_beans.get_vm_hostname(99, 'testdata'), None) | ||
1107 | + # one host where no config file exists | ||
1108 | + self.assertEqual(check_openvz_beans.get_vm_hostname(90, 'testdata'), None) | ||
1109 | + | ||
1110 | + def test_get_local_fqdn(self): | ||
1111 | + """Test getting local hostnames.""" | ||
1112 | + # due to the local nature of hostnames we can only check wether it has a qualifying '.' in it | ||
1113 | + self.assertTrue(check_openvz_beans.get_local_fqdn().find('.') > 0) | ||
1114 | + | ||
1115 | + def test_get_hostname(self): | ||
1116 | + """Test getting and shortening hostnames.""" | ||
1117 | + self.mox.StubOutWithMock(check_openvz_beans, 'get_vm_hostname') | ||
1118 | + self.mox.StubOutWithMock(check_openvz_beans, 'get_local_fqdn') | ||
1119 | + check_openvz_beans.get_local_fqdn().AndReturn('localhost.localdomain') | ||
1120 | + check_openvz_beans.get_local_fqdn().AndReturn('localhost.localdomain') | ||
1121 | + check_openvz_beans.get_vm_hostname(10, 'testdata').AndReturn('hostname.with.long.domain.name') | ||
1122 | + self.mox.ReplayAll() | ||
1123 | + | ||
1124 | + self.assertEqual(check_openvz_beans.get_hostname(0, 'testdata'), 'localhost.localdomain') | ||
1125 | + self.assertEqual(check_openvz_beans.get_hostname(99, 'nonexistent'), 'localhost.localdomain') | ||
1126 | + self.assertEqual(check_openvz_beans.get_hostname(10, 'testdata'), 'hostname.with') | ||
1127 | + | ||
1128 | + self.mox.VerifyAll() | ||
1129 | + self.mox.UnsetStubs() | ||
1130 | + | ||
1131 | + def test_create_optionsparser(self): | ||
1132 | + """Test creating a custom OptionParser.""" | ||
1133 | + parser = check_openvz_beans.create_optionsparser() | ||
1134 | + self.assertTrue(isinstance(parser, optparse.OptionParser)) | ||
1135 | + # FIXME: do we want to check more? | ||
1136 | + | ||
1137 | + | ||
1138 | +class MainEarlyFailuresTest(mox.MoxTestBase): | ||
1139 | + def setUp(self): | ||
1140 | + super(MainEarlyFailuresTest, self).setUp() | ||
1141 | + self.saved_stdout, sys.stdout = sys.stdout, StringIO.StringIO() | ||
1142 | + self.option_mock = self.mox.CreateMock(optparse.Values) | ||
1143 | + self.option_mock.use_absolute_failcnt = False | ||
1144 | + self.option_mock.storepath = 'testdata' | ||
1145 | + | ||
1146 | + def tearDown(self): | ||
1147 | + sys.stdout = self.saved_stdout | ||
1148 | + | ||
1149 | + def test___main___wrongargs(self): | ||
1150 | + """Test the __main__ function -- wrong args.""" | ||
1151 | + optionsparser_mock = self.mox.CreateMock(optparse.OptionParser) | ||
1152 | + self.mox.StubOutWithMock(check_openvz_beans, 'create_optionsparser') | ||
1153 | + check_openvz_beans.create_optionsparser().AndReturn(optionsparser_mock) | ||
1154 | + optionsparser_mock.parse_args().AndReturn((self.option_mock, ['arg',])) | ||
1155 | + optionsparser_mock.error('incorrect number of arguments').AndRaise(SystemExit) | ||
1156 | + | ||
1157 | + self.mox.ReplayAll() | ||
1158 | + | ||
1159 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1160 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1161 | + | ||
1162 | + def test___main___nodata(self): | ||
1163 | + """Test the __main__ function -- no data.""" | ||
1164 | + optionsparser_mock = self.mox.CreateMock(optparse.OptionParser) | ||
1165 | + self.mox.StubOutWithMock(check_openvz_beans, 'create_optionsparser') | ||
1166 | + check_openvz_beans.create_optionsparser().AndReturn(optionsparser_mock) | ||
1167 | + optionsparser_mock.parse_args().AndReturn((self.option_mock, [])) | ||
1168 | + | ||
1169 | + self.mox.StubOutWithMock(check_openvz_beans, 'read_data') | ||
1170 | + check_openvz_beans.read_data(mox.IsA(optparse.Values)).AndReturn('') | ||
1171 | + | ||
1172 | + self.mox.ReplayAll() | ||
1173 | + | ||
1174 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1175 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1176 | + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: No data given while reading beancounters.\n') | ||
1177 | + | ||
1178 | + def test___main___nobeancounters(self): | ||
1179 | + """Test the __main__ function -- no beancounters""" | ||
1180 | + optionsparser_mock = self.mox.CreateMock(optparse.OptionParser) | ||
1181 | + self.mox.StubOutWithMock(check_openvz_beans, 'create_optionsparser') | ||
1182 | + check_openvz_beans.create_optionsparser().AndReturn(optionsparser_mock) | ||
1183 | + optionsparser_mock.parse_args().AndReturn((self.option_mock, [])) | ||
1184 | + | ||
1185 | + self.mox.StubOutWithMock(check_openvz_beans, 'read_data') | ||
1186 | + check_openvz_beans.read_data(mox.IsA(optparse.Values)).AndReturn('somedata') | ||
1187 | + | ||
1188 | + beancounterparser_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterParser) | ||
1189 | + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterParser') | ||
1190 | + check_openvz_beans.BeanCounterParser('somedata', False, 'testdata').AndRaise(check_openvz_beans.NoBeanCountersFoundError) | ||
1191 | + | ||
1192 | + self.mox.ReplayAll() | ||
1193 | + | ||
1194 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1195 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1196 | + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: No beancounters found in data read.\n') | ||
1197 | + | ||
1198 | + | ||
1199 | +class MainRegularTest(mox.MoxTestBase): | ||
1200 | + def setUp(self): | ||
1201 | + super(MainRegularTest, self).setUp() | ||
1202 | + self.saved_stdout, sys.stdout = sys.stdout, StringIO.StringIO() | ||
1203 | + | ||
1204 | + self.optionsparser_mock = self.mox.CreateMock(optparse.OptionParser) | ||
1205 | + self.option_mock = self.mox.CreateMock(optparse.Values) | ||
1206 | + self.option_mock.confpath = 'irrelevant' | ||
1207 | + self.option_mock.domainparts = 21 | ||
1208 | + self.option_mock.use_absolute_failcnt = False | ||
1209 | + self.option_mock.storepath = 'testdata' | ||
1210 | + self.option_mock.use_maxheld = False | ||
1211 | + self.mox.StubOutWithMock(check_openvz_beans, 'create_optionsparser') | ||
1212 | + check_openvz_beans.create_optionsparser().AndReturn(self.optionsparser_mock) | ||
1213 | + self.optionsparser_mock.parse_args().AndReturn((self.option_mock, [])) | ||
1214 | + | ||
1215 | + self.mox.StubOutWithMock(check_openvz_beans, 'read_data') | ||
1216 | + check_openvz_beans.read_data(mox.IsA(optparse.Values)).AndReturn('somedata') | ||
1217 | + | ||
1218 | + self.beancounterparser_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterParser) | ||
1219 | + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterParser') | ||
1220 | + check_openvz_beans.BeanCounterParser('somedata', False, 'testdata').AndReturn(self.beancounterparser_mock) | ||
1221 | + self.beancounterparser_mock.get_beancounters().AndReturn(None) | ||
1222 | + | ||
1223 | + self.beancounterverifier_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterVerifier) | ||
1224 | + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterVerifier') | ||
1225 | + check_openvz_beans.BeanCounterVerifier(mox.IsA(optparse.Values)).AndReturn(self.beancounterverifier_mock) | ||
1226 | + | ||
1227 | + def tearDown(self): | ||
1228 | + sys.stdout = self.saved_stdout | ||
1229 | + | ||
1230 | + def test___main___noalerts(self): | ||
1231 | + """Test the __main__ function -- no alerts.""" | ||
1232 | + self.beancounterverifier_mock.verify(None, False).AndReturn([]) | ||
1233 | + | ||
1234 | + self.mox.ReplayAll() | ||
1235 | + | ||
1236 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1237 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1238 | + self.assertEqual(sys.stdout.getvalue(), 'OK: all beancounters below configured thresholds\n') | ||
1239 | + | ||
1240 | + def test___main___nospecificalert(self): | ||
1241 | + """Test the __main__ function -- no alerts.""" | ||
1242 | + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterViolation) | ||
1243 | + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) | ||
1244 | + | ||
1245 | + self.mox.ReplayAll() | ||
1246 | + | ||
1247 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1248 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1249 | + self.assertEqual(sys.stdout.getvalue(), 'OK: all beancounters below configured thresholds\n') | ||
1250 | + | ||
1251 | + def test___main___failalert(self): | ||
1252 | + """Test the __main__ function -- fail alert.""" | ||
1253 | + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterFailure) | ||
1254 | + beancounteralert_mock.uid = 123 | ||
1255 | + beancounteralert_mock.excess = 987 | ||
1256 | + beancounteralert_mock.resource = 'resource' | ||
1257 | + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) | ||
1258 | + | ||
1259 | + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') | ||
1260 | + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('dummy') | ||
1261 | + | ||
1262 | + self.mox.ReplayAll() | ||
1263 | + | ||
1264 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1265 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1266 | + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: FAIL: dummy(123)| FAIL: HOST dummy(123): resource(+987)\n') | ||
1267 | + | ||
1268 | + def test___main___overalert(self): | ||
1269 | + """Test the __main__ function -- over alert.""" | ||
1270 | + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterOvershoot) | ||
1271 | + beancounteralert_mock.uid = 123 | ||
1272 | + beancounteralert_mock.excess = 987 | ||
1273 | + beancounteralert_mock.resource = 'resource' | ||
1274 | + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) | ||
1275 | + | ||
1276 | + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') | ||
1277 | + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('dummy') | ||
1278 | + | ||
1279 | + self.mox.ReplayAll() | ||
1280 | + | ||
1281 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1282 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1283 | + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: OVER: dummy(123)| OVER: HOST dummy(123): resource(+987)\n') | ||
1284 | + | ||
1285 | + def test___main___critalert(self): | ||
1286 | + """Test the __main__ function -- crit alert.""" | ||
1287 | + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterCritical) | ||
1288 | + beancounteralert_mock.uid = 123 | ||
1289 | + beancounteralert_mock.excess = 987 | ||
1290 | + beancounteralert_mock.resource = 'resource' | ||
1291 | + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) | ||
1292 | + | ||
1293 | + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') | ||
1294 | + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('dummy') | ||
1295 | + | ||
1296 | + self.mox.ReplayAll() | ||
1297 | + | ||
1298 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1299 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1300 | + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: CRIT: dummy(123)| CRIT: HOST dummy(123): resource(+987)\n') | ||
1301 | + | ||
1302 | + def test___main___warnalert(self): | ||
1303 | + """Test the __main__ function -- warn alert.""" | ||
1304 | + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterWarning) | ||
1305 | + beancounteralert_mock.uid = 123 | ||
1306 | + beancounteralert_mock.excess = 987 | ||
1307 | + beancounteralert_mock.resource = 'resource' | ||
1308 | + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) | ||
1309 | + | ||
1310 | + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') | ||
1311 | + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('dummy') | ||
1312 | + | ||
1313 | + self.mox.ReplayAll() | ||
1314 | + | ||
1315 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1316 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1317 | + self.assertEqual(sys.stdout.getvalue(), 'WARNING: WARN: dummy(123)| WARN: HOST dummy(123): resource(+987)\n') | ||
1318 | + | ||
1319 | + def test___main___failwarnalert(self): | ||
1320 | + """Test the __main__ function -- failwarn alert.""" | ||
1321 | + beancounteralert_fail_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterFailure) | ||
1322 | + beancounteralert_fail_mock.uid = 123 | ||
1323 | + beancounteralert_fail_mock.excess = 987 | ||
1324 | + beancounteralert_fail_mock.resource = 'resource' | ||
1325 | + beancounteralert_warn_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterWarning) | ||
1326 | + beancounteralert_warn_mock.uid = 234 | ||
1327 | + beancounteralert_warn_mock.excess = 896 | ||
1328 | + beancounteralert_warn_mock.resource = 'resource' | ||
1329 | + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_fail_mock, beancounteralert_warn_mock]) | ||
1330 | + | ||
1331 | + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') | ||
1332 | + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('foo') | ||
1333 | + check_openvz_beans.get_hostname(234, 'irrelevant', 21).AndReturn('bar') | ||
1334 | + | ||
1335 | + self.mox.ReplayAll() | ||
1336 | + | ||
1337 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1338 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1339 | + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: FAIL: foo(123) WARN: bar(234)| FAIL: HOST foo(123): resource(+987) WARN: HOST bar(234): resource(+896)\n') | ||
1340 | + | ||
1341 | + def test___main___multifailwarnalert(self): | ||
1342 | + """Test the __main__ function -- failwarn alert.""" | ||
1343 | + beancounteralert_fail1_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterFailure) | ||
1344 | + beancounteralert_fail1_mock.uid = 123 | ||
1345 | + beancounteralert_fail1_mock.excess = 987 | ||
1346 | + beancounteralert_fail1_mock.resource = 'resource1' | ||
1347 | + beancounteralert_fail2_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterFailure) | ||
1348 | + beancounteralert_fail2_mock.uid = 123 | ||
1349 | + beancounteralert_fail2_mock.excess = 987 | ||
1350 | + beancounteralert_fail2_mock.resource = 'resource2' | ||
1351 | + beancounteralert_warn1_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterWarning) | ||
1352 | + beancounteralert_warn1_mock.uid = 234 | ||
1353 | + beancounteralert_warn1_mock.excess = 896 | ||
1354 | + beancounteralert_warn1_mock.resource = 'resource' | ||
1355 | + beancounteralert_warn2_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterWarning) | ||
1356 | + beancounteralert_warn2_mock.uid = 345 | ||
1357 | + beancounteralert_warn2_mock.excess = 896 | ||
1358 | + beancounteralert_warn2_mock.resource = 'resource' | ||
1359 | + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_fail1_mock, beancounteralert_fail2_mock, beancounteralert_warn1_mock, beancounteralert_warn2_mock]) | ||
1360 | + | ||
1361 | + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') | ||
1362 | + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('foo') | ||
1363 | + check_openvz_beans.get_hostname(345, 'irrelevant', 21).AndReturn('baz') | ||
1364 | + check_openvz_beans.get_hostname(234, 'irrelevant', 21).AndReturn('bar') | ||
1365 | + | ||
1366 | + self.mox.ReplayAll() | ||
1367 | + | ||
1368 | + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ | ||
1369 | + self.assertRaises(SystemExit, check_openvz_beans.__main__) | ||
1370 | + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: FAIL: foo(123) WARN: baz(345) bar(234)| FAIL: HOST foo(123): resource1(+987) resource2(+987) WARN: HOST baz(345): resource(+896) HOST bar(234): resource(+896)\n') | ||
1371 | + | ||
1372 | +if __name__ == '__main__': | ||
1373 | + unittest.main() | ||
1374 | diff -urN nothing/debian/changelog nagios-plugins-openvz-beans/debian/changelog | ||
1375 | --- nothing/debian/changelog 1970-01-01 01:00:00.000000000 +0100 | ||
1376 | +++ nagios-plugins-openvz-beans/debian/changelog 2011-11-01 18:30:10.247574999 +0100 | ||
1377 | @@ -0,0 +1,5 @@ | ||
1378 | +nagios-plugins-openvz-beans (0.5-0) stable; urgency=low | ||
1379 | + | ||
1380 | + * initial release packaging | ||
1381 | + | ||
1382 | + -- Andreas Kotes <andreas.kotes@nokia.com> Thu, 01 Nov 2011 18:30:23 +0100 | ||
1383 | diff -urN nothing/debian/compat nagios-plugins-openvz-beans/debian/compat | ||
1384 | --- nothing/debian/compat 1970-01-01 01:00:00.000000000 +0100 | ||
1385 | +++ nagios-plugins-openvz-beans/debian/compat 2011-10-17 14:23:00.655695188 +0200 | ||
1386 | @@ -0,0 +1 @@ | ||
1387 | +5 | ||
1388 | diff -urN nothing/debian/control nagios-plugins-openvz-beans/debian/control | ||
1389 | --- nothing/debian/control 1970-01-01 01:00:00.000000000 +0100 | ||
1390 | +++ nagios-plugins-openvz-beans/debian/control 2011-11-01 16:54:48.381618568 +0100 | ||
1391 | @@ -0,0 +1,9 @@ | ||
1392 | +Source: nagios-plugins-openvz-beans | ||
1393 | +Section: net | ||
1394 | +Priority: extra | ||
1395 | +Maintainer: Andreas Kotes <andreas.kotes@nokia.com> | ||
1396 | + | ||
1397 | +Package: nagios-plugins-openvz-beans | ||
1398 | +Architecture: all | ||
1399 | +Depends: python-minimal | ||
1400 | +Description: Nagios Plugin to check OpenVZ Bean Counters | ||
1401 | diff -urN nothing/debian/.gitignore nagios-plugins-openvz-beans/debian/.gitignore | ||
1402 | --- nothing/debian/.gitignore 1970-01-01 01:00:00.000000000 +0100 | ||
1403 | +++ nagios-plugins-openvz-beans/debian/.gitignore 2011-10-17 14:23:00.655695188 +0200 | ||
1404 | @@ -0,0 +1,4 @@ | ||
1405 | +files | ||
1406 | +nagios-plugins-openvz-beans.debhelper.log | ||
1407 | +nagios-plugins-openvz-beans.substvars | ||
1408 | +nagios-plugins-openvz-beans/ | ||
1409 | diff -urN nothing/debian/rules nagios-plugins-openvz-beans/debian/rules | ||
1410 | --- nothing/debian/rules 1970-01-01 01:00:00.000000000 +0100 | ||
1411 | +++ nagios-plugins-openvz-beans/debian/rules 2011-11-01 18:28:09.727238903 +0100 | ||
1412 | @@ -0,0 +1,33 @@ | ||
1413 | +#!/usr/bin/make -f | ||
1414 | +DEBIANDIR=$(CURDIR)/debian | ||
1415 | + | ||
1416 | +%: | ||
1417 | + dh $@ | ||
1418 | + | ||
1419 | +install: build | ||
1420 | + dh_testdir | ||
1421 | + dh_testroot | ||
1422 | + dh_clean -k | ||
1423 | + dh_installdirs | ||
1424 | + | ||
1425 | + install -d -m0755 $(DEBIANDIR)/nagios-plugins-openvz-beans/usr/lib/nagios/plugins | ||
1426 | + install -m0755 check_openvz_beans.py $(DEBIANDIR)/nagios-plugins-openvz-beans/usr/lib/nagios/plugins | ||
1427 | + install -d -m0755 $(DEBIANDIR)/nagios-plugins-openvz-beans/usr/share/doc/nagios-plugins-openvz-beans | ||
1428 | + install -m0644 README $(DEBIANDIR)/nagios-plugins-openvz-beans/usr/share/doc/nagios-plugins-openvz-beans | ||
1429 | + | ||
1430 | +# Build architecture-independent files here. | ||
1431 | +binary-indep: build install | ||
1432 | + dh_testdir -i | ||
1433 | + dh_testroot -i | ||
1434 | + dh_installchangelogs -i | ||
1435 | + dh_installdocs -i | ||
1436 | + dh_link -i | ||
1437 | + dh_compress -i | ||
1438 | + dh_fixperms -i | ||
1439 | + dh_installdeb -i | ||
1440 | + dh_gencontrol -i | ||
1441 | + dh_md5sums -i | ||
1442 | + dh_builddeb -i | ||
1443 | + | ||
1444 | +binary: binary-indep | ||
1445 | +.PHONY: build clean binary-indep binary install | ||
1446 | diff -urN nothing/.gitignore nagios-plugins-openvz-beans/.gitignore | ||
1447 | --- nothing/.gitignore 1970-01-01 01:00:00.000000000 +0100 | ||
1448 | +++ nagios-plugins-openvz-beans/.gitignore 2011-10-17 14:23:00.695695306 +0200 | ||
1449 | @@ -0,0 +1 @@ | ||
1450 | +*.pyc | ||
1451 | diff -urN nothing/nagios-plugins-openvz-beans.spec nagios-plugins-openvz-beans/nagios-plugins-openvz-beans.spec | ||
1452 | --- nothing/nagios-plugins-openvz-beans.spec 1970-01-01 01:00:00.000000000 +0100 | ||
1453 | +++ nagios-plugins-openvz-beans/nagios-plugins-openvz-beans.spec 2011-11-01 16:54:35.621582988 +0100 | ||
1454 | @@ -0,0 +1,34 @@ | ||
1455 | +Name: nagios-plugins-openvz-beans | ||
1456 | +Version: 0.5 | ||
1457 | +Release: 0 | ||
1458 | +Summary: Nagios Plugin to check OpenVZ Bean Counters | ||
1459 | +License: GPL | ||
1460 | +Group: Applications/System | ||
1461 | +Source: check_openvz_beans.py | ||
1462 | + | ||
1463 | +Autoreq: 0 | ||
1464 | +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root | ||
1465 | +BuildArch: noarch | ||
1466 | +Requires: python | ||
1467 | + | ||
1468 | +%description | ||
1469 | +Nagios Plugin to check OpenVZ Bean Counters | ||
1470 | + | ||
1471 | +%prep | ||
1472 | +rm -rf %{buildroot} | ||
1473 | + | ||
1474 | +%install | ||
1475 | +install -d -m0755 %{buildroot}%{_libdir}/nagios/plugins/contrib/ | ||
1476 | +install -m0755 %{_sourcefile} %{buildroot}%{_libdir}/nagios/plugins/contrib/ | ||
1477 | + | ||
1478 | + | ||
1479 | +%clean | ||
1480 | +rm -rf %{buildroot} | ||
1481 | + | ||
1482 | +%files | ||
1483 | +%dir %{_libdir}/nagios/plugins/contrib | ||
1484 | +%attr(755, root, root) %{_libdir}/nagios/plugins/contrib/check_openvz_beans.py | ||
1485 | + | ||
1486 | +%changelog | ||
1487 | +* Thu Oct 13 2011 Andreas Kotes <andreas.kotes@nokia.com> 0.5 | ||
1488 | + - initial release packaging | ||
1489 | diff -urN nothing/README nagios-plugins-openvz-beans/README | ||
1490 | --- nothing/README 1970-01-01 01:00:00.000000000 +0100 | ||
1491 | +++ nagios-plugins-openvz-beans/README 2011-11-01 18:04:40.633309403 +0100 | ||
1492 | @@ -0,0 +1,75 @@ | ||
1493 | +Nagios check for OpenVZ bean counters | ||
1494 | +===================================== | ||
1495 | + | ||
1496 | +General | ||
1497 | +------- | ||
1498 | + | ||
1499 | +This Nagios plugin allows to define and check for thresholds against bean | ||
1500 | +counters defined in /proc/user_beancounters, which are available in both OpenVZ | ||
1501 | +hypervisors and VMs. | ||
1502 | + | ||
1503 | +It's possible to set global thresholds via '-c' (for critical) and '-w' (for | ||
1504 | +warning) which apply to all resources checked. | ||
1505 | + | ||
1506 | +Tresholds for individual resources can be set by using '-C'/'-W' and giving | ||
1507 | +comma-separated key:value pairs. | ||
1508 | + | ||
1509 | +The script was written to be compatible with Python 2.4 and later. | ||
1510 | + | ||
1511 | +Reading /proc/user_beancounters | ||
1512 | +------------------------------- | ||
1513 | + | ||
1514 | +Since /proc/user_beancounters is only readable by the root user - and running | ||
1515 | +Nagios plugins as the root user is highly discouraged - you can either copy | ||
1516 | +this file and check against the copy using the -f option. Or - if you have set | ||
1517 | +up your /etc/sudoers file to allow it - you can use the -s option, which lets | ||
1518 | +check_openvz_beans use 'sudo cat /proc/user_beancounters' to read the file. | ||
1519 | + | ||
1520 | +Hostname handling | ||
1521 | +----------------- | ||
1522 | +The OpenVZ context IDs are resolved by grepping for the HOSTNAME variable in | ||
1523 | +the uid's config file found in the path given by '-p'. | ||
1524 | + | ||
1525 | +Hostnames (e.g. this.is.a.very.long.hostname) are shortened to their first two | ||
1526 | +components per default (e.g. this.is for the former one), but this length can | ||
1527 | +be configured using -d (e.g. '-d 1' for 'this', '-d 3' for 'this.is.a'), | ||
1528 | +allowing you to identify the affected VM(s) in the Nagios alert message | ||
1529 | +already. | ||
1530 | + | ||
1531 | +Output | ||
1532 | +------ | ||
1533 | + | ||
1534 | +The output follows the Nagios Plugin API 3.0, i.e. the script gives the proper | ||
1535 | +exit codes (0 = OK, 1 = Warning, 2 = Critical, 3 = Unknown), a simple single | ||
1536 | +line of text, and additional perf-data. | ||
1537 | + | ||
1538 | +The single line of text informs about WARNING or CRITICAL state, and what's a | ||
1539 | +problem: | ||
1540 | + | ||
1541 | +FAIL - a resource against its limit and the was blocked by kernel | ||
1542 | +OVER - a resource was over its relevant threshold and will cause problems | ||
1543 | +CRIT - a resource ran close to its threshold by given (critical) percentage | ||
1544 | +WARN - a resource ran close to its threshold by given (warning) percentage | ||
1545 | + | ||
1546 | +Data used | ||
1547 | +--------- | ||
1548 | + | ||
1549 | +The value checked for each resource is the 'held' value, but you could use '-m' | ||
1550 | +to use the 'maxheld' value instead. You'll not be able to reset that value, | ||
1551 | +though, so any Nagios warning/failure status will stay until the next | ||
1552 | +hypervisor reboot. | ||
1553 | + | ||
1554 | +Failure counters can't be reset either, but the plugin reads the data stored by | ||
1555 | +vzubc's relative mode, i.e. if you inspect the problem with 'vzubc -r', the | ||
1556 | +critical status will be resolved. This behaviour can be turned off with '-a'. | ||
1557 | + | ||
1558 | +If you are storing data somewhere else than the default '/tmp/vzubc.store', you | ||
1559 | +can change that via '-u'. | ||
1560 | + | ||
1561 | +vzubc is provided by the vzctl package which isn't used by this code directly, | ||
1562 | +nor is there a dependency - you'll have to install it yourself if you want to | ||
1563 | +use this functionality. | ||
1564 | + | ||
1565 | +Each group lists the hostname & uid of the VM in the regular message, | ||
1566 | +additionally the resource name and how much its threshold was exeeded are given | ||
1567 | +in the perfdata line. | ||
1568 | diff -urN nothing/README.source nagios-plugins-openvz-beans/README.source | ||
1569 | --- nothing/README.source 1970-01-01 01:00:00.000000000 +0100 | ||
1570 | +++ nagios-plugins-openvz-beans/README.source 2011-11-01 16:29:20.667358270 +0100 | ||
1571 | @@ -0,0 +1,16 @@ | ||
1572 | +The implementation can be tested by calling its test scripts | ||
1573 | +./check_openvz_beans-test.py, which runs unit tests for almost all of its code, | ||
1574 | +and requires Python unittest >= 2.4 as well as pymox. | ||
1575 | + | ||
1576 | +Current code coverage of the unit tests is >95%. | ||
1577 | + | ||
1578 | +RPMs can be build using the following commandline: | ||
1579 | + | ||
1580 | +rpmbuild -bb -D "_sourcefile `pwd`/check_openvz_beans.py" nagios-plugins-openvz-beans.spec | ||
1581 | + | ||
1582 | +Debian / Ubuntu packages can be build by simply calling: | ||
1583 | + | ||
1584 | +dpkg-buildpackage | ||
1585 | + | ||
1586 | +... in both cases the proper developer tools for the corresponding distribution | ||
1587 | +have to be installed. | ||
1588 | diff -urN nothing/testdata/10.conf nagios-plugins-openvz-beans/testdata/10.conf | ||
1589 | --- nothing/testdata/10.conf 1970-01-01 01:00:00.000000000 +0100 | ||
1590 | +++ nagios-plugins-openvz-beans/testdata/10.conf 2011-11-01 19:09:28.124150356 +0100 | ||
1591 | @@ -0,0 +1 @@ | ||
1592 | +HOSTNAME="vmokay.pr.foo.test.your.do.main" | ||
1593 | diff -urN nothing/testdata/60.conf nagios-plugins-openvz-beans/testdata/60.conf | ||
1594 | --- nothing/testdata/60.conf 1970-01-01 01:00:00.000000000 +0100 | ||
1595 | +++ nagios-plugins-openvz-beans/testdata/60.conf 2011-11-01 19:09:32.834163485 +0100 | ||
1596 | @@ -0,0 +1 @@ | ||
1597 | +HOSTNAME="vmover1.st.foo.test.your.do.main" | ||
1598 | diff -urN nothing/testdata/hostcrit.bcs nagios-plugins-openvz-beans/testdata/hostcrit.bcs | ||
1599 | --- nothing/testdata/hostcrit.bcs 1970-01-01 01:00:00.000000000 +0100 | ||
1600 | +++ nagios-plugins-openvz-beans/testdata/hostcrit.bcs 2011-11-01 19:07:53.643886880 +0100 | ||
1601 | @@ -0,0 +1,170 @@ | ||
1602 | +Version: 2.5 | ||
1603 | + uid resource held maxheld barrier limit failcnt | ||
1604 | + 40: kmemsize 12345678 33456789 34567890 40000000 0 | ||
1605 | + lockedpages 0 5859 32768 65536 0 | ||
1606 | + privvmpages 1234567 2345678 3276800 9223372036854775807 0 | ||
1607 | + shmpages 688 1344 9223372036854775807 9223372036854775807 0 | ||
1608 | + dummy 0 0 0 0 0 | ||
1609 | + numproc 75 88 8000 8000 0 | ||
1610 | + physpages 199301 239798 0 9223372036854775807 0 | ||
1611 | + vmguarpages 0 0 4194304 9223372036854775807 0 | ||
1612 | + oomguarpages 199301 239798 4186112 9223372036854775807 0 | ||
1613 | + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 | ||
1614 | + numflock 4 14 200 220 0 | ||
1615 | + numpty 1 2 64 64 0 | ||
1616 | + numsiginfo 0 9 512 512 0 | ||
1617 | + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 | ||
1618 | + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 | ||
1619 | + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 | ||
1620 | + dgramrcvbuf 0 58680 262144 262144 0 | ||
1621 | + numothersock 115 125 360 360 0 | ||
1622 | + dcachesize 0 0 9223372036854775807 9223372036854775807 0 | ||
1623 | + numfile 1655 1958 9312 9312 0 | ||
1624 | + dummy 0 0 0 0 0 | ||
1625 | + dummy 0 0 0 0 0 | ||
1626 | + dummy 0 0 0 0 0 | ||
1627 | + numiptent 20 20 128 128 0 | ||
1628 | + 20: kmemsize 12345678 23456789 34567890 40000000 0 | ||
1629 | + lockedpages 0 5859 32768 65536 0 | ||
1630 | + privvmpages 1234567 2345678 3276800 9223372036854775807 3 | ||
1631 | + shmpages 688 1344 9223372036854775807 9223372036854775807 0 | ||
1632 | + dummy 0 0 0 0 0 | ||
1633 | + numproc 75 88 8000 8000 0 | ||
1634 | + physpages 199301 239798 0 9223372036854775807 0 | ||
1635 | + vmguarpages 0 0 4194304 9223372036854775807 0 | ||
1636 | + oomguarpages 199301 239798 4186112 9223372036854775807 0 | ||
1637 | + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 | ||
1638 | + numflock 4 14 200 220 0 | ||
1639 | + numpty 1 2 64 64 0 | ||
1640 | + numsiginfo 0 9 512 512 0 | ||
1641 | + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 | ||
1642 | + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 | ||
1643 | + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 | ||
1644 | + dgramrcvbuf 0 58680 262144 262144 0 | ||
1645 | + numothersock 115 125 360 360 0 | ||
1646 | + dcachesize 0 0 9223372036854775807 9223372036854775807 0 | ||
1647 | + numfile 1655 1958 9312 9312 0 | ||
1648 | + dummy 0 0 0 0 0 | ||
1649 | + dummy 0 0 0 0 0 | ||
1650 | + dummy 0 0 0 0 0 | ||
1651 | + numiptent 20 20 128 128 0 | ||
1652 | + 30: kmemsize 12345678 23456789 34567890 40000000 0 | ||
1653 | + lockedpages 0 5859 32768 65536 0 | ||
1654 | + privvmpages 1234567 2345678 3276800 9223372036854775807 3 | ||
1655 | + shmpages 688 1344 9223372036854775807 9223372036854775807 0 | ||
1656 | + dummy 0 0 0 0 0 | ||
1657 | + numproc 75 88 100 120 2 | ||
1658 | + physpages 199301 239798 0 9223372036854775807 0 | ||
1659 | + vmguarpages 0 0 4194304 9223372036854775807 0 | ||
1660 | + oomguarpages 199301 239798 4186112 9223372036854775807 0 | ||
1661 | + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 | ||
1662 | + numflock 4 14 200 220 0 | ||
1663 | + numpty 1 2 64 64 0 | ||
1664 | + numsiginfo 0 9 512 512 0 | ||
1665 | + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 | ||
1666 | + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 | ||
1667 | + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 | ||
1668 | + dgramrcvbuf 0 58680 262144 262144 0 | ||
1669 | + numothersock 115 125 360 360 0 | ||
1670 | + dcachesize 0 0 9223372036854775807 9223372036854775807 0 | ||
1671 | + numfile 1655 1958 9312 9312 0 | ||
1672 | + dummy 0 0 0 0 0 | ||
1673 | + dummy 0 0 0 0 0 | ||
1674 | + dummy 0 0 0 0 0 | ||
1675 | + numiptent 20 20 128 128 0 | ||
1676 | + 10: kmemsize 12345678 30456789 34567890 40000000 0 | ||
1677 | + lockedpages 0 5859 32768 65536 0 | ||
1678 | + privvmpages 1234567 2345678 3276800 9223372036854775807 0 | ||
1679 | + shmpages 688 1344 9223372036854775807 9223372036854775807 0 | ||
1680 | + dummy 0 0 0 0 0 | ||
1681 | + numproc 75 88 8000 8000 0 | ||
1682 | + physpages 199301 239798 0 9223372036854775807 0 | ||
1683 | + vmguarpages 0 0 4194304 9223372036854775807 0 | ||
1684 | + oomguarpages 199301 239798 4186112 9223372036854775807 0 | ||
1685 | + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 | ||
1686 | + numflock 4 14 200 220 0 | ||
1687 | + numpty 1 2 64 64 0 | ||
1688 | + numsiginfo 0 9 512 512 0 | ||
1689 | + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 | ||
1690 | + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 | ||
1691 | + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 | ||
1692 | + dgramrcvbuf 0 58680 262144 262144 0 | ||
1693 | + numothersock 115 125 360 360 0 | ||
1694 | + dcachesize 0 0 9223372036854775807 9223372036854775807 0 | ||
1695 | + numfile 1655 1958 9312 9312 0 | ||
1696 | + dummy 0 0 0 0 0 | ||
1697 | + dummy 0 0 0 0 0 | ||
1698 | + dummy 0 0 0 0 0 | ||
1699 | + numiptent 20 20 128 128 0 | ||
1700 | + 50: kmemsize 12345678 31456789 34567890 40000000 0 | ||
1701 | + lockedpages 0 5859 32768 65536 0 | ||
1702 | + privvmpages 1234567 2345678 3276800 9223372036854775807 0 | ||
1703 | + shmpages 688 1344 9223372036854775807 9223372036854775807 0 | ||
1704 | + dummy 0 0 0 0 0 | ||
1705 | + numproc 75 88 8000 8000 0 | ||
1706 | + physpages 199301 239798 0 9223372036854775807 0 | ||
1707 | + vmguarpages 0 0 4194304 9223372036854775807 0 | ||
1708 | + oomguarpages 199301 239798 4186112 9223372036854775807 0 | ||
1709 | + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 | ||
1710 | + numflock 4 14 200 220 0 | ||
1711 | + numpty 1 2 64 64 0 | ||
1712 | + numsiginfo 0 9 512 512 0 | ||
1713 | + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 | ||
1714 | + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 | ||
1715 | + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 | ||
1716 | + dgramrcvbuf 0 58680 262144 262144 0 | ||
1717 | + numothersock 115 125 360 360 0 | ||
1718 | + dcachesize 0 0 9223372036854775807 9223372036854775807 0 | ||
1719 | + numfile 1655 1958 9312 9312 0 | ||
1720 | + dummy 0 0 0 0 0 | ||
1721 | + dummy 0 0 0 0 0 | ||
1722 | + dummy 0 0 0 0 0 | ||
1723 | + numiptent 20 20 128 128 0 | ||
1724 | + 60: kmemsize 12345678 40000001 34567890 40000000 0 | ||
1725 | + lockedpages 0 5859 32768 65536 0 | ||
1726 | + privvmpages 1234567 2345678 3276800 9223372036854775807 0 | ||
1727 | + shmpages 688 1344 9223372036854775807 9223372036854775807 0 | ||
1728 | + dummy 0 0 0 0 0 | ||
1729 | + numproc 75 88 8000 8000 0 | ||
1730 | + physpages 199301 239798 0 9223372036854775807 0 | ||
1731 | + vmguarpages 0 0 4194304 9223372036854775807 0 | ||
1732 | + oomguarpages 199301 239798 4186112 9223372036854775807 0 | ||
1733 | + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 | ||
1734 | + numflock 4 14 200 220 0 | ||
1735 | + numpty 1 2 64 64 0 | ||
1736 | + numsiginfo 0 9 512 512 0 | ||
1737 | + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 | ||
1738 | + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 | ||
1739 | + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 | ||
1740 | + dgramrcvbuf 0 58680 262144 262144 0 | ||
1741 | + numothersock 115 125 360 360 0 | ||
1742 | + dcachesize 0 0 9223372036854775807 9223372036854775807 0 | ||
1743 | + numfile 1655 1958 9312 9312 0 | ||
1744 | + dummy 0 0 0 0 0 | ||
1745 | + dummy 0 0 0 0 0 | ||
1746 | + dummy 0 0 0 0 0 | ||
1747 | + numiptent 20 20 128 128 0 | ||
1748 | + 0: kmemsize 15408521 33415719 9223372036854775807 9223372036854775807 0 | ||
1749 | + lockedpages 8789 12353 9223372036854775807 9223372036854775807 0 | ||
1750 | + privvmpages 41072 104930 9223372036854775807 9223372036854775807 0 | ||
1751 | + shmpages 730 1706 9223372036854775807 9223372036854775807 0 | ||
1752 | + dummy 0 0 9223372036854775807 9223372036854775807 0 | ||
1753 | + numproc 291 472 9223372036854775807 9223372036854775807 0 | ||
1754 | + physpages 9432 54303 9223372036854775807 9223372036854775807 0 | ||
1755 | + vmguarpages 0 0 9223372036854775807 9223372036854775807 0 | ||
1756 | + oomguarpages 9432 54303 9223372036854775807 9223372036854775807 0 | ||
1757 | + numtcpsock 13 21 9223372036854775807 9223372036854775807 0 | ||
1758 | + numflock 5 22 9223372036854775807 9223372036854775807 0 | ||
1759 | + numpty 1 1 9223372036854775807 9223372036854775807 0 | ||
1760 | + numsiginfo 0 6 9223372036854775807 9223372036854775807 0 | ||
1761 | + tcpsndbuf 773048 924800 9223372036854775807 9223372036854775807 0 | ||
1762 | + tcprcvbuf 212992 854408 9223372036854775807 9223372036854775807 0 | ||
1763 | + othersockbuf 204864 283784 9223372036854775807 9223372036854775807 0 | ||
1764 | + dgramrcvbuf 0 16944 9223372036854775807 9223372036854775807 0 | ||
1765 | + numothersock 178 225 9223372036854775807 9223372036854775807 0 | ||
1766 | + dcachesize 0 0 9223372036854775807 9223372036854775807 0 | ||
1767 | + numfile 5141 6779 9223372036854775807 9223372036854775807 0 | ||
1768 | + dummy 0 0 9223372036854775807 9223372036854775807 0 | ||
1769 | + dummy 0 0 9223372036854775807 9223372036854775807 0 | ||
1770 | + dummy 0 0 9223372036854775807 9223372036854775807 0 | ||
1771 | + numiptent 51 51 9223372036854775807 9223372036854775807 0 | ||