23 Commits
v1.0 ... v1.3

Author SHA1 Message Date
drewkerrigan
fb4e58b635 Adding support for -E, -Q, -w, -c, fixing threshold checking on -m, added UnitTest task, removed -l, -g 2015-11-18 23:07:50 -05:00
drewkerrigan
32a8884881 added ability to supply an alias for a key re: #10 2015-11-15 23:50:12 -05:00
drewkerrigan
2644151b5f adding header for nagios configuration 2015-10-05 12:40:52 -04:00
drewkerrigan
55b979f3e2 Merge branch 'master' of github.com:drewkerrigan/nagios-http-json 2015-10-05 10:36:46 -04:00
drewkerrigan
369d5115a3 extra debugging 2015-10-05 10:36:40 -04:00
Drew Kerrigan
ea7edf5d01 Merge pull request #17 from billmoritz/metrics-fix
Return metrics no matter the result
2015-08-31 12:55:17 -04:00
Bill Moritz
a4be4d42c6 Return metrics no matter the result 2015-08-31 11:36:27 -04:00
Drew Kerrigan
cb0a5927c2 Merge pull request #15 from billmoritz/http-post
Http post
2015-08-22 18:22:07 -04:00
Bill Moritz
42e75abcad Update README.md 2015-08-22 09:47:25 -04:00
Bill Moritz
3058176ba1 Add data argument
Add an option to HTTP POST data to the host.
2015-08-22 09:42:06 -04:00
Drew Kerrigan
e4334d0c4a Merge pull request #13 from nejec/master
Allow multiple key value specification
2015-07-29 11:45:04 -07:00
Jernej Porenta
b9d03c899f Allow multiple key value specification
Multiple key values can be specified by using colon delimiter.
2015-07-28 09:08:55 +02:00
Drew Kerrigan
15c5075cc1 Merge pull request #12 from MrOppermann/public-readme.me-and-script-help-are-different
synchronized readme.me and help of plugin regarding usage description
2015-07-13 17:22:42 -04:00
frederic.oppermann
1be1b2e5a2 synchronized readme.me and help of plugin regarding usage description 2015-07-13 17:05:27 +02:00
Drew Kerrigan
1772543ee3 Merge pull request #9 from invertigo/master
add arguments for http timeout and tcp port
2015-05-07 19:48:51 -04:00
root
fe2e830bf7 add arguments for http timeout and tcp port 2015-05-07 22:19:23 +00:00
drewkerrigan
dd439998db added ability to reference array elements when the root element is an array 2015-05-06 12:19:18 -04:00
drewkerrigan
82f4eaa48a added ability to reference array elements when the root element is an array 2015-05-06 12:17:00 -04:00
Drew Kerrigan
9aa7ed57aa Merge pull request #7 from kovacshuni/arrays
Changing form [] to () because of Nagios issues. Fixes.
2015-05-05 10:30:10 -04:00
Hunor Kovács
4fa2c3b39a Updating readme with arrays. 2015-05-05 14:58:24 +03:00
Hunor Kovács
aa90ecad65 Changed from square brackets to normal paranthesis for arrays. Nagios doesn't like square brackets.
(http://sourceforge.net/p/nagios/nrpe/ci/master/tree/SECURITY illegal metachars section)
Fixed case when no dot after array (e.g. checks(0) wasn't working)
Extract paranthesis characters in attribute.
2015-05-05 14:32:16 +03:00
Drew Kerrigan
c678dfd518 Merge pull request #6 from kovacshuni/arrays
Arrays are supported as well. format: `alpha.beta[2].gamma.theta[0]`
2015-05-04 14:00:02 -04:00
Hunor Kovács
3b048f66c5 Arrays are supported as well. format: alpha.beta[2].gamma.theta[0]
Fix: Now keys that are in the middle of the expression that are not found in the data, can be checked upon, without the script throwing an error.
2015-05-04 20:45:46 +03:00
2 changed files with 386 additions and 142 deletions

110
README.md
View File

@@ -9,6 +9,8 @@ This is a generic plugin for Nagios which checks json values from a given HTTP e
* Nagios * Nagios
* Python * Python
### Nagios Configuration
Assuming a standard installation of Nagios, the plugin can be executed from the machine that Nagios is running on. Assuming a standard installation of Nagios, the plugin can be executed from the machine that Nagios is running on.
```bash ```bash
@@ -35,7 +37,7 @@ Add the following command definition to your commands config (`commands.config`)
define command{ define command{
command_name <command_name> command_name <command_name>
command_line /usr/bin/python /usr/local/nagios/libexec/plugins/check_http_json.py -H <host>:<port> -p <path> [-e|-q|-l|-g <rules>] [-m <metrics>] command_line /usr/bin/python /usr/local/nagios/libexec/plugins/check_http_json.py -H <host>:<port> -p <path> [-e|-q|-w|-c <rules>] [-m <metrics>]
} }
``` ```
@@ -47,13 +49,15 @@ More info about options in Usage.
Executing `./check_http_json.py -h` will yield the following details: Executing `./check_http_json.py -h` will yield the following details:
``` ```
usage: check_http_json.py [-h] -H HOST [-B AUTH] [-p PATH] usage: check_http_json.py [-h] [-d] [-s] -H HOST [-P PORT] [-p PATH]
[-t TIMEOUT] [-B AUTH] [-D DATA] [-f SEPARATOR]
[-w [KEY_THRESHOLD_WARNING [KEY_THRESHOLD_WARNING ...]]]
[-c [KEY_THRESHOLD_CRITICAL [KEY_THRESHOLD_CRITICAL ...]]]
[-e [KEY_LIST [KEY_LIST ...]]] [-e [KEY_LIST [KEY_LIST ...]]]
[-E [KEY_LIST_CRITICAL [KEY_LIST_CRITICAL ...]]]
[-q [KEY_VALUE_LIST [KEY_VALUE_LIST ...]]] [-q [KEY_VALUE_LIST [KEY_VALUE_LIST ...]]]
[-l [KEY_LTE_LIST [KEY_LTE_LIST ...]]] [-Q [KEY_VALUE_LIST_CRITICAL [KEY_VALUE_LIST_CRITICAL ...]]]
[-g [KEY_GTE_LIST [KEY_GTE_LIST ...]]] [-m [METRIC_LIST [METRIC_LIST ...]]]
[-m [METRIC_LIST [METRIC_LIST ...]]] [-s]
[-f SEPARATOR] [-d]
Nagios plugin which checks json values from a given endpoint against argument Nagios plugin which checks json values from a given endpoint against argument
specified rules and determines the status and performance data for that specified rules and determines the status and performance data for that
@@ -61,36 +65,70 @@ service
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
-d, --debug Debug mode.
-s, --ssl HTTPS mode.
-H HOST, --host HOST Host. -H HOST, --host HOST Host.
-P PORT, --port PORT TCP port
-p PATH, --path PATH Path.
-t TIMEOUT, --timeout TIMEOUT
Connection timeout (seconds)
-B AUTH, --basic-auth AUTH -B AUTH, --basic-auth AUTH
Basic auth string "username:password" Basic auth string "username:password"
-p PATH, --path PATH Path. -D DATA, --data DATA The http payload to send as a POST
-f SEPARATOR, --field_separator SEPARATOR
Json Field separator, defaults to "." ; Select element
in an array with "(" ")"
-w [KEY_THRESHOLD_WARNING [KEY_THRESHOLD_WARNING ...]], --warning [KEY_THRESHOLD_WARNING [KEY_THRESHOLD_WARNING ...]]
Warning threshold for these values
(key1[>alias],WarnRange key2[>alias],WarnRange).
WarnRange is in the format [@]start:end, more
information at nagios-plugins.org/doc/guidelines.html.
-c [KEY_THRESHOLD_CRITICAL [KEY_THRESHOLD_CRITICAL ...]], --critical [KEY_THRESHOLD_CRITICAL [KEY_THRESHOLD_CRITICAL ...]]
Critical threshold for these values
(key1[>alias],CriticalRange
key2[>alias],CriticalRange. CriticalRange is in the
format [@]start:end, more information at nagios-
plugins.org/doc/guidelines.html.
-e [KEY_LIST [KEY_LIST ...]], --key_exists [KEY_LIST [KEY_LIST ...]] -e [KEY_LIST [KEY_LIST ...]], --key_exists [KEY_LIST [KEY_LIST ...]]
Checks existence of these keys to determine status. Checks existence of these keys to determine status.
Return warning if key is not present.
-E [KEY_LIST_CRITICAL [KEY_LIST_CRITICAL ...]], --key_exists_critical [KEY_LIST_CRITICAL [KEY_LIST_CRITICAL ...]]
Same as -e but return critical if key is not present.
-q [KEY_VALUE_LIST [KEY_VALUE_LIST ...]], --key_equals [KEY_VALUE_LIST [KEY_VALUE_LIST ...]] -q [KEY_VALUE_LIST [KEY_VALUE_LIST ...]], --key_equals [KEY_VALUE_LIST [KEY_VALUE_LIST ...]]
Checks equality of these keys and values (key,value Checks equality of these keys and values
key2,value2) to determine status. (key[>alias],value key2,value2) to determine status.
-l [KEY_LTE_LIST [KEY_LTE_LIST ...]], --key_lte [KEY_LTE_LIST [KEY_LTE_LIST ...]] Multiple key values can be delimited with colon
Checks that these keys and values (key,value (key,value1:value2). Return warning if equality check
key2,value2) are less than or equal to the returned fails
json value to determine status. -Q [KEY_VALUE_LIST_CRITICAL [KEY_VALUE_LIST_CRITICAL ...]], --key_equals_critical [KEY_VALUE_LIST_CRITICAL [KEY_VALUE_LIST_CRITICAL ...]]
-g [KEY_GTE_LIST [KEY_GTE_LIST ...]], --key_gte [KEY_GTE_LIST [KEY_GTE_LIST ...]] Same as -q but return critical if equality check
Checks that these keys and values (key,value fails.
key2,value2) are greater than or equal to the returned
json value to determine status.
-m [METRIC_LIST [METRIC_LIST ...]], --key_metric [METRIC_LIST [METRIC_LIST ...]] -m [METRIC_LIST [METRIC_LIST ...]], --key_metric [METRIC_LIST [METRIC_LIST ...]]
Gathers the values of these keys Gathers the values of these keys (key[>alias],UnitOfMe
(key,UnitOfMeasure,Min,Max,WarnRange,CriticalRange) asure,WarnRange,CriticalRange,Min,Max) for Nagios
for Nagios performance data. More information about performance data. More information about Range format
Range format and units of measure for nagios can be and units of measure for nagios can be found at
found at https://nagios- nagios-plugins.org/doc/guidelines.html Additional
plugins.org/doc/guidelines.html Additional formats for formats for this parameter are: (key[>alias]),
this parameter are: (key), (key,UnitOfMeasure), (key[>alias],UnitOfMeasure),
(key,UnitOfMeasure,Min,Max). (key[>alias],UnitOfMeasure,WarnRange,CriticalRange).
-s, --ssl HTTPS mode. ```
-f SEPARATOR, --field_separator SEPARATOR
Json Field separator, defaults to "." Access a specific JSON field by following this syntax: `alpha.beta.gamma(3).theta.omega(0)`
-d, --debug Debug mode. Dots are field separators (changeable), parentheses are for entering arrays.
If the root of the JSON data is itself an array like the following:
```
[
{ "gauges": { "jvm.buffers.direct.capacity": {"value": 215415}}}
]
```
The beginning of the key should start with ($index) as in this example:
```
./check_http_json.py -H localhost:8081 -p metrics --key_exists "(0)_gauges_jvm.buffers.direct.capacity_value" -f _
``` ```
More info about Nagios Range format and Units of Measure can be found at [https://nagios-plugins.org/doc/guidelines.html](https://nagios-plugins.org/doc/guidelines.html). More info about Nagios Range format and Units of Measure can be found at [https://nagios-plugins.org/doc/guidelines.html](https://nagios-plugins.org/doc/guidelines.html).
@@ -128,14 +166,12 @@ Let's say we want to use `check_http_json.py` to read from Docker's `/info` HTTP
`localhost.cfg` `localhost.cfg`
``` ```
define service { define service {
use local-service use local-service
host_name localhost host_name localhost
service_description Docker info status checker service_description Docker info status checker
check_command check_docker check_command check_docker
} }
``` ```
#### Command Definition with Arguments #### Command Definition with Arguments
@@ -143,18 +179,16 @@ define service {
`commands.cfg` `commands.cfg`
``` ```
define command{ define command{
command_name check_docker command_name check_docker
command_line /usr/bin/python /usr/local/nagios/libexec/plugins/check_http_json.py -H 127.0.0.1:4243 -p info -e Containers -q IPv4Forwarding,1 -l Debug,2 -g Images,1 -m Containers,,0,1000 Images NEventsListener NFd NGoroutines SwapLimit command_line /usr/bin/python /usr/local/nagios/libexec/plugins/check_http_json.py -H 127.0.0.1:4243 -p info -e Containers -q IPv4Forwarding,1 -w Debug,2:2 -c Images,1:1 -m Containers,0:250,0:500,0,1000 Images NEventsListener NFd NGoroutines SwapLimit
} }
``` ```
#### Sample Output #### Sample Output
``` ```
OK: Status OK.|'Containers'=1;0;1000 'Images'=11;0;0 'NEventsListener'=3;0;0 'NFd'=10;0;0 'NGoroutines'=14;0;0 'SwapLimit'=1;0;0 OK: Status OK.|'Containers'=1;0;1000 'Images'=11;0;0 'NEventsListener'=3;0;0 'NFd'=10;0;0 'NGoroutines'=14;0;0 'SwapLimit'=1;0;0
``` ```
### Docker Container Monitor Example Plugin ### Docker Container Monitor Example Plugin
@@ -176,14 +210,12 @@ OK: Status OK.|'Containers'=1;0;1000 'Images'=11;0;0 'NEventsListener'=3;0;0 'NF
`localhost.cfg` `localhost.cfg`
``` ```
define service { define service {
use local-service use local-service
host_name localhost host_name localhost
service_description Docker container liveness check service_description Docker container liveness check
check_command check_my_container check_command check_my_container
} }
``` ```
#### Command Definition with Arguments #### Command Definition with Arguments
@@ -191,12 +223,10 @@ define service {
`commands.cfg` `commands.cfg`
``` ```
define command{ define command{
command_name check_my_container command_name check_my_container
command_line /usr/bin/python /usr/local/nagios/libexec/plugins/check_http_json.py -H 127.0.0.1:4243 -p /containers/2356e8ccb3de8308ccb16cf8f5d157bc85ded5c3d8327b0dfb11818222b6f615/json -q ID,2356e8ccb3de8308ccb16cf8f5d157bc85ded5c3d8327b0dfb11818222b6f615 State.Running,True command_line /usr/bin/python /usr/local/nagios/libexec/plugins/check_http_json.py -H 127.0.0.1:4243 -p /containers/2356e8ccb3de8308ccb16cf8f5d157bc85ded5c3d8327b0dfb11818222b6f615/json -q ID,2356e8ccb3de8308ccb16cf8f5d157bc85ded5c3d8327b0dfb11818222b6f615 State.Running,True
} }
``` ```
#### Sample Output #### Sample Output
@@ -247,4 +277,4 @@ In this example I've chosen `_` to separate `guages` from `jvm` and `capacity` f
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.

View File

@@ -10,45 +10,97 @@ and determines the status and performance data for that service.
import httplib, urllib, urllib2, base64 import httplib, urllib, urllib2, base64
import json import json
import argparse import argparse
import sys
from pprint import pprint from pprint import pprint
from urllib2 import HTTPError from urllib2 import HTTPError
from urllib2 import URLError from urllib2 import URLError
# TEST = False
OK_CODE = 0
WARNING_CODE = 1
CRITICAL_CODE = 2
UNKNOWN_CODE = 3
class NagiosHelper: class NagiosHelper:
"""Help with Nagios specific status string formatting.""" """Help with Nagios specific status string formatting."""
code = 0 message_prefixes = {OK_CODE: 'OK', WARNING_CODE: 'WARNING', CRITICAL_CODE: 'CRITICAL', UNKNOWN_CODE: 'UNKNOWN'}
message_prefixes = {0: 'OK', 1: 'WARNING', 2: 'CRITICAL', 3: 'UNKNOWN'}
message_text = ''
performance_data = '' performance_data = ''
warning_message = ''
critical_message = ''
unknown_message = ''
def getMessage(self): def getMessage(self):
"""Build a status-prefixed message with optional performance data generated externally""" """Build a status-prefixed message with optional performance data generated externally"""
text = "%s" % self.message_prefixes[self.code] text = "%s: Status %s." % (self.message_prefixes[self.getCode()], self.message_prefixes[self.getCode()])
if self.message_text: text += self.warning_message
text += ": %s" % self.message_text text += self.critical_message
text += self.unknown_message
if self.performance_data: if self.performance_data:
text += "|%s" % self.performance_data text += "|%s" % self.performance_data
return text return text
def setCodeAndMessage(self, code, text): def getCode(self):
self.code = code code = OK_CODE
self.message_text = text if (self.warning_message != ''):
code = WARNING_CODE
if (self.critical_message != ''):
code = CRITICAL_CODE
if (self.unknown_message != ''):
code = UNKNOWN_CODE
return code
def ok(self, text): self.setCodeAndMessage(0, text) def append_warning(self, warning_message):
def warning(self, text): self.setCodeAndMessage(1, text) self.warning_message += warning_message
def critical(self, text): self.setCodeAndMessage(2, text) def append_critical(self, critical_message):
def unknown(self, text): self.setCodeAndMessage(3, text) self.critical_message += critical_message
def append_unknown(self, unknown_message):
self.critical_message += critical_message
def append_metrics(self, (performance_data, warning_message, critical_message)):
self.performance_data += performance_data
self.append_warning(warning_message)
self.append_critical(critical_message)
class JsonHelper: class JsonHelper:
"""Perform simple comparison operations against values in a given JSON dict""" """Perform simple comparison operations against values in a given JSON dict"""
def __init__(self, json_data, separator): def __init__(self, json_data, separator):
self.data = json_data self.data = json_data
self.separator = separator self.separator = separator
self.arrayOpener = '('
self.arrayCloser = ')'
def equals(self, key, value): return self.exists(key) and str(self.get(key)) == value def getSubElement(self, key, data):
separatorIndex = key.find(self.separator)
partialKey = key[:separatorIndex]
remainingKey = key[separatorIndex + 1:]
if partialKey in data:
return self.get(remainingKey, data[partialKey])
else:
return (None, 'not_found')
def getSubArrayElement(self, key, data):
subElemKey = key[:key.find(self.arrayOpener)]
index = int(key[key.find(self.arrayOpener) + 1:key.find(self.arrayCloser)])
remainingKey = key[key.find(self.arrayCloser + self.separator) + 2:]
if key.find(self.arrayCloser + self.separator) == -1:
remainingKey = key[key.find(self.arrayCloser) + 1:]
if subElemKey in data:
if index < len(data[subElemKey]):
return self.get(remainingKey, data[subElemKey][index])
else:
return (None, 'not_found')
else:
if not subElemKey:
return self.get(remainingKey, data[index])
else:
return (None, 'not_found')
def equals(self, key, value): return self.exists(key) and str(self.get(key)) in value.split(':')
def lte(self, key, value): return self.exists(key) and float(self.get(key)) <= float(value) def lte(self, key, value): return self.exists(key) and float(self.get(key)) <= float(value)
def lt(self, key, value): return self.exists(key) and float(self.get(key)) < float(value)
def gte(self, key, value): return self.exists(key) and float(self.get(key)) >= float(value) def gte(self, key, value): return self.exists(key) and float(self.get(key)) >= float(value)
def gt(self, key, value): return self.exists(key) and float(self.get(key)) > float(value)
def exists(self, key): return (self.get(key) != (None, 'not_found')) def exists(self, key): return (self.get(key) != (None, 'not_found'))
def get(self, key, temp_data=''): def get(self, key, temp_data=''):
"""Can navigate nested json keys with a dot format (Element.Key.NestedKey). Returns (None, 'not_found') if not found""" """Can navigate nested json keys with a dot format (Element.Key.NestedKey). Returns (None, 'not_found') if not found"""
@@ -56,14 +108,33 @@ class JsonHelper:
data = temp_data data = temp_data
else: else:
data = self.data data = self.data
if len(key) <= 0:
if self.separator in key: return data
return self.get(key[key.find(self.separator) + 1:], data[key[:key.find(self.separator)]]) if key.find(self.separator) != -1 and key.find(self.arrayOpener) != -1 :
else: if key.find(self.separator) < key.find(self.arrayOpener) :
if key in data: return self.getSubElement(key, data)
return data[key]
else: else:
return (None, 'not_found') return self.getSubArrayElement(key, data)
else:
if key.find(self.separator) != -1 :
return self.getSubElement(key, data)
else:
if key.find(self.arrayOpener) != -1 :
return self.getSubArrayElement(key, data)
else:
if key in data:
return data[key]
else:
return (None, 'not_found')
def _getKeyAlias(original_key):
key = original_key
alias = original_key
if '>' in original_key:
keys = original_key.split('>')
if len(keys) == 2:
key, alias = keys
return key, alias
class JsonRuleProcessor: class JsonRuleProcessor:
"""Perform checks and gather values from a JSON dict given rules and metrics definitions""" """Perform checks and gather values from a JSON dict given rules and metrics definitions"""
@@ -73,98 +144,156 @@ class JsonRuleProcessor:
separator = '.' separator = '.'
if self.rules.separator: separator = self.rules.separator if self.rules.separator: separator = self.rules.separator
self.helper = JsonHelper(self.data, separator) self.helper = JsonHelper(self.data, separator)
debugPrint(rules_args.debug, "rules:%s" % rules_args)
debugPrint(rules_args.debug, "separator:%s" % separator) debugPrint(rules_args.debug, "separator:%s" % separator)
def isAlive(self): def checkExists(self, exists_list):
"""Return a tuple with liveness and reason for not liveness given existence, equality, and comparison rules""" failure = ''
reason = '' for k in exists_list:
key, alias = _getKeyAlias(k)
if (self.helper.exists(key) == False):
failure += " Key %s did not exist." % alias
return failure
if self.rules.key_list != None: def checkEquality(self, equality_list):
for k in self.rules.key_list: failure = ''
if (self.helper.exists(k) == False): for kv in equality_list:
reason += " Key %s did not exist." % k k, v = kv.split(',')
key, alias = _getKeyAlias(k)
if (self.helper.equals(key, v) == False):
failure += " Value for key %s did not match %s." % (alias, v)
return failure
def checkThreshold(self, key, alias, r):
failure = ''
invert = False
start = 0
end = 'infinity'
if r.startswith('@'):
invert = True
r = r[1:]
vals = r.split(':')
if len(vals) == 1:
end = vals[0]
if len(vals) == 2:
start = vals[0]
if vals[1] != '':
end = vals[1]
if(start == '~'):
if (invert and self.helper.lte(key, end)):
failure += " Value for key %s was less than or equal to %s." % (alias, end)
elif (not invert and self.helper.gt(key, end)):
failure += " Value for key %s was greater than %s." % (alias, end)
elif(end == 'infinity'):
if (invert and self.helper.gte(key, start)):
failure += " Value for key %s was greater than or equal to %s." % (alias, start)
elif (not invert and self.helper.lt(key, start)):
failure += " Value for key %s was less than %s." % (alias, start)
else:
if (invert and self.helper.gte(key, start) and self.helper.lte(key, end)):
failure += " Value for key %s was inside the range %s:%s." % (alias, start, end)
elif (not invert and (self.helper.lt(key, start) or self.helper.gt(key, end))):
failure += " Value for key %s was outside the range %s:%s." % (alias, start, end)
return failure
def checkThresholds(self, threshold_list):
failure = ''
for threshold in threshold_list:
k, r = threshold.split(',')
key, alias = _getKeyAlias(k)
failure += self.checkThreshold(key, alias, r)
return failure
def checkWarning(self):
failure = ''
if self.rules.key_threshold_warning != None:
failure += self.checkThresholds(self.rules.key_threshold_warning)
if self.rules.key_value_list != None: if self.rules.key_value_list != None:
for kv in self.rules.key_value_list: failure += self.checkEquality(self.rules.key_value_list)
k, v = kv.split(',') if self.rules.key_list != None:
if (self.helper.equals(k, v) == False): failure += self.checkExists(self.rules.key_list)
reason += " Value %s for key %s did not match." % (v, k) return failure
if self.rules.key_lte_list != None: def checkCritical(self):
for kv in self.rules.key_lte_list: failure = ''
k, v = kv.split(',') if self.rules.key_threshold_critical != None:
if (self.helper.lte(k, v) == False): failure += self.checkThresholds(self.rules.key_threshold_critical)
reason += " Value %s was not less than or equal to value for key %s." % (v, k) if self.rules.key_value_list_critical != None:
failure += self.checkEquality(self.rules.key_value_list_critical)
if self.rules.key_list_critical != None:
failure += self.checkExists(self.rules.key_list_critical)
return failure
if self.rules.key_gte_list != None: def checkMetrics(self):
for kv in self.rules.key_gte_list:
k, v = kv.split(',')
if (self.helper.gte(k, v) == False):
reason += " Value %s was not greater than or equal to value for key %s." % (v, k)
is_alive = (reason == '')
return (is_alive, reason)
def getMetrics(self):
"""Return a Nagios specific performance metrics string given keys and parameter definitions""" """Return a Nagios specific performance metrics string given keys and parameter definitions"""
metrics = '' metrics = ''
warning = ''
critical = ''
if self.rules.metric_list != None: if self.rules.metric_list != None:
for metric in self.rules.metric_list: for metric in self.rules.metric_list:
key = metric key = metric
minimum = maximum = warn_range = crit_range = 0 minimum = maximum = warn_range = crit_range = None
uom = '' uom = ''
if ',' in metric: if ',' in metric:
vals = metric.split(',') vals = metric.split(',')
if len(vals) == 2: if len(vals) == 2:
key,uom = vals key,uom = vals
if len(vals) == 4: if len(vals) == 4:
key,uom,minimum,maximum = vals key,uom,warn_range,crit_range = vals
if len(vals) == 6: if len(vals) == 6:
key,uom,minimum,maximum,warn_range,crit_range = vals key,uom,warn_range,crit_range,minimum,maximum = vals
key, alias = _getKeyAlias(key)
if self.helper.exists(key): if self.helper.exists(key):
metrics += "'%s'=%s" % (key, self.helper.get(key)) metrics += "'%s'=%s" % (alias, self.helper.get(key))
if uom: metrics += uom if uom: metrics += uom
metrics += ";%s" % minimum if warn_range != None:
metrics += ";%s" % maximum warning += self.checkThreshold(key, alias, warn_range)
if warn_range: metrics += ";%s" % warn_range metrics += ";%s" % warn_range
if crit_range: metrics += ";%s" % crit_range if crit_range != None:
critical += self.checkThreshold(key, alias, crit_range)
metrics += ";%s" % crit_range
if minimum != None:
critical += self.checkThreshold(key, alias, minimum + ':')
metrics += ";%s" % minimum
if maximum != None:
critical += self.checkThreshold(key, alias, '~:' + maximum)
metrics += ";%s" % maximum
metrics += ' ' metrics += ' '
return ("%s" % metrics, warning, critical)
return "%s" % metrics
def parseArgs(): def parseArgs():
parser = argparse.ArgumentParser(description= parser = argparse.ArgumentParser(description=
'Nagios plugin which checks json values from a given endpoint against argument specified rules\ 'Nagios plugin which checks json values from a given endpoint against argument specified rules\
and determines the status and performance data for that service') and determines the status and performance data for that service')
parser.add_argument('-H', '--host', dest='host', required=True, help='Host.') # parser.add_argument('-v', '--verbose', action='store_true', help='Verbose Output')
parser.add_argument('-B', '--basic-auth', dest='auth', required=False, help='Basic auth string "username:password"')
parser.add_argument('-p', '--path', dest='path', help='Path.')
parser.add_argument('-e', '--key_exists', dest='key_list', nargs='*',
help='Checks existence of these keys to determine status.')
parser.add_argument('-q', '--key_equals', dest='key_value_list', nargs='*',
help='Checks equality of these keys and values (key,value key2,value2) to determine status.')
parser.add_argument('-l', '--key_lte', dest='key_lte_list', nargs='*',
help='Checks that these keys and values (key,value key2,value2) are less than or equal to\
the returned json value to determine status.')
parser.add_argument('-g', '--key_gte', dest='key_gte_list', nargs='*',
help='Checks that these keys and values (key,value key2,value2) are greater than or equal to\
the returned json value to determine status.')
parser.add_argument('-m', '--key_metric', dest='metric_list', nargs='*',
help='Gathers the values of these keys (key,UnitOfMeasure,Min,Max,WarnRange,CriticalRange) for Nagios performance data.\
More information about Range format and units of measure for nagios can be found at https://nagios-plugins.org/doc/guidelines.html\
Additional formats for this parameter are: (key), (key,UnitOfMeasure), (key,UnitOfMeasure,Min,Max).')
parser.add_argument('-s', '--ssl', action='store_true', help='HTTPS mode.')
parser.add_argument('-f', '--field_separator', dest='separator', help='Json Field separator, defaults to "."')
parser.add_argument('-d', '--debug', action='store_true', help='Debug mode.') parser.add_argument('-d', '--debug', action='store_true', help='Debug mode.')
parser.add_argument('-s', '--ssl', action='store_true', help='HTTPS mode.')
parser.add_argument('-H', '--host', dest='host', required=True, help='Host.')
parser.add_argument('-P', '--port', dest='port', help='TCP port')
parser.add_argument('-p', '--path', dest='path', help='Path.')
parser.add_argument('-t', '--timeout', type=int, help='Connection timeout (seconds)')
parser.add_argument('-B', '--basic-auth', dest='auth', help='Basic auth string "username:password"')
parser.add_argument('-D', '--data', dest='data', help='The http payload to send as a POST')
parser.add_argument('-f', '--field_separator', dest='separator',
help='Json Field separator, defaults to "." ; Select element in an array with "(" ")"')
parser.add_argument('-w', '--warning', dest='key_threshold_warning', nargs='*',
help='Warning threshold for these values (key1[>alias],WarnRange key2[>alias],WarnRange). WarnRange is in the format [@]start:end, more information at nagios-plugins.org/doc/guidelines.html.')
parser.add_argument('-c', '--critical', dest='key_threshold_critical', nargs='*',
help='Critical threshold for these values (key1[>alias],CriticalRange key2[>alias],CriticalRange. CriticalRange is in the format [@]start:end, more information at nagios-plugins.org/doc/guidelines.html.')
parser.add_argument('-e', '--key_exists', dest='key_list', nargs='*',
help='Checks existence of these keys to determine status. Return warning if key is not present.')
parser.add_argument('-E', '--key_exists_critical', dest='key_list_critical', nargs='*',
help='Same as -e but return critical if key is not present.')
parser.add_argument('-q', '--key_equals', dest='key_value_list', nargs='*',
help='Checks equality of these keys and values (key[>alias],value key2,value2) to determine status.\
Multiple key values can be delimited with colon (key,value1:value2). Return warning if equality check fails')
parser.add_argument('-Q', '--key_equals_critical', dest='key_value_list_critical', nargs='*',
help='Same as -q but return critical if equality check fails.')
parser.add_argument('-m', '--key_metric', dest='metric_list', nargs='*',
help='Gathers the values of these keys (key[>alias],UnitOfMeasure,WarnRange,CriticalRange,Min,Max) for Nagios performance data.\
More information about Range format and units of measure for nagios can be found at nagios-plugins.org/doc/guidelines.html\
Additional formats for this parameter are: (key[>alias]), (key[>alias],UnitOfMeasure), (key[>alias],UnitOfMeasure,WarnRange,CriticalRange).')
return parser.parse_args() return parser.parse_args()
@@ -175,48 +304,133 @@ def debugPrint(debug_flag, message, pretty_flag=False):
else: else:
print message print message
if __name__ == "__main__" and len(sys.argv) >= 2 and sys.argv[1] == 'UnitTest':
import unittest
class RulesHelper:
separator = '.'
debug = False
key_threshold_warning,key_value_list,key_list,key_threshold_critical,key_value_list_critical,key_list_critical,metric_list = None, None, None, None, None, None, None
def dash_m(self, data):
self.metric_list = data; return self
def dash_e(self, data):
self.key_list = data; return self
def dash_E(self, data):
self.key_list_critical = data; return self
def dash_q(self, data):
self.key_value_list = data; return self
def dash_Q(self, data):
self.key_value_list_critical = data; return self
def dash_w(self, data):
self.key_threshold_warning = data; return self
def dash_c(self, data):
self.key_threshold_critical = data; return self
class UnitTest(unittest.TestCase):
rules = RulesHelper()
def check_data(self, args, jsondata, code):
data = json.loads(jsondata)
nagios = NagiosHelper()
processor = JsonRuleProcessor(data, args)
nagios.append_warning(processor.checkWarning())
nagios.append_critical(processor.checkCritical())
nagios.append_metrics(processor.checkMetrics())
self.assertEqual(code, nagios.getCode())
def test_metrics(self):
self.check_data(RulesHelper().dash_m(['metric,,1:4,1:5']), '{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:4']), '{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:5,6,10']), '{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:5,1,4']), '{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,s,@1:4,@6:10,1,10']), '{"metric": 5}', OK_CODE)
def test_exists(self):
self.check_data(RulesHelper().dash_e(['nothere']), '{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_E(['nothere']), '{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_e(['metric']), '{"metric": 5}', OK_CODE)
def test_equality(self):
self.check_data(RulesHelper().dash_q(['metric,6']), '{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_Q(['metric,6']), '{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_q(['metric,5']), '{"metric": 5}', OK_CODE)
def test_warning_thresholds(self):
self.check_data(RulesHelper().dash_w(['metric,5']), '{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,5:']), '{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,~:5']), '{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,1:5']), '{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5']), '{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5:']), '{"metric": 4}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@~:5']), '{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@1:5']), '{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,5']), '{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,5:']), '{"metric": 4}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,~:5']), '{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,1:5']), '{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5']), '{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5:']), '{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@~:5']), '{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@1:5']), '{"metric": 5}', WARNING_CODE)
def test_critical_thresholds(self):
self.check_data(RulesHelper().dash_c(['metric,5']), '{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,5:']), '{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,~:5']), '{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,1:5']), '{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5']), '{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5:']), '{"metric": 4}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@~:5']), '{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@1:5']), '{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,5']), '{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,5:']), '{"metric": 4}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,~:5']), '{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,1:5']), '{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5']), '{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5:']), '{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@~:5']), '{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@1:5']), '{"metric": 5}', CRITICAL_CODE)
def test_separator(self):
rules = RulesHelper()
rules.separator = '_'
self.check_data(
rules.dash_q(['(0)_gauges_jvm.buffers.direct.capacity(1)_value,1234']),
'[{ "gauges": { "jvm.buffers.direct.capacity": [{"value": 215415},{"value": 1234}]}}]',
OK_CODE)
unittest.main()
exit(0)
"""Program entry point""" """Program entry point"""
if __name__ == "__main__": if __name__ == "__main__":
args = parseArgs() args = parseArgs()
nagios = NagiosHelper() nagios = NagiosHelper()
if args.ssl: if args.ssl:
url = "https://%s" % args.host url = "https://%s" % args.host
else: else:
url = "http://%s" % args.host url = "http://%s" % args.host
if args.port: url += ":%s" % args.port
if args.path: url += "/%s" % args.path if args.path: url += "/%s" % args.path
debugPrint(args.debug, "url:%s" % url) debugPrint(args.debug, "url:%s" % url)
# Attempt to reach the endpoint # Attempt to reach the endpoint
try: try:
req = urllib2.Request(url) req = urllib2.Request(url)
if args.auth: if args.auth:
base64str = base64.encodestring(args.auth).replace('\n', '') base64str = base64.encodestring(args.auth).replace('\n', '')
req.add_header('Authorization', 'Basic %s' % base64str) req.add_header('Authorization', 'Basic %s' % base64str)
response = urllib2.urlopen(req) if args.timeout and args.data:
response = urllib2.urlopen(req, timeout=args.timeout, data=args.data)
elif args.timeout:
response = urllib2.urlopen(req, timeout=args.timeout)
elif args.data:
response = urllib2.urlopen(req, data=args.data)
else:
response = urllib2.urlopen(req)
except HTTPError as e: except HTTPError as e:
nagios.unknown("HTTPError[%s], url:%s" % (str(e.code), url)) nagios.append_unknown("HTTPError[%s], url:%s" % (str(e.code), url))
except URLError as e: except URLError as e:
nagios.critical("URLError[%s], url:%s" % (str(e.reason), url)) nagios.append_critical("URLError[%s], url:%s" % (str(e.reason), url))
else: else:
jsondata = response.read() jsondata = response.read()
data = json.loads(jsondata) data = json.loads(jsondata)
debugPrint(args.debug, 'json:') debugPrint(args.debug, 'json:')
debugPrint(args.debug, data, True) debugPrint(args.debug, data, True)
# Apply rules to returned JSON data # Apply rules to returned JSON data
processor = JsonRuleProcessor(data, args) processor = JsonRuleProcessor(data, args)
is_alive, reason = processor.isAlive() nagios.append_warning(processor.checkWarning())
nagios.append_critical(processor.checkCritical())
if is_alive: nagios.append_metrics(processor.checkMetrics())
# Rules all passed, attempt to get performance data
nagios.performance_data = processor.getMetrics()
nagios.ok("Status OK.")
else:
nagios.warning("Status check failed, reason:%s" % reason)
# Print Nagios specific string and exit appropriately # Print Nagios specific string and exit appropriately
print nagios.getMessage() print nagios.getMessage()
exit(nagios.code) exit(nagios.getCode())