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