diff -urN nothing/check_openvz_beans.py nagios-plugins-openvz-beans/check_openvz_beans.py --- nothing/check_openvz_beans.py 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/check_openvz_beans.py 2011-11-01 19:04:11.133266362 +0100 @@ -0,0 +1,589 @@ +#!/usr/bin/python +# +# Nagios check for OpenVZ bean counters +# +# usage: +# check_openvz_beans.py [options] +# +# see "check_openvz_beans.py -h" for detailed options +# +# Licensed under GPLv2 (or later) +# +# Copyright (C) 2011 NOKIA +# Written by Andreas Kotes +# +# Reviewed-by: Pascal Hahn +# Signed-Off-by: Pascal Hahn +# Signed-Off-by: Jessvin Thomas +# + +import os +import re +import errno +import sys +import socket + +# using 'optparse' for Python 2.4 compatibility +import optparse +# using subprocess for clean sudo handling +import subprocess + +__author__ = ('Andreas Kotes ') + +OPENVZ_CONFPATH = '/etc/vz/conf' +OPENVZ_UBCSTORE = '/tmp/vzubc.store' + +class Error(Exception): + """Module level error.""" + pass + + +class NoBeanCountersFoundError(Error): + "No BeanCounters could be found in output.""" + pass + + +class BeanCounter(object): + """Object storing OpenVZ beancounter information""" + FIND_BEAN_COUNTER_RE = r'''(?xms) # verbose, multiline, special + ^ # begin entry + \s*(?P\S+) # resource name + \s*(?P\d+) # held count + \s*(?P\d+) # maximum held + \s*(?P\d+) # barrier + \s*(?P\d+) # limit + \s*(?P\d+) # failure counter + ''' + def __init__(self, uid, resource_txt, use_absolute_failcnt=False, storepath=OPENVZ_UBCSTORE): + """Create object storing OpenVZ beancounter information + + Args + uid: int - OpenVZ context identifier + resource_txt: str - OpenVZ resource + use_absolute_failcnt: + bool - Whether to use delta failcnts or not + storepath: str - Path to OpenVZ's vzubc's data storage + """ + self.uid = uid + + # extract data from text line + resourcedata = re.match(BeanCounter.FIND_BEAN_COUNTER_RE, resource_txt).groupdict() + self.resource = resourcedata['resource'] + self.held = int(resourcedata['held']) + self.maxheld = int(resourcedata['maxheld']) + self.barrier = int(resourcedata['barrier']) + self.limit = int(resourcedata['limit']) + self.failcnt = int(resourcedata['failcnt']) + + if not use_absolute_failcnt: + prev_failcnt = self.read_vzubc_failcnt(storepath) + # don't update from stale / lower value + if (prev_failcnt <= self.failcnt): + self.failcnt -= prev_failcnt + + def read_vzubc_failcnt(self, storepath=OPENVZ_UBCSTORE): + """Read data stored by vzubc -r. + + Args + uid: int - OpenVZ context identifier + storepath: str - Path to OpenVZ's vzubc's data storage + + Returns: + int - stored value or 0 + """ + filename = os.path.join(storepath, "ubc.%d.%s.failcnt" % (self.uid, self.resource)) + if os.path.exists(filename): + return int(open(filename, "r").read()) + else: + return 0 + + def __repr__(self): + return ('' % + (self.uid, self.resource, self.held, self.maxheld, self.barrier, self.limit, self.failcnt)) + + def __eq__(self, other): + if (self.uid == other.uid and + self.resource == other.resource and + self.held == other.held and + self.maxheld == other.maxheld and + self.barrier == other.barrier and + self.limit == other.limit and + self.failcnt == other.failcnt): + return True + else: + return False + + +class BeanCounterParser(object): + """Parse bean counter information.""" + FIND_BEAN_COUNTER_GROUP_RE = r'''(?xms) # verbose, multline, special + ^ # begin entry + \s*(?P\d+): # group / context number + (?P[^:]*)$ # everything up to next group/EOF + ''' + + def parse_beancounters(self, beancounters_data, use_absolute_failcnt=False, storepath=OPENVZ_UBCSTORE): + """Populate BeanCounter objects with data. + + Args: + beancounters_data: str - containing the unparsed + use_absolute_failcnt: + bool - Whether to use delta failcnts or not + storepath: str - Path to OpenVZ's vzubc's data storage + + Raises: + NoBeanCountersFoundError - in case no counters where found + """ + # find all beans blocks + result = re.findall(BeanCounterParser.FIND_BEAN_COUNTER_GROUP_RE, beancounters_data) + if not result: + raise NoBeanCountersFoundError + self.beancounters = [] + for uid, resource_txt in result: + for line in resource_txt.split("\n"): + if line: + self.beancounters.append(BeanCounter(int(uid), line, use_absolute_failcnt, storepath)) + return self.beancounters + + def __init__(self, beancounters_data, use_absolute_failcnt=False, storepath=OPENVZ_UBCSTORE): + """Create a new BeanCounter object + + Args: + beancounters_data: str - containing the unparsed + contents of /proc/user_beancounters + use_absolute_failcnt: + bool - Whether to use delta failcnts or not + storepath: str - Path to OpenVZ's vzubc's data storage + """ + self.parse_beancounters(beancounters_data, use_absolute_failcnt, storepath) + + def get_beancounters(self): + return self.beancounters + + +class BeanCounterThreshold(object): + """Holds threshold values for BeanCounters""" + THRESHOLD_KEYS = { + # relevant threshold limiting in OpenVZ + # see http://wiki.openvz.org/UBC_parameter_properties + # NOTE: custom/missing beancounters are treated limited by limit + 'limit' : ['lockedpages', 'numfile' , 'numiptent', 'numothersock', 'numproc', 'numpty', 'numsiginfo', 'numtcpsock', 'shmpages'], + 'barrier': ['dcachesize', 'dgramrcvbuf', 'kmemsize', 'numflock', 'oomguarpages', 'othersockbuf', 'privvmpages', 'tcprcvbuf', 'tcpsndbuf', 'vmguarpages'], + 'none' : ['physpages'] + } + + def __init__(self, resource=None, critical=95, warning=90): + """Create a new verifier. + + Args: + resource: str - resource name (None = wildcard match all resources) + critical: int - critical level (percentage of limit) + warning: int - critical level (percentage of limit) + """ + self.resource = resource + self.critical = critical + self.warning = warning + + def __repr__(self): + if not self.resource: + resource = '*' + else: + resource = self.resource + return ('' % + (resource, self.critical, self.warning)) + + def check(self, beancounter, use_maxheld=False): + """Check a threshold + + Args: + beancounter: BeanCounter object + use_maxheld: bool - use maxheld instead of held counter for limits + + Returns: + None or BeanCounterViolation (or subclass) + """ + if not self.resource or beancounter.resource == self.resource: + if beancounter.failcnt: + return BeanCounterFailure(beancounter.resource, beancounter.uid, beancounter.failcnt) + + # what are we measuring against? + if beancounter.resource in BeanCounterThreshold.THRESHOLD_KEYS['barrier']: + quota = beancounter.barrier + else: + quota = beancounter.limit + + # what are we measuring? + if use_maxheld: + value = beancounter.maxheld + else: + value = beancounter.held + + if value > quota: + return BeanCounterOvershoot(beancounter.resource, beancounter.uid, value - quota) + + # check for critical / warning by comparing our value with the relevant + # percentage of our quota. return an object with information how much + # over quota we are. + if quota and self.critical >= 0 and value >= (quota * (float(self.critical)/100)): + return BeanCounterCritical(beancounter.resource, beancounter.uid, value - int((quota * (float(self.critical)/100)))) + if quota and self.warning >= 0 and value >= (quota * (float(self.warning)/100)): + return BeanCounterWarning(beancounter.resource, beancounter.uid, value - int((quota * (float(self.warning)/100)))) + return None + + +class BeanCounterViolation(object): + """Alert containing a BeanCounter violation.""" + def __init__(self, resource, uid, excess): + """Create a new BeanCounter violation. + + Args: + resource: str - resource name + uid: int - context uid + excess: int - how much the limit was exceeded + """ + self.tag = 'VIOL' + self.resource = resource + self.uid = uid + self.excess = excess + + def __repr__(self): + return ('<%s %s: resource %s in context %d by %d>' % + (self.__class__.__name__, self.tag, self.resource, self.uid, self.excess)) + + +class BeanCounterFailure(BeanCounterViolation): + """Alert containing a BeanCounter failure.""" + def __init__(self, resource, uid, excess): + """Create a new BeanCounter failure. + + Args: + resource: str - resource name + uid: int - context uid + excess: int - how much the limit was exceeded + """ + super(BeanCounterFailure, self).__init__(resource, uid, excess) + self.tag = 'FAIL' + + +class BeanCounterOvershoot(BeanCounterViolation): + """Alert containing a BeanCounter overshoot.""" + def __init__(self, resource, uid, excess): + """Create a new BeanCounter overshoot. + + Args: + resource: str - resource name + uid: int - context uid + excess: int - how much the limit was exceeded + """ + super(BeanCounterOvershoot, self).__init__(resource, uid, excess) + self.tag = 'OVER' + + +class BeanCounterCritical(BeanCounterViolation): + """Alert containing a BeanCounter critical.""" + def __init__(self, resource, uid, excess): + """Create a new BeanCounter critical alert + + Args: + resource: str - resource name + uid: int - context uid + excess: int - how much the limit was exceeded + """ + super(BeanCounterCritical, self).__init__(resource, uid, excess) + self.tag = 'CRIT' + + +class BeanCounterWarning(BeanCounterViolation): + """Alert containing a BeanCounter warning.""" + def __init__(self, resource, uid, excess): + """Create a new BeanCounter warning alert + + Args: + resource: str - resource name + uid: int - context uid + excess: int - how much the limit was exceeded + """ + super(BeanCounterWarning, self).__init__(resource, uid, excess) + self.tag = 'WARN' + + +class BeanCounterVerifier(object): + """Verifier wrapper for bean counters.""" + def __init__(self, options): + """Create a new set of verifiers + + Args: + options: optparse.Values, options used: + warning: int - generic warning level + critical: int - generic critical level + res_warning: dict - resource name:int level + res_critical: dict - resource name:int level + """ + # add common thresholds + self.thresholds = [BeanCounterThreshold(None, options.critical, options.warning)] + for resource, level in options.res_warning.items(): + # has both critical and warning values? + if resource in options.res_critical: + self.thresholds.append(BeanCounterThreshold(resource, options.res_critical[resource], level)) + else: + self.thresholds.append(BeanCounterThreshold(resource, -1, level)) + for resource, level in options.res_critical.iteritems(): + # already defined during warning value loop? + if not resource in options.res_warning: + self.thresholds.append(BeanCounterThreshold(resource, level, -1)) + + def verify(self, beancounters, use_maxheld=False): + """Verify BeanCounters + + Args: + beancounters: list of BeanCounters + use_maxheld: bool - use maxheld instead of held counter for limits + + Returns: + list of lists (see BeanCounterThreshold.check) + """ + results = [] + for beancounter in beancounters: + for threshold in self.thresholds: + result = threshold.check(beancounter, use_maxheld) + if result: + results.append(result) + return results + + +def shorten_hostname(hostname, parts=2): + """Shorten hostname by taking only certain parts from the beginning of it. + + Args: + hostname: str - FQDN + parts: int - number of parts to keep + """ + # return at least the hostname + if (parts < 1): + parts = 1 + if hostname: + return '.'.join(hostname.split('.')[:parts]) + return None + + +def get_vm_hostname(uid, confpath=OPENVZ_CONFPATH): + """Get hostname for a vm by getting the hostname from config file + + Args: + uid: int - uid / container context number of vm + confpath: str - folder in which to find .conf + + Returns: + str: FQDN as configured, or None + """ + try: + result = re.search(r'HOSTNAME="(.*)"', open(confpath+"/"+str(uid)+".conf", "r").read()) + if result: + return result.group(1) + except IOError: + return None + + +def get_local_fqdn(): + """Get local hostname.""" + return socket.gethostname() + + +def get_hostname(uid, confpath=OPENVZ_CONFPATH, parts=2): + """Get and shorten OpenVZ vm hostname + + Args: + uid: ints - uid / container context number + confpath: str - folder in which to find .conf + parts: int - number of hostname parts to return + + Returns: + str: hostname + """ + # the hypervisor itself, or a VM -> use local hostname + # NOTE: this assumes a correct confpath and non-existence of it on VMs. + if uid == 0 or not os.path.exists(confpath): + hostname = get_local_fqdn() + else: + hostname = get_vm_hostname(uid, confpath) + # drop ending domain parts of FQDN + return shorten_hostname(hostname, parts) + + +def read_data(options): + """Read OpenVZ counter data from system + + Args: + options: optparse object: + sudo: bool - use sudo to read file or not + filename: str - filename to read + + Return: + str: verbatim file contents + + Sideeffects: + May exit to system directly. + """ + # read into a string directly, or with a "sudo cat" + if not options.sudo: + try: + data = open(options.filename, "r").read() + except IOError, (errorcode, errorstr): + # file not found? not an OpenVZ system! + if (errorcode == errno.ENOENT): + print "OK: Not an OpenVZ system, no need to worry about beancounters." + sys.exit(0) + elif (errorcode == errno.EACCES): + print "UNKNOWN: Not permitted to read beancounter information." + sys.exit(3) + else: + print "CRITICAL: Unknown problem reading beancounters." + sys.exit(2) + else: + # call a non-interactive sudo to read the file + # needs to be configured via e.g. /etc/sudoers + try: + data = subprocess.Popen(["/usr/bin/sudo", "-n", "cat", options.filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] + except OSError, (errorcode, errorstr): + print "CRITICAL: Can't execute sudo to read beancounters. (%d - %s)" % (errorcode, errorstr) + sys.exit(2) + if not data: + print "CRITICAL: Trying to read beancounters with sudo didn't yield data." + sys.exit(2) + return data + + +def create_optionsparser(): + """Create optparse.OptionParser object for us + + Returns: + optparse.OptionParser object + """ + usage = "usage: %prog [options]" + parser = optparse.OptionParser(usage=usage, option_class=ExtOption) + parser.add_option("-w", "--warning", dest="warning", default=90, + action="store_int", help="default warning threshold percentage for everything (default: %default)") + parser.add_option("-c", "--critical", dest="critical", default=95, + action="store_int", help="default critical threshold percentage for everything (default: %default)") + parser.add_option("-W", "--resource-warning", dest="res_warning", default={}, + action="keyvalgroup", help="resource specific warning percentage (e.g. foo:50,bar:60)") + parser.add_option("-C", "--resource-critical", dest="res_critical", default={}, + action="keyvalgroup", help="resource-specific critical percentage (e.g. foo:90,bar:95)") + parser.add_option("-f", "--file", dest="filename", default='/proc/user_beancounters', + action="store", help="read beancounters from file (default: %default)") + parser.add_option("-s", "--sudo", dest="sudo", default=False, + action="store_true", help="use 'sudo cat filename' to read file as root (filename see -f)") + parser.add_option("-p", "--configs", dest="confpath", default=OPENVZ_CONFPATH, + action="store", help="path for OpenVZ config path (to get hostnames, default: %default)") + parser.add_option("-d", "--domainparts", dest="domainparts", default=2, + action="store_int", help="how many domain parts of the hostname to keep (default: %default)") + parser.add_option("-u", "--ubc-store-path", dest="storepath", default=OPENVZ_UBCSTORE, + action="store", help="path for vzubc relative information (default: %default)") + parser.add_option("-m", "--use-maxheld", dest="use_maxheld", default=False, + action="store_true", help="use resource's maxheld instead of current_held (default: %default)") + parser.add_option("-a", "--absolute-fails", dest="use_absolute_failcnt", default=False, + action="store_true", help="don't use vzubc's relative fail counters (default: %default)") + return parser + + +class ExtOption(optparse.Option): + """Specialized Option class which parses key:val,key:val parameters and integers + See optparse documentation for detailed explanation of how this works.""" + ACTIONS = optparse.Option.ACTIONS + ("keyvalgroup", "store_int",) + STORE_ACTIONS = optparse.Option.STORE_ACTIONS + ("keyvalgroup", "store_int",) + TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + ("keyvalgroup", "store_int",) + ALWAYS_TYPED_ACTIONS = optparse.Option.ALWAYS_TYPED_ACTIONS + ("keyvalgroup", "store_int",) + + def take_action(self, action, dest, opt, value, values, parser): + if action == "keyvalgroup": + keyvals = value.split(",") + for keyval in keyvals: + key, val = keyval.split(":") + if re.match(r'\d+', val): + val = int(val) + values.ensure_value(dest, {}).update({key:val}) + elif action == "store_int": + setattr(values, dest, int(value)) + else: + optparse.Option.take_action(self, action, dest, opt, value, values, parser) + + +def __main__(): + optionsparser = create_optionsparser() + # args parsing will exit the program if options are used wrong or help is + # requested. optionsparser.error exists to system as well - we call it when + # extra arguments are given. none are expected, we use options only. + options, args = optionsparser.parse_args() + if args: + optionsparser.error("incorrect number of arguments") + + # NOTE: read_data itself may exit to system in some cases + data = read_data(options) + if not data: + print "CRITICAL: No data given while reading beancounters." + sys.exit(2) + + # parse beancounters, create verifiers, verify beancounters + try: + beancounterparser = BeanCounterParser(data, options.use_absolute_failcnt, options.storepath) + except NoBeanCountersFoundError: + print "CRITICAL: No beancounters found in data read." + sys.exit(2) + + beancounterverifier = BeanCounterVerifier(options) + beancounteralerts = beancounterverifier.verify(beancounterparser.get_beancounters(), options.use_maxheld) + + # find uids and combine alert groups + fail = {} + over = {} + crit = {} + warn = {} + for alert in beancounteralerts: + if isinstance (alert, BeanCounterFailure): + fail.setdefault(alert.uid, {}) + fail[alert.uid][alert.resource] = alert.excess + elif isinstance (alert, BeanCounterOvershoot): + over.setdefault(alert.uid, {}) + over[alert.uid][alert.resource] = alert.excess + elif isinstance (alert, BeanCounterCritical): + crit.setdefault(alert.uid, {}) + crit[alert.uid][alert.resource] = alert.excess + elif isinstance (alert, BeanCounterWarning): + warn.setdefault(alert.uid, {}) + warn[alert.uid][alert.resource] = alert.excess + + # default message & exitcode if everything is fine + message = "OK: all beancounters below configured thresholds" + perfdata = "" + exitcode = 0 + + # set message prefix and errocode accordingly + if fail or over or crit: + message = "CRITICAL:" + exitcode = 2 + elif warn: + message = "WARNING:" + exitcode = 1 + + # combine specific Nagios message(s) + for level, tag in (fail, 'FAIL'), (over, 'OVER'), (crit, 'CRIT'), (warn, 'WARN'): + if level: + message += " %s:" % tag + perfdata += " %s:" % tag + for uid, counters in level.items(): + hostname = get_hostname(uid, options.confpath, options.domainparts) + message += " %s(%d)" % (hostname, uid) + perfdata += " HOST %s(%d):" % (hostname, uid) + for resource, counter in counters.items(): + perfdata += " %s(+%d)" % (resource, counter) + + # output message & exit + if len(perfdata): + print "%s|%s" % (message, perfdata) + else: + print message + sys.exit(exitcode) + + +if __name__ == "__main__": + __main__() Binary files nothing/check_openvz_beans.pyc and nagios-plugins-openvz-beans/check_openvz_beans.pyc differ diff -urN nothing/check_openvz_beans-test.py nagios-plugins-openvz-beans/check_openvz_beans-test.py --- nothing/check_openvz_beans-test.py 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/check_openvz_beans-test.py 2011-11-01 18:28:47.867345265 +0100 @@ -0,0 +1,775 @@ +#!/usr/bin/python +"""check_openvz_beans unit tests.""" + +__author__ = 'Andreas Kotes ' + +import __builtin__ +import os +import sys +import unittest +import mox +import optparse +import StringIO +import subprocess +import check_openvz_beans + +class BeanCounterTest(mox.MoxTestBase): + """Test the BeanCounter class.""" + def testCreate(self): + beancounter = check_openvz_beans.BeanCounter(123, "dummy 1 2 3 4 5") + self.assertEquals(beancounter.uid, 123) + self.assertEquals(beancounter.resource, 'dummy') + self.assertEquals(beancounter.held, 1) + self.assertEquals(beancounter.maxheld, 2) + self.assertEquals(beancounter.barrier, 3) + self.assertEquals(beancounter.limit, 4) + self.assertEquals(beancounter.failcnt, 5) + self.assertEquals(beancounter.__repr__(), + '') + self.assertTrue(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 2 3 4 5")) + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(124, "dummy 1 2 3 4 5")) + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "yummy 1 2 3 4 5")) + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 2 2 3 4 5")) + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 3 3 4 5")) + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 2 4 4 5")) + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 2 3 5 5")) + self.assertFalse(beancounter == check_openvz_beans.BeanCounter(123, "dummy 1 2 3 4 6")) + + +class BeanCounterParserTest(mox.MoxTestBase): + """Test the BeanCounterParser class.""" + def setUp(self): + super(BeanCounterParserTest, self).setUp() + + def test_init_and_parsebeancounters(self): + 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") + self.assertEquals(beancounters.get_beancounters(), [ + check_openvz_beans.BeanCounter(123, ' dummy1 1 2 3 4 5'), + check_openvz_beans.BeanCounter(123, ' dummy2 54 63 62 13 53'), + check_openvz_beans.BeanCounter(234, ' dummy3 9 8 7 6 5')]) + + def test_parsefail(self): + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(check_openvz_beans.NoBeanCountersFoundError, check_openvz_beans.BeanCounterParser, "nonsense") + + +class BeanCounterThresholdTest(mox.MoxTestBase): + """Test the BeanCounterThreshold class.""" + def setUp(self): + super(BeanCounterThresholdTest, self).setUp() + # Mock BeanCounters + self.beancounter_mock = self.mox.CreateMock(check_openvz_beans.BeanCounter) + self.beancounter_mock.uid = 123 + self.beancounter_mock.failcnt = 0 + + def testCreateSimple(self): + threshold = check_openvz_beans.BeanCounterThreshold() + self.assertEquals(threshold.resource, None) + self.assertEquals(threshold.critical, 95) + self.assertEquals(threshold.warning, 90) + self.assertEquals(threshold.__repr__(), + '') + + def testCreateCustom(self): + threshold = check_openvz_beans.BeanCounterThreshold('dummy', 70, 60) + self.assertEquals(threshold.resource, 'dummy') + self.assertEquals(threshold.critical, 70) + self.assertEquals(threshold.warning, 60) + self.assertEquals(threshold.__repr__(), + '') + + def testCheckSimpleOkay(self): + self.beancounter_mock.resource = 'dummy_okay' + self.beancounter_mock.held = 1 + self.beancounter_mock.maxheld = 2 + self.beancounter_mock.barrier = 3 + self.beancounter_mock.limit = 4 + threshold = check_openvz_beans.BeanCounterThreshold() + self.assertFalse(threshold.check(self.beancounter_mock)) + + def testCheckSimpleOkayNegWarn(self): + self.beancounter_mock.resource = 'dummy_okay' + self.beancounter_mock.held = 1 + self.beancounter_mock.maxheld = 2 + self.beancounter_mock.barrier = 3 + self.beancounter_mock.limit = 4 + threshold = check_openvz_beans.BeanCounterThreshold(warning=-1) + self.assertEqual(threshold.check(self.beancounter_mock), None) + + def testCheckSimpleOkayNegCrit(self): + self.beancounter_mock.resource = 'dummy_okay' + self.beancounter_mock.held = 1 + self.beancounter_mock.maxheld = 2 + self.beancounter_mock.barrier = 3 + self.beancounter_mock.limit = 4 + threshold = check_openvz_beans.BeanCounterThreshold(critical=-1) + self.assertEqual(threshold.check(self.beancounter_mock), None) + + def testCheckSimpleFail(self): + # failcnt engaged + self.beancounter_mock.resource = 'dummy_fail' + self.beancounter_mock.held = 1 + self.beancounter_mock.maxheld = 2 + self.beancounter_mock.barrier = 3 + self.beancounter_mock.limit = 4 + self.beancounter_mock.failcnt = 1 + threshold = check_openvz_beans.BeanCounterThreshold() + result = threshold.check(self.beancounter_mock) + self.assertTrue(result) + self.assertEquals(result.tag, 'FAIL') + + def testCheckSimpleEqual(self): + # eq Limit + self.beancounter_mock.resource = 'dummy_equal' + self.beancounter_mock.held = 50 + self.beancounter_mock.maxheld = 100 + self.beancounter_mock.barrier = 100 + self.beancounter_mock.limit = 100 + threshold = check_openvz_beans.BeanCounterThreshold(critical=101, warning=101) + self.assertFalse(threshold.check(self.beancounter_mock)) + + def testCheckSimpleOverGt(self): + # over Limit + self.beancounter_mock.resource = 'dummy_over' + self.beancounter_mock.held = 4 + self.beancounter_mock.maxheld = 4 + self.beancounter_mock.barrier = 3 + self.beancounter_mock.limit = 3 + threshold = check_openvz_beans.BeanCounterThreshold() + result = threshold.check(self.beancounter_mock) + self.assertTrue(result) + self.assertEquals(result.tag, 'OVER') + + def testCheckSimpleCritEq(self): + # eq critical value + self.beancounter_mock.resource = 'dummy_crit_eq' + self.beancounter_mock.held = 95 + self.beancounter_mock.maxheld = 95 + self.beancounter_mock.barrier = 100 + self.beancounter_mock.limit = 100 + threshold = check_openvz_beans.BeanCounterThreshold() + result = threshold.check(self.beancounter_mock) + self.assertTrue(result) + self.assertEquals(result.tag, 'CRIT') + + def testCheckSimpleCritGt(self): + # gt critical value + self.beancounter_mock.resource = 'dummy_crit_gt' + self.beancounter_mock.held = 96 + self.beancounter_mock.maxheld = 96 + self.beancounter_mock.barrier = 100 + self.beancounter_mock.limit = 100 + threshold = check_openvz_beans.BeanCounterThreshold() + result = threshold.check(self.beancounter_mock) + self.assertTrue(result) + self.assertEquals(result.tag, 'CRIT') + + def testCheckSimpleWarnEq(self): + # eq warning value + self.beancounter_mock.resource = 'dummy_warn_eq' + self.beancounter_mock.held = 90 + self.beancounter_mock.maxheld = 90 + self.beancounter_mock.barrier = 100 + self.beancounter_mock.limit = 100 + threshold = check_openvz_beans.BeanCounterThreshold() + result = threshold.check(self.beancounter_mock) + self.assertTrue(result) + self.assertEquals(result.tag, 'WARN') + + def testCheckSimpleWarnGt(self): + # gt warning value + self.beancounter_mock.resource = 'dummy_warn_gt' + self.beancounter_mock.held = 91 + self.beancounter_mock.maxheld = 91 + self.beancounter_mock.barrier = 100 + self.beancounter_mock.limit = 100 + threshold = check_openvz_beans.BeanCounterThreshold() + result = threshold.check(self.beancounter_mock) + self.assertTrue(result) + self.assertEquals(result.tag, 'WARN') + + def testCheckBarrierQuota(self): + # limited by barrier -> would not warn if limited by limit + self.beancounter_mock.resource = 'numflock' + self.beancounter_mock.held = 45 + self.beancounter_mock.maxheld = 45 + self.beancounter_mock.barrier = 50 + self.beancounter_mock.limit = 100 + threshold = check_openvz_beans.BeanCounterThreshold() + result = threshold.check(self.beancounter_mock) + self.assertTrue(result) + self.assertEquals(result.tag, 'WARN') + + def testCheckLimitQuota(self): + # limited by limit -> would be over if limited by barrier + self.beancounter_mock.resource = 'numproc' + self.beancounter_mock.held = 50 + self.beancounter_mock.maxheld = 56 + self.beancounter_mock.barrier = 50 + self.beancounter_mock.limit = 100 + threshold = check_openvz_beans.BeanCounterThreshold() + self.assertFalse(threshold.check(self.beancounter_mock)) + + def testCheckNotResponsible(self): + # gt critical value + self.beancounter_mock.resource = 'dummy_crit_gt' + self.beancounter_mock.held = 50 + self.beancounter_mock.maxheld = 96 + self.beancounter_mock.barrier = 100 + self.beancounter_mock.limit = 100 + threshold = check_openvz_beans.BeanCounterThreshold('differentresource') + self.assertFalse(threshold.check(self.beancounter_mock)) + + +class BeanCounterViolationTest(unittest.TestCase): + """Test the BeanCounterViolation class.""" + def testCreate(self): + violation = check_openvz_beans.BeanCounterViolation('dummy', 123, 10) + self.assertEquals(violation.uid, 123) + self.assertEquals(violation.resource, 'dummy') + self.assertEquals(violation.tag, 'VIOL') + self.assertEquals(violation.excess, 10) + self.assertEquals(violation.__repr__(), + '') + + +class BeanCounterFailureTest(unittest.TestCase): + """Test the BeanCounterFailure class.""" + def testCreate(self): + violation = check_openvz_beans.BeanCounterFailure('dummy', 123, 10) + self.assertEquals(violation.uid, 123) + self.assertEquals(violation.resource, 'dummy') + self.assertEquals(violation.tag, 'FAIL') + self.assertEquals(violation.excess, 10) + self.assertEquals(violation.__repr__(), + '') + + +class BeanCounterOvershootTest(unittest.TestCase): + """Test the BeanCounterOvershoot class.""" + def testCreate(self): + violation = check_openvz_beans.BeanCounterOvershoot('dummy', 123, 10) + self.assertEquals(violation.uid, 123) + self.assertEquals(violation.resource, 'dummy') + self.assertEquals(violation.tag, 'OVER') + self.assertEquals(violation.excess, 10) + self.assertEquals(violation.__repr__(), + '') + + +class BeanCounterCritical(unittest.TestCase): + """Test the BeanCounterCritical class.""" + def testCreate(self): + violation = check_openvz_beans.BeanCounterCritical('dummy', 123, 10) + self.assertEquals(violation.uid, 123) + self.assertEquals(violation.resource, 'dummy') + self.assertEquals(violation.tag, 'CRIT') + self.assertEquals(violation.excess, 10) + self.assertEquals(violation.__repr__(), + '') + + +class BeanCounterWarningTest(unittest.TestCase): + """Test the BeanCounterWarning class.""" + def testCreate(self): + violation = check_openvz_beans.BeanCounterWarning('dummy', 123, 10) + self.assertEquals(violation.uid, 123) + self.assertEquals(violation.resource, 'dummy') + self.assertEquals(violation.tag, 'WARN') + self.assertEquals(violation.excess, 10) + self.assertEquals(violation.__repr__(), + '') + + +class BeanCounterVerifierInitTest(mox.MoxTestBase): + """Test the BeanCounterVerifier class (1/2).""" + def setUp(self): + super(BeanCounterVerifierInitTest, self).setUp() + # Mock BeanCounterThresholds + self.values_mock = self.mox.CreateMock(optparse.Values) + self.threshold_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterThreshold) + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterThreshold') + + # check with common thresholds + self.values_mock.critical = 95 + self.values_mock.warning = 90 + check_openvz_beans.BeanCounterThreshold(None, 95, 90) + + # check with resource-specific thresholds + self.values_mock.res_critical = {'foo':20, 'bar':30} + self.values_mock.res_warning = {'foo':10, 'baz':20} + check_openvz_beans.BeanCounterThreshold('foo', 20, 10) + check_openvz_beans.BeanCounterThreshold('baz', -1, 20) + check_openvz_beans.BeanCounterThreshold('bar', 30, -1) + + self.mox.ReplayAll() + + def test_init(self): + beancounterverifier = check_openvz_beans.BeanCounterVerifier(self.values_mock) + + +class BeanCounterVerifierVerifyTest(mox.MoxTestBase): + """Test the BeanCounterVerifier class (2/2).""" + def setUp(self): + super(BeanCounterVerifierVerifyTest, self).setUp() + # Mock BeanCounterThresholds + self.values_mock = self.mox.CreateMock(optparse.Values) + self.threshold_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterThreshold) + self.beancounter_mock = self.mox.CreateMock(check_openvz_beans.BeanCounter) + self.violation_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterViolation) + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterThreshold') + self.mox.StubOutWithMock(check_openvz_beans.BeanCounterThreshold, 'check') + + # check with common thresholds + self.values_mock.critical = 95 + self.values_mock.warning = 90 + self.values_mock.res_critical = {} + self.values_mock.res_warning = {} + check_openvz_beans.BeanCounterThreshold(None, 95, 90).AndReturn(self.threshold_mock) + self.threshold_mock.check(mox.IsA(check_openvz_beans.BeanCounter), False) + self.threshold_mock.check(mox.IsA(check_openvz_beans.BeanCounter), False).AndReturn(self.violation_mock) + self.threshold_mock.check(mox.IsA(check_openvz_beans.BeanCounter), False).AndReturn(self.violation_mock) + self.threshold_mock.check(mox.IsA(check_openvz_beans.BeanCounter), False).AndReturn(self.violation_mock) + + self.mox.ReplayAll() + + def test_verify(self): + beancounterverifier = check_openvz_beans.BeanCounterVerifier(self.values_mock) + beancounter_mock = self.mox.CreateMock(check_openvz_beans.BeanCounter) + # first one is okay + self.assertEquals(len(beancounterverifier.verify([beancounter_mock,], False)), 0) + # second one has a violation + self.assertEquals(len(beancounterverifier.verify([beancounter_mock,], False)), 1) + # third + fourth each have a violation + self.assertEquals(len(beancounterverifier.verify([beancounter_mock, beancounter_mock], False)), 2) + + +class ExtOptionTest(mox.MoxTestBase): + """Test the ExtOption class.""" + def setUp(self): + super(ExtOptionTest, self).setUp() + # Mock optparse objects + self.values1 = self.mox.CreateMockAnything() + self.values1.ensure_value('foobar', {}).AndReturn(self.values1) + self.values1.update({'key1': 'val1'}) + self.values1.ensure_value('foobar', {}).AndReturn(self.values1) + self.values1.update({'key2': 'val2'}) + + self.values2 = self.mox.CreateMockAnything() + self.values2.intval = 0 + + self.values3 = self.mox.CreateMockAnything() + + self.mox.StubOutWithMock(optparse.Option, '__init__') + self.mox.StubOutWithMock(optparse.Option, 'take_action') + optparse.Option.__init__() + optparse.Option.take_action(mox.IsA(optparse.Option), 'something_else', None, None, None, None, None) + + self.mox.ReplayAll() + + def test_take_action(self): + option = check_openvz_beans.ExtOption() + option.take_action('keyvalgroup', 'foobar', 'dunno', 'key1:val1,key2:val2', self.values1, 'dunno') + + self.assertEquals(self.values2.intval, 0) + option.take_action('store_int', 'intval', 'dunno', '10', self.values2, 'dunno') + self.assertEquals(self.values2.intval, 10) + + option.take_action('something_else', None, None, None, None, None) + + +class ReadDataTest(mox.MoxTestBase): + """Check read_data functions without sudo.""" + def setUp(self): + super(ReadDataTest, self).setUp() + # Mock optparse objects + self.options_mock = self.mox.CreateMock(optparse.Values) + self.popen_mock = self.mox.CreateMock(subprocess.Popen) + self.mox.StubOutWithMock(subprocess, 'Popen') + self.saved_stdout = sys.stdout + sys.stdout = StringIO.StringIO() + + def tearDown(self): + super(ReadDataTest, self).tearDown() + sys.stdout = self.saved_stdout + + def test_read_data(self): + """Test reading OpenVZ counter data from system without sudo - existing file.""" + self.options_mock.sudo = False + self.options_mock.filename = 'testdata/hostcrit.bcs' + self.mox.ReplayAll() + + result = check_openvz_beans.read_data(self.options_mock) + + self.assertTrue(isinstance(result, str)) + self.assertEquals(len(result), os.path.getsize(self.options_mock.filename)) + + def test_read_data_missing_file(self): + """Test reading OpenVZ counter data from system without sudo - missing file.""" + self.options_mock.sudo = False + self.options_mock.filename = '/nonexisting/foobar' + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.read_data, self.options_mock) + self.assertEqual(sys.stdout.getvalue(), 'OK: Not an OpenVZ system, no need to worry about beancounters.\n') + + def test_read_data_missing_file(self): + """Test reading OpenVZ counter data from system without sudo - unreadable file.""" + self.options_mock.sudo = False + self.options_mock.filename = 'testdata/unreadable.bcs' + try: + os.unlink(self.options_mock.filename) + except OSError: + pass + + open(self.options_mock.filename, 'w').close() + os.chmod(self.options_mock.filename, 0000) + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.read_data, self.options_mock) + self.assertEqual(sys.stdout.getvalue(), 'UNKNOWN: Not permitted to read beancounter information.\n') + + try: + os.unlink(self.options_mock.filename) + except OSError: + pass + + # FIXME: not all code pathes completely tested due to unmockability of open() + + def test_read_data_sudo(self): + """Test reading OpenVZ counter data from system with sudo - sucess.""" + self.options_mock.sudo = True + self.options_mock.filename = 'irrelevant' + subprocess.Popen(['/usr/bin/sudo', '-n', 'cat', 'irrelevant'], stderr=-1, stdout=-1).AndReturn(self.popen_mock) + self.popen_mock.communicate().AndReturn(['somedata']) + self.mox.ReplayAll() + + result = check_openvz_beans.read_data(self.options_mock) + + self.assertTrue(isinstance(result, str)) + self.assertEquals(result, 'somedata') + + def test_read_data_sudo_oserror(self): + """Test reading OpenVZ counter data from system with sudo - OSError.""" + self.options_mock.sudo = True + self.options_mock.filename = 'irrelevant' + subprocess.Popen(['/usr/bin/sudo', '-n', 'cat', 'irrelevant'], stderr=-1, stdout=-1).AndRaise(OSError(42, 'mocketimock')) + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.read_data, self.options_mock) + self.assertEqual(sys.stdout.getvalue(), "CRITICAL: Can't execute sudo to read beancounters. (42 - mocketimock)\n") + + def test_read_data_sudo_nodata(self): + """Test reading OpenVZ counter data from system with sudo - no data.""" + self.options_mock.sudo = True + self.options_mock.filename = 'irrelevant' + + subprocess.Popen(['/usr/bin/sudo', '-n', 'cat', 'irrelevant'], stderr=-1, stdout=-1).AndReturn(self.popen_mock) + self.popen_mock.communicate().AndReturn(['']) + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.read_data, self.options_mock) + self.assertEqual(sys.stdout.getvalue(), "CRITICAL: Trying to read beancounters with sudo didn't yield data.\n") + + +class GlobalsTest(mox.MoxTestBase): + """Check global functions.""" + def setUp(self): + super(GlobalsTest, self).setUp() + self.saved_stdout, sys.stdout = sys.stdout, StringIO.StringIO() + + def tearDown(self): + super(GlobalsTest, self).tearDown() + sys.stdout = self.saved_stdout + + def test_shorten_hostname(self): + """Test shortening hostnames.""" + self.testfqdn = "hostname.with.long.domain.name" + # the (short) hostname should be returned in any case + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 0), "hostname") + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 1), "hostname") + # various lengths should be returned properly + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 2), "hostname.with") + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 5), "hostname.with.long.domain.name") + # if more is requested than is there, just give all there is + self.assertEqual(check_openvz_beans.shorten_hostname(self.testfqdn, 42), "hostname.with.long.domain.name") + + def test_get_vm_hostname(self): + """Test getting vm hostnames.""" + # two known existing hosts + self.assertEqual(check_openvz_beans.get_vm_hostname(10, 'testdata'), 'vmokay.pr.foo.test.your.do.main') + self.assertEqual(check_openvz_beans.get_vm_hostname(60, 'testdata'), 'vmover1.st.foo.test.your.do.main') + # one host whose config file doesn't contain a hostname + self.assertEqual(check_openvz_beans.get_vm_hostname(99, 'testdata'), None) + # one host where no config file exists + self.assertEqual(check_openvz_beans.get_vm_hostname(90, 'testdata'), None) + + def test_get_local_fqdn(self): + """Test getting local hostnames.""" + # due to the local nature of hostnames we can only check wether it has a qualifying '.' in it + self.assertTrue(check_openvz_beans.get_local_fqdn().find('.') > 0) + + def test_get_hostname(self): + """Test getting and shortening hostnames.""" + self.mox.StubOutWithMock(check_openvz_beans, 'get_vm_hostname') + self.mox.StubOutWithMock(check_openvz_beans, 'get_local_fqdn') + check_openvz_beans.get_local_fqdn().AndReturn('localhost.localdomain') + check_openvz_beans.get_local_fqdn().AndReturn('localhost.localdomain') + check_openvz_beans.get_vm_hostname(10, 'testdata').AndReturn('hostname.with.long.domain.name') + self.mox.ReplayAll() + + self.assertEqual(check_openvz_beans.get_hostname(0, 'testdata'), 'localhost.localdomain') + self.assertEqual(check_openvz_beans.get_hostname(99, 'nonexistent'), 'localhost.localdomain') + self.assertEqual(check_openvz_beans.get_hostname(10, 'testdata'), 'hostname.with') + + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def test_create_optionsparser(self): + """Test creating a custom OptionParser.""" + parser = check_openvz_beans.create_optionsparser() + self.assertTrue(isinstance(parser, optparse.OptionParser)) + # FIXME: do we want to check more? + + +class MainEarlyFailuresTest(mox.MoxTestBase): + def setUp(self): + super(MainEarlyFailuresTest, self).setUp() + self.saved_stdout, sys.stdout = sys.stdout, StringIO.StringIO() + self.option_mock = self.mox.CreateMock(optparse.Values) + self.option_mock.use_absolute_failcnt = False + self.option_mock.storepath = 'testdata' + + def tearDown(self): + sys.stdout = self.saved_stdout + + def test___main___wrongargs(self): + """Test the __main__ function -- wrong args.""" + optionsparser_mock = self.mox.CreateMock(optparse.OptionParser) + self.mox.StubOutWithMock(check_openvz_beans, 'create_optionsparser') + check_openvz_beans.create_optionsparser().AndReturn(optionsparser_mock) + optionsparser_mock.parse_args().AndReturn((self.option_mock, ['arg',])) + optionsparser_mock.error('incorrect number of arguments').AndRaise(SystemExit) + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + + def test___main___nodata(self): + """Test the __main__ function -- no data.""" + optionsparser_mock = self.mox.CreateMock(optparse.OptionParser) + self.mox.StubOutWithMock(check_openvz_beans, 'create_optionsparser') + check_openvz_beans.create_optionsparser().AndReturn(optionsparser_mock) + optionsparser_mock.parse_args().AndReturn((self.option_mock, [])) + + self.mox.StubOutWithMock(check_openvz_beans, 'read_data') + check_openvz_beans.read_data(mox.IsA(optparse.Values)).AndReturn('') + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: No data given while reading beancounters.\n') + + def test___main___nobeancounters(self): + """Test the __main__ function -- no beancounters""" + optionsparser_mock = self.mox.CreateMock(optparse.OptionParser) + self.mox.StubOutWithMock(check_openvz_beans, 'create_optionsparser') + check_openvz_beans.create_optionsparser().AndReturn(optionsparser_mock) + optionsparser_mock.parse_args().AndReturn((self.option_mock, [])) + + self.mox.StubOutWithMock(check_openvz_beans, 'read_data') + check_openvz_beans.read_data(mox.IsA(optparse.Values)).AndReturn('somedata') + + beancounterparser_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterParser) + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterParser') + check_openvz_beans.BeanCounterParser('somedata', False, 'testdata').AndRaise(check_openvz_beans.NoBeanCountersFoundError) + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: No beancounters found in data read.\n') + + +class MainRegularTest(mox.MoxTestBase): + def setUp(self): + super(MainRegularTest, self).setUp() + self.saved_stdout, sys.stdout = sys.stdout, StringIO.StringIO() + + self.optionsparser_mock = self.mox.CreateMock(optparse.OptionParser) + self.option_mock = self.mox.CreateMock(optparse.Values) + self.option_mock.confpath = 'irrelevant' + self.option_mock.domainparts = 21 + self.option_mock.use_absolute_failcnt = False + self.option_mock.storepath = 'testdata' + self.option_mock.use_maxheld = False + self.mox.StubOutWithMock(check_openvz_beans, 'create_optionsparser') + check_openvz_beans.create_optionsparser().AndReturn(self.optionsparser_mock) + self.optionsparser_mock.parse_args().AndReturn((self.option_mock, [])) + + self.mox.StubOutWithMock(check_openvz_beans, 'read_data') + check_openvz_beans.read_data(mox.IsA(optparse.Values)).AndReturn('somedata') + + self.beancounterparser_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterParser) + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterParser') + check_openvz_beans.BeanCounterParser('somedata', False, 'testdata').AndReturn(self.beancounterparser_mock) + self.beancounterparser_mock.get_beancounters().AndReturn(None) + + self.beancounterverifier_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterVerifier) + self.mox.StubOutWithMock(check_openvz_beans, 'BeanCounterVerifier') + check_openvz_beans.BeanCounterVerifier(mox.IsA(optparse.Values)).AndReturn(self.beancounterverifier_mock) + + def tearDown(self): + sys.stdout = self.saved_stdout + + def test___main___noalerts(self): + """Test the __main__ function -- no alerts.""" + self.beancounterverifier_mock.verify(None, False).AndReturn([]) + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + self.assertEqual(sys.stdout.getvalue(), 'OK: all beancounters below configured thresholds\n') + + def test___main___nospecificalert(self): + """Test the __main__ function -- no alerts.""" + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterViolation) + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + self.assertEqual(sys.stdout.getvalue(), 'OK: all beancounters below configured thresholds\n') + + def test___main___failalert(self): + """Test the __main__ function -- fail alert.""" + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterFailure) + beancounteralert_mock.uid = 123 + beancounteralert_mock.excess = 987 + beancounteralert_mock.resource = 'resource' + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) + + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('dummy') + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: FAIL: dummy(123)| FAIL: HOST dummy(123): resource(+987)\n') + + def test___main___overalert(self): + """Test the __main__ function -- over alert.""" + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterOvershoot) + beancounteralert_mock.uid = 123 + beancounteralert_mock.excess = 987 + beancounteralert_mock.resource = 'resource' + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) + + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('dummy') + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: OVER: dummy(123)| OVER: HOST dummy(123): resource(+987)\n') + + def test___main___critalert(self): + """Test the __main__ function -- crit alert.""" + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterCritical) + beancounteralert_mock.uid = 123 + beancounteralert_mock.excess = 987 + beancounteralert_mock.resource = 'resource' + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) + + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('dummy') + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + self.assertEqual(sys.stdout.getvalue(), 'CRITICAL: CRIT: dummy(123)| CRIT: HOST dummy(123): resource(+987)\n') + + def test___main___warnalert(self): + """Test the __main__ function -- warn alert.""" + beancounteralert_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterWarning) + beancounteralert_mock.uid = 123 + beancounteralert_mock.excess = 987 + beancounteralert_mock.resource = 'resource' + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_mock,]) + + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('dummy') + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + self.assertEqual(sys.stdout.getvalue(), 'WARNING: WARN: dummy(123)| WARN: HOST dummy(123): resource(+987)\n') + + def test___main___failwarnalert(self): + """Test the __main__ function -- failwarn alert.""" + beancounteralert_fail_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterFailure) + beancounteralert_fail_mock.uid = 123 + beancounteralert_fail_mock.excess = 987 + beancounteralert_fail_mock.resource = 'resource' + beancounteralert_warn_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterWarning) + beancounteralert_warn_mock.uid = 234 + beancounteralert_warn_mock.excess = 896 + beancounteralert_warn_mock.resource = 'resource' + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_fail_mock, beancounteralert_warn_mock]) + + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('foo') + check_openvz_beans.get_hostname(234, 'irrelevant', 21).AndReturn('bar') + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + 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') + + def test___main___multifailwarnalert(self): + """Test the __main__ function -- failwarn alert.""" + beancounteralert_fail1_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterFailure) + beancounteralert_fail1_mock.uid = 123 + beancounteralert_fail1_mock.excess = 987 + beancounteralert_fail1_mock.resource = 'resource1' + beancounteralert_fail2_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterFailure) + beancounteralert_fail2_mock.uid = 123 + beancounteralert_fail2_mock.excess = 987 + beancounteralert_fail2_mock.resource = 'resource2' + beancounteralert_warn1_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterWarning) + beancounteralert_warn1_mock.uid = 234 + beancounteralert_warn1_mock.excess = 896 + beancounteralert_warn1_mock.resource = 'resource' + beancounteralert_warn2_mock = self.mox.CreateMock(check_openvz_beans.BeanCounterWarning) + beancounteralert_warn2_mock.uid = 345 + beancounteralert_warn2_mock.excess = 896 + beancounteralert_warn2_mock.resource = 'resource' + self.beancounterverifier_mock.verify(None, False).AndReturn([beancounteralert_fail1_mock, beancounteralert_fail2_mock, beancounteralert_warn1_mock, beancounteralert_warn2_mock]) + + self.mox.StubOutWithMock(check_openvz_beans, 'get_hostname') + check_openvz_beans.get_hostname(123, 'irrelevant', 21).AndReturn('foo') + check_openvz_beans.get_hostname(345, 'irrelevant', 21).AndReturn('baz') + check_openvz_beans.get_hostname(234, 'irrelevant', 21).AndReturn('bar') + + self.mox.ReplayAll() + + # FIXME: check the Exception value. assertRaisesRegexp() is only available in Python unittest 2.7+ + self.assertRaises(SystemExit, check_openvz_beans.__main__) + 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') + +if __name__ == '__main__': + unittest.main() diff -urN nothing/debian/changelog nagios-plugins-openvz-beans/debian/changelog --- nothing/debian/changelog 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/debian/changelog 2011-11-01 18:30:10.247574999 +0100 @@ -0,0 +1,5 @@ +nagios-plugins-openvz-beans (0.5-0) stable; urgency=low + + * initial release packaging + + -- Andreas Kotes Thu, 01 Nov 2011 18:30:23 +0100 diff -urN nothing/debian/compat nagios-plugins-openvz-beans/debian/compat --- nothing/debian/compat 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/debian/compat 2011-10-17 14:23:00.655695188 +0200 @@ -0,0 +1 @@ +5 diff -urN nothing/debian/control nagios-plugins-openvz-beans/debian/control --- nothing/debian/control 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/debian/control 2011-11-01 16:54:48.381618568 +0100 @@ -0,0 +1,9 @@ +Source: nagios-plugins-openvz-beans +Section: net +Priority: extra +Maintainer: Andreas Kotes + +Package: nagios-plugins-openvz-beans +Architecture: all +Depends: python-minimal +Description: Nagios Plugin to check OpenVZ Bean Counters diff -urN nothing/debian/.gitignore nagios-plugins-openvz-beans/debian/.gitignore --- nothing/debian/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/debian/.gitignore 2011-10-17 14:23:00.655695188 +0200 @@ -0,0 +1,4 @@ +files +nagios-plugins-openvz-beans.debhelper.log +nagios-plugins-openvz-beans.substvars +nagios-plugins-openvz-beans/ diff -urN nothing/debian/rules nagios-plugins-openvz-beans/debian/rules --- nothing/debian/rules 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/debian/rules 2011-11-01 18:28:09.727238903 +0100 @@ -0,0 +1,33 @@ +#!/usr/bin/make -f +DEBIANDIR=$(CURDIR)/debian + +%: + dh $@ + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + install -d -m0755 $(DEBIANDIR)/nagios-plugins-openvz-beans/usr/lib/nagios/plugins + install -m0755 check_openvz_beans.py $(DEBIANDIR)/nagios-plugins-openvz-beans/usr/lib/nagios/plugins + install -d -m0755 $(DEBIANDIR)/nagios-plugins-openvz-beans/usr/share/doc/nagios-plugins-openvz-beans + install -m0644 README $(DEBIANDIR)/nagios-plugins-openvz-beans/usr/share/doc/nagios-plugins-openvz-beans + +# Build architecture-independent files here. +binary-indep: build install + dh_testdir -i + dh_testroot -i + dh_installchangelogs -i + dh_installdocs -i + dh_link -i + dh_compress -i + dh_fixperms -i + dh_installdeb -i + dh_gencontrol -i + dh_md5sums -i + dh_builddeb -i + +binary: binary-indep +.PHONY: build clean binary-indep binary install diff -urN nothing/.gitignore nagios-plugins-openvz-beans/.gitignore --- nothing/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/.gitignore 2011-10-17 14:23:00.695695306 +0200 @@ -0,0 +1 @@ +*.pyc diff -urN nothing/nagios-plugins-openvz-beans.spec nagios-plugins-openvz-beans/nagios-plugins-openvz-beans.spec --- nothing/nagios-plugins-openvz-beans.spec 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/nagios-plugins-openvz-beans.spec 2011-11-01 16:54:35.621582988 +0100 @@ -0,0 +1,34 @@ +Name: nagios-plugins-openvz-beans +Version: 0.5 +Release: 0 +Summary: Nagios Plugin to check OpenVZ Bean Counters +License: GPL +Group: Applications/System +Source: check_openvz_beans.py + +Autoreq: 0 +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root +BuildArch: noarch +Requires: python + +%description +Nagios Plugin to check OpenVZ Bean Counters + +%prep +rm -rf %{buildroot} + +%install +install -d -m0755 %{buildroot}%{_libdir}/nagios/plugins/contrib/ +install -m0755 %{_sourcefile} %{buildroot}%{_libdir}/nagios/plugins/contrib/ + + +%clean +rm -rf %{buildroot} + +%files +%dir %{_libdir}/nagios/plugins/contrib +%attr(755, root, root) %{_libdir}/nagios/plugins/contrib/check_openvz_beans.py + +%changelog +* Thu Oct 13 2011 Andreas Kotes 0.5 + - initial release packaging diff -urN nothing/README nagios-plugins-openvz-beans/README --- nothing/README 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/README 2011-11-01 18:04:40.633309403 +0100 @@ -0,0 +1,75 @@ +Nagios check for OpenVZ bean counters +===================================== + +General +------- + +This Nagios plugin allows to define and check for thresholds against bean +counters defined in /proc/user_beancounters, which are available in both OpenVZ +hypervisors and VMs. + +It's possible to set global thresholds via '-c' (for critical) and '-w' (for +warning) which apply to all resources checked. + +Tresholds for individual resources can be set by using '-C'/'-W' and giving +comma-separated key:value pairs. + +The script was written to be compatible with Python 2.4 and later. + +Reading /proc/user_beancounters +------------------------------- + +Since /proc/user_beancounters is only readable by the root user - and running +Nagios plugins as the root user is highly discouraged - you can either copy +this file and check against the copy using the -f option. Or - if you have set +up your /etc/sudoers file to allow it - you can use the -s option, which lets +check_openvz_beans use 'sudo cat /proc/user_beancounters' to read the file. + +Hostname handling +----------------- +The OpenVZ context IDs are resolved by grepping for the HOSTNAME variable in +the uid's config file found in the path given by '-p'. + +Hostnames (e.g. this.is.a.very.long.hostname) are shortened to their first two +components per default (e.g. this.is for the former one), but this length can +be configured using -d (e.g. '-d 1' for 'this', '-d 3' for 'this.is.a'), +allowing you to identify the affected VM(s) in the Nagios alert message +already. + +Output +------ + +The output follows the Nagios Plugin API 3.0, i.e. the script gives the proper +exit codes (0 = OK, 1 = Warning, 2 = Critical, 3 = Unknown), a simple single +line of text, and additional perf-data. + +The single line of text informs about WARNING or CRITICAL state, and what's a +problem: + +FAIL - a resource against its limit and the was blocked by kernel +OVER - a resource was over its relevant threshold and will cause problems +CRIT - a resource ran close to its threshold by given (critical) percentage +WARN - a resource ran close to its threshold by given (warning) percentage + +Data used +--------- + +The value checked for each resource is the 'held' value, but you could use '-m' +to use the 'maxheld' value instead. You'll not be able to reset that value, +though, so any Nagios warning/failure status will stay until the next +hypervisor reboot. + +Failure counters can't be reset either, but the plugin reads the data stored by +vzubc's relative mode, i.e. if you inspect the problem with 'vzubc -r', the +critical status will be resolved. This behaviour can be turned off with '-a'. + +If you are storing data somewhere else than the default '/tmp/vzubc.store', you +can change that via '-u'. + +vzubc is provided by the vzctl package which isn't used by this code directly, +nor is there a dependency - you'll have to install it yourself if you want to +use this functionality. + +Each group lists the hostname & uid of the VM in the regular message, +additionally the resource name and how much its threshold was exeeded are given +in the perfdata line. diff -urN nothing/README.source nagios-plugins-openvz-beans/README.source --- nothing/README.source 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/README.source 2011-11-01 16:29:20.667358270 +0100 @@ -0,0 +1,16 @@ +The implementation can be tested by calling its test scripts +./check_openvz_beans-test.py, which runs unit tests for almost all of its code, +and requires Python unittest >= 2.4 as well as pymox. + +Current code coverage of the unit tests is >95%. + +RPMs can be build using the following commandline: + +rpmbuild -bb -D "_sourcefile `pwd`/check_openvz_beans.py" nagios-plugins-openvz-beans.spec + +Debian / Ubuntu packages can be build by simply calling: + +dpkg-buildpackage + +... in both cases the proper developer tools for the corresponding distribution +have to be installed. diff -urN nothing/testdata/10.conf nagios-plugins-openvz-beans/testdata/10.conf --- nothing/testdata/10.conf 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/testdata/10.conf 2011-11-01 19:09:28.124150356 +0100 @@ -0,0 +1 @@ +HOSTNAME="vmokay.pr.foo.test.your.do.main" diff -urN nothing/testdata/60.conf nagios-plugins-openvz-beans/testdata/60.conf --- nothing/testdata/60.conf 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/testdata/60.conf 2011-11-01 19:09:32.834163485 +0100 @@ -0,0 +1 @@ +HOSTNAME="vmover1.st.foo.test.your.do.main" diff -urN nothing/testdata/hostcrit.bcs nagios-plugins-openvz-beans/testdata/hostcrit.bcs --- nothing/testdata/hostcrit.bcs 1970-01-01 01:00:00.000000000 +0100 +++ nagios-plugins-openvz-beans/testdata/hostcrit.bcs 2011-11-01 19:07:53.643886880 +0100 @@ -0,0 +1,170 @@ +Version: 2.5 + uid resource held maxheld barrier limit failcnt + 40: kmemsize 12345678 33456789 34567890 40000000 0 + lockedpages 0 5859 32768 65536 0 + privvmpages 1234567 2345678 3276800 9223372036854775807 0 + shmpages 688 1344 9223372036854775807 9223372036854775807 0 + dummy 0 0 0 0 0 + numproc 75 88 8000 8000 0 + physpages 199301 239798 0 9223372036854775807 0 + vmguarpages 0 0 4194304 9223372036854775807 0 + oomguarpages 199301 239798 4186112 9223372036854775807 0 + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 + numflock 4 14 200 220 0 + numpty 1 2 64 64 0 + numsiginfo 0 9 512 512 0 + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 + dgramrcvbuf 0 58680 262144 262144 0 + numothersock 115 125 360 360 0 + dcachesize 0 0 9223372036854775807 9223372036854775807 0 + numfile 1655 1958 9312 9312 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + numiptent 20 20 128 128 0 + 20: kmemsize 12345678 23456789 34567890 40000000 0 + lockedpages 0 5859 32768 65536 0 + privvmpages 1234567 2345678 3276800 9223372036854775807 3 + shmpages 688 1344 9223372036854775807 9223372036854775807 0 + dummy 0 0 0 0 0 + numproc 75 88 8000 8000 0 + physpages 199301 239798 0 9223372036854775807 0 + vmguarpages 0 0 4194304 9223372036854775807 0 + oomguarpages 199301 239798 4186112 9223372036854775807 0 + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 + numflock 4 14 200 220 0 + numpty 1 2 64 64 0 + numsiginfo 0 9 512 512 0 + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 + dgramrcvbuf 0 58680 262144 262144 0 + numothersock 115 125 360 360 0 + dcachesize 0 0 9223372036854775807 9223372036854775807 0 + numfile 1655 1958 9312 9312 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + numiptent 20 20 128 128 0 + 30: kmemsize 12345678 23456789 34567890 40000000 0 + lockedpages 0 5859 32768 65536 0 + privvmpages 1234567 2345678 3276800 9223372036854775807 3 + shmpages 688 1344 9223372036854775807 9223372036854775807 0 + dummy 0 0 0 0 0 + numproc 75 88 100 120 2 + physpages 199301 239798 0 9223372036854775807 0 + vmguarpages 0 0 4194304 9223372036854775807 0 + oomguarpages 199301 239798 4186112 9223372036854775807 0 + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 + numflock 4 14 200 220 0 + numpty 1 2 64 64 0 + numsiginfo 0 9 512 512 0 + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 + dgramrcvbuf 0 58680 262144 262144 0 + numothersock 115 125 360 360 0 + dcachesize 0 0 9223372036854775807 9223372036854775807 0 + numfile 1655 1958 9312 9312 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + numiptent 20 20 128 128 0 + 10: kmemsize 12345678 30456789 34567890 40000000 0 + lockedpages 0 5859 32768 65536 0 + privvmpages 1234567 2345678 3276800 9223372036854775807 0 + shmpages 688 1344 9223372036854775807 9223372036854775807 0 + dummy 0 0 0 0 0 + numproc 75 88 8000 8000 0 + physpages 199301 239798 0 9223372036854775807 0 + vmguarpages 0 0 4194304 9223372036854775807 0 + oomguarpages 199301 239798 4186112 9223372036854775807 0 + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 + numflock 4 14 200 220 0 + numpty 1 2 64 64 0 + numsiginfo 0 9 512 512 0 + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 + dgramrcvbuf 0 58680 262144 262144 0 + numothersock 115 125 360 360 0 + dcachesize 0 0 9223372036854775807 9223372036854775807 0 + numfile 1655 1958 9312 9312 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + numiptent 20 20 128 128 0 + 50: kmemsize 12345678 31456789 34567890 40000000 0 + lockedpages 0 5859 32768 65536 0 + privvmpages 1234567 2345678 3276800 9223372036854775807 0 + shmpages 688 1344 9223372036854775807 9223372036854775807 0 + dummy 0 0 0 0 0 + numproc 75 88 8000 8000 0 + physpages 199301 239798 0 9223372036854775807 0 + vmguarpages 0 0 4194304 9223372036854775807 0 + oomguarpages 199301 239798 4186112 9223372036854775807 0 + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 + numflock 4 14 200 220 0 + numpty 1 2 64 64 0 + numsiginfo 0 9 512 512 0 + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 + dgramrcvbuf 0 58680 262144 262144 0 + numothersock 115 125 360 360 0 + dcachesize 0 0 9223372036854775807 9223372036854775807 0 + numfile 1655 1958 9312 9312 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + numiptent 20 20 128 128 0 + 60: kmemsize 12345678 40000001 34567890 40000000 0 + lockedpages 0 5859 32768 65536 0 + privvmpages 1234567 2345678 3276800 9223372036854775807 0 + shmpages 688 1344 9223372036854775807 9223372036854775807 0 + dummy 0 0 0 0 0 + numproc 75 88 8000 8000 0 + physpages 199301 239798 0 9223372036854775807 0 + vmguarpages 0 0 4194304 9223372036854775807 0 + oomguarpages 199301 239798 4186112 9223372036854775807 0 + numtcpsock 16 21 9223372036854775807 9223372036854775807 0 + numflock 4 14 200 220 0 + numpty 1 2 64 64 0 + numsiginfo 0 9 512 512 0 + tcpsndbuf 278016 547280 2703360 9223372036854775807 0 + tcprcvbuf 262144 769968 2703360 9223372036854775807 0 + othersockbuf 158304 197424 9223372036854775807 9223372036854775807 0 + dgramrcvbuf 0 58680 262144 262144 0 + numothersock 115 125 360 360 0 + dcachesize 0 0 9223372036854775807 9223372036854775807 0 + numfile 1655 1958 9312 9312 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + dummy 0 0 0 0 0 + numiptent 20 20 128 128 0 + 0: kmemsize 15408521 33415719 9223372036854775807 9223372036854775807 0 + lockedpages 8789 12353 9223372036854775807 9223372036854775807 0 + privvmpages 41072 104930 9223372036854775807 9223372036854775807 0 + shmpages 730 1706 9223372036854775807 9223372036854775807 0 + dummy 0 0 9223372036854775807 9223372036854775807 0 + numproc 291 472 9223372036854775807 9223372036854775807 0 + physpages 9432 54303 9223372036854775807 9223372036854775807 0 + vmguarpages 0 0 9223372036854775807 9223372036854775807 0 + oomguarpages 9432 54303 9223372036854775807 9223372036854775807 0 + numtcpsock 13 21 9223372036854775807 9223372036854775807 0 + numflock 5 22 9223372036854775807 9223372036854775807 0 + numpty 1 1 9223372036854775807 9223372036854775807 0 + numsiginfo 0 6 9223372036854775807 9223372036854775807 0 + tcpsndbuf 773048 924800 9223372036854775807 9223372036854775807 0 + tcprcvbuf 212992 854408 9223372036854775807 9223372036854775807 0 + othersockbuf 204864 283784 9223372036854775807 9223372036854775807 0 + dgramrcvbuf 0 16944 9223372036854775807 9223372036854775807 0 + numothersock 178 225 9223372036854775807 9223372036854775807 0 + dcachesize 0 0 9223372036854775807 9223372036854775807 0 + numfile 5141 6779 9223372036854775807 9223372036854775807 0 + dummy 0 0 9223372036854775807 9223372036854775807 0 + dummy 0 0 9223372036854775807 9223372036854775807 0 + dummy 0 0 9223372036854775807 9223372036854775807 0 + numiptent 51 51 9223372036854775807 9223372036854775807 0