Skip to content

Commit

Permalink
Refactor Plugins check_threshold and check_range
Browse files Browse the repository at this point in the history
They have now been moved into their own module, called
classic_threshold_syntax

This is in style with 'new_threshold_syntax' being in a
seperate module. and this gives us guideline on where to
put 'relational expressions' a.k.a. NSClient style thresholds.

As before, backwards compatibility is maintained via explicit assignments
in Plugins/__init__.py but later we might add a deprecationwarning here.
  • Loading branch information
Pall Sigurdsson committed Dec 7, 2014
1 parent f8cad15 commit 76387b5
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 139 deletions.
143 changes: 7 additions & 136 deletions pynag/Plugins/__init__.py
Expand Up @@ -29,13 +29,13 @@
import os
import traceback
import signal
from platform import node
from optparse import OptionParser, OptionGroup
from pynag.Utils import PerfData, reconsile_threshold, runCommand
from pynag.Utils import PerfData, reconsile_threshold
from pynag.Parsers import ExtraOptsParser
import pynag.Utils
import pynag.errors
from . import new_threshold_syntax
from . import classic_threshold_syntax

# Map the return codes
OK = 0
Expand Down Expand Up @@ -268,7 +268,7 @@ def check_range(self, value):
self._check_messages_and_exit()

def _range_checker(self, value, range_threshold):
""" deprecated. Use pynag.Plugins.check_range() """
""" deprecated. Use pynag.Plugins.class_threshold_syntax.check_range() """

This comment has been minimized.

Copy link
@pall-valmundsson

pall-valmundsson Dec 7, 2014

Contributor

typo, class => classic

return check_range(value=value, range_threshold=range_threshold)

def send_nsca(self, *args, **kwargs):
Expand Down Expand Up @@ -398,139 +398,6 @@ def __getitem__(self, key):
return None


def check_threshold(value, warning=None, critical=None):
""" Checks value against warning/critical and returns Nagios exit code.
Format of range_threshold is according to:
http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT
Returns (in order of appearance):
UNKNOWN int(3) -- On errors or bad input
CRITICAL int(2) -- if value is within critical threshold
WARNING int(1) -- If value is within warning threshold
OK int(0) -- If value is outside both tresholds
Arguments:
value -- value to check
warning -- warning range
critical -- critical range
# Example Usage:
>>> check_threshold(88, warning="0:90", critical="0:95")
0
>>> check_threshold(92, warning=":90", critical=":95")
1
>>> check_threshold(96, warning=":90", critical=":95")
2
"""
if critical and not check_range(value, critical):
return CRITICAL
elif warning and not check_range(value, warning):
return WARNING
else:
return OK


def check_range(value, range_threshold=None):
""" Returns True if value is within range_threshold.
Format of range_threshold is according to:
http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT
Arguments:
value -- Numerical value to check (i.e. 70 )
range -- Range to compare against (i.e. 0:90 )
Returns:
True -- If value is inside the range
False -- If value is outside the range (alert if this happens)
False -- if invalid value is specified
Summary from plugin developer guidelines:
---------------------------------------------------------
x Generate an alert if x...
---------------------------------------------------------
10 < 0 or > 10, (outside the range of {0 .. 10})
10: < 10, (outside {10 .. ∞})
~:10 > 10, (outside the range of {-∞ .. 10})
10:20 < 10 or > 20, (outside the range of {10 .. 20})
@10:20 ≥ 10 and ≤ 20, (inside the range of {10 .. 20})
10 < 0 or > 10, (outside the range of {0 .. 10})
---------------------------------------------------------
# Example runs for doctest, False should mean alert
>>> check_range(78, "90") # Example disk is 78% full, threshold is 90
True
>>> check_range(5, 10) # Everything between 0 and 10 is True
True
>>> check_range(0, 10) # Everything between 0 and 10 is True
True
>>> check_range(10, 10) # Everything between 0 and 10 is True
True
>>> check_range(11, 10) # Everything between 0 and 10 is True
False
>>> check_range(-1, 10) # Everything between 0 and 10 is True
False
>>> check_range(-1, "~:10") # Everything Below 10
True
>>> check_range(11, "10:") # Everything above 10 is True
True
>>> check_range(1, "10:") # Everything above 10 is True
False
>>> check_range(0, "5:10") # Everything between 5 and 10 is True
False
>>> check_range(0, "@5:10") # Everything outside 5:10 is True
True
>>> check_range(None) # Return False if value is not a number
False
>>> check_range("10000000 PX") # What happens on invalid input
False
>>> check_range("10000000", "invalid:invalid") # What happens on invalid range
Traceback (most recent call last):
...
PluginError: Invalid threshold format: invalid:invalid
"""

# Return false if value is not a number
try:
float(value)
except Exception:
return False

# if no range_threshold is provided, assume everything is ok
if not range_threshold:
range_threshold = '~:'
range_threshold = str(range_threshold)
# If range starts with @, then we do the opposite
if range_threshold[0] == '@':
return not check_range(value, range_threshold[1:])

if range_threshold.find(':') > -1:
(start, end) = (range_threshold.split(':', 1))
# we get here if ":" was not provided in range_threshold
else:
start = ''
end = range_threshold
# assume infinity if start is not provided
if start == '~':
start = None
# assume start=0 if start is not provided
if start == '':
start = 0
# assume infinity if end is not provided
if end == '':
end = None

try:
# start is defined and value is lower than start
if start is not None and float(value) < float(start):
return False
if end is not None and float(value) > float(end):
return False
except ValueError:
raise PluginError("Invalid threshold format: %s" % range_threshold)
return True


class PluginHelper(object):

""" PluginHelper takes away some of the tedious work of writing Nagios plugins. Primary features include:
Expand Down Expand Up @@ -1206,3 +1073,7 @@ def __str__(self):

def __repr__(self):
return self.get_plugin_output(long_output='', perfdata='')

# For backwards compatibility:
check_range = classic_threshold_syntax.check_range
check_threshold = classic_threshold_syntax.check_threshold
157 changes: 157 additions & 0 deletions pynag/Plugins/classic_threshold_syntax.py
@@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
"""Support for classic nagios threshold syntax.
Nagios plugins development team and Monitoring Plugin Development team both
define what the syntax of a threshold should be, and it can be looked up here:
* http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT
* https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT
"""
import pynag.errors

# Map the return codes
OK = 0
WARNING = 1
CRITICAL = 2
UNKNOWN = 3


class Error(pynag.errors.PynagError):
"""Base class for errors in this module."""


class InvalidThreshold(Error):
"""Raised when an invalid threshold was provided."""


def check_threshold(value, warning=None, critical=None):
""" Checks value against warning/critical and returns Nagios exit code.
Format of range_threshold is according to:
http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT
Returns (in order of appearance):
UNKNOWN int(3) -- On errors or bad input
CRITICAL int(2) -- if value is within critical threshold
WARNING int(1) -- If value is within warning threshold
OK int(0) -- If value is outside both tresholds
Arguments:
value -- value to check
warning -- warning range
critical -- critical range
# Example Usage:
>>> check_threshold(88, warning="0:90", critical="0:95")
0
>>> check_threshold(92, warning=":90", critical=":95")
1
>>> check_threshold(96, warning=":90", critical=":95")
2
"""
if critical and not check_range(value, critical):
return CRITICAL
elif warning and not check_range(value, warning):
return WARNING
else:
return OK


def check_range(value, range_threshold=None):
""" Returns True if value is within range_threshold.
Format of range_threshold is according to:
http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT
Arguments:
value -- Numerical value to check (i.e. 70 )
range -- Range to compare against (i.e. 0:90 )
Returns:
True -- If value is inside the range
False -- If value is outside the range (alert if this happens)
False -- if invalid value is specified
Summary from plugin developer guidelines:
---------------------------------------------------------
x Generate an alert if x...
---------------------------------------------------------
10 < 0 or > 10, (outside the range of {0 .. 10})
10: < 10, (outside {10 .. ∞})
~:10 > 10, (outside the range of {-∞ .. 10})
10:20 < 10 or > 20, (outside the range of {10 .. 20})
@10:20 ≥ 10 and ≤ 20, (inside the range of {10 .. 20})
10 < 0 or > 10, (outside the range of {0 .. 10})
---------------------------------------------------------
# Example runs for doctest, False should mean alert
>>> check_range(78, "90") # Example disk is 78% full, threshold is 90
True
>>> check_range(5, 10) # Everything between 0 and 10 is True
True
>>> check_range(0, 10) # Everything between 0 and 10 is True
True
>>> check_range(10, 10) # Everything between 0 and 10 is True
True
>>> check_range(11, 10) # Everything between 0 and 10 is True
False
>>> check_range(-1, 10) # Everything between 0 and 10 is True
False
>>> check_range(-1, "~:10") # Everything Below 10
True
>>> check_range(11, "10:") # Everything above 10 is True
True
>>> check_range(1, "10:") # Everything above 10 is True
False
>>> check_range(0, "5:10") # Everything between 5 and 10 is True
False
>>> check_range(0, "@5:10") # Everything outside 5:10 is True
True
>>> check_range(None) # Return False if value is not a number
False
>>> check_range("10000000 PX") # What happens on invalid input
False
>>> check_range("10000000", "invalid:invalid") # What happens on invalid range
Traceback (most recent call last):
...
PynagError: Invalid threshold format: invalid:invalid
"""

# Return false if value is not a number
try:
float(value)
except Exception:
return False

# if no range_threshold is provided, assume everything is ok
if not range_threshold:
range_threshold = '~:'
range_threshold = str(range_threshold)
# If range starts with @, then we do the opposite
if range_threshold[0] == '@':
return not check_range(value, range_threshold[1:])

if range_threshold.find(':') > -1:
(start, end) = (range_threshold.split(':', 1))
# we get here if ":" was not provided in range_threshold
else:
start = ''
end = range_threshold
# assume infinity if start is not provided
if start == '~':
start = None
# assume start=0 if start is not provided
if start == '':
start = 0
# assume infinity if end is not provided
if end == '':
end = None

try:
# start is defined and value is lower than start
if start is not None and float(value) < float(start):
return False
if end is not None and float(value) > float(end):
return False
except ValueError:
raise InvalidThreshold("Invalid threshold format: %s" % range_threshold)
return True
2 changes: 1 addition & 1 deletion pynag/Plugins/new_threshold_syntax.py
Expand Up @@ -121,7 +121,7 @@ def check_range(value, range):
# If range does not contain ".." then we assume its the older style of
# ranges (either a plain number or the start:end syntax)
if '..' not in range:
return not pynag.Plugins.check_range(value=value, range_threshold=range)
return not pynag.Plugins.classic_threshold_syntax.check_range(value=value, range_threshold=range)
# If range starts with ^ we the conditions are inverted
if range[0] == '^':
return not check_range(value, range[1:])
Expand Down
5 changes: 3 additions & 2 deletions pynag/Utils/metrics.py
Expand Up @@ -2,9 +2,10 @@
""" Classes and functions related to Perfdata metrics."""
import shlex
import re
import pynag.Plugins
from pynag import errors
from pynag.Plugins import new_threshold_syntax
from pynag.Plugins import classic_threshold_syntax


class PerfDataMetric(object):

Expand Down Expand Up @@ -141,7 +142,7 @@ def get_status(self):
"""

try:
status = pynag.Plugins.check_threshold(self.value, warning=self.warn, critical=self.crit)
status = classic_threshold_syntax.check_threshold(self.value, warning=self.warn, critical=self.crit)
except errors.PynagError:
status = 3

Expand Down

0 comments on commit 76387b5

Please sign in to comment.