summaryrefslogtreecommitdiffstats
path: root/web/attachments/427640-nagios-plugins-openvz-beans.diff
diff options
context:
space:
mode:
Diffstat (limited to 'web/attachments/427640-nagios-plugins-openvz-beans.diff')
-rw-r--r--web/attachments/427640-nagios-plugins-openvz-beans.diff1771
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 @@
1diff -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__()
594Binary files nothing/check_openvz_beans.pyc and nagios-plugins-openvz-beans/check_openvz_beans.pyc differ
595diff -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()
1374diff -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
1383diff -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
1388diff -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
1401diff -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/
1409diff -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
1446diff -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
1451diff -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
1489diff -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.
1568diff -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.
1588diff -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"
1593diff -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"
1598diff -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