Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package monitoring-plugins-http_json for
openSUSE:Factory checked in at 2026-05-05 15:15:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/monitoring-plugins-http_json (Old)
and /work/SRC/openSUSE:Factory/.monitoring-plugins-http_json.new.30200
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "monitoring-plugins-http_json"
Tue May 5 15:15:12 2026 rev:7 rq:1350730 version:2.4.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/monitoring-plugins-http_json/monitoring-plugins-http_json.changes
2026-03-02 17:35:47.343963810 +0100
+++
/work/SRC/openSUSE:Factory/.monitoring-plugins-http_json.new.30200/monitoring-plugins-http_json.changes
2026-05-05 15:16:31.962032487 +0200
@@ -1,0 +2,8 @@
+Mon May 4 08:43:15 UTC 2026 - Martin Hauke <[email protected]>
+
+- Update to version 2.4.0
+ * Add default timeout and handle timeout error.
+ * Add -M flag to map non-numeric perfdata values.
+ * Allow for bracket in key.
+
+-------------------------------------------------------------------
Old:
----
nagios-http-json-2.3.1.tar.gz
New:
----
nagios-http-json-2.4.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ monitoring-plugins-http_json.spec ++++++
--- /var/tmp/diff_new_pack.zBxxCW/_old 2026-05-05 15:16:32.446052569 +0200
+++ /var/tmp/diff_new_pack.zBxxCW/_new 2026-05-05 15:16:32.450052735 +0200
@@ -19,7 +19,7 @@
%define modname monitoring-plugins-http_json
%define pythons python3
Name: monitoring-plugins-http_json
-Version: 2.3.1
+Version: 2.4.0
Release: 0
Summary: Plugin for Nagios which checks json values from a given HTTP
endpoint
License: Apache-2.0
++++++ nagios-http-json-2.3.1.tar.gz -> nagios-http-json-2.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/nagios-http-json-2.3.1/README.md
new/nagios-http-json-2.4.0/README.md
--- old/nagios-http-json-2.3.1/README.md 2026-02-25 08:29:54.000000000
+0100
+++ new/nagios-http-json-2.4.0/README.md 2026-05-04 08:54:24.000000000
+0200
@@ -1,8 +1,8 @@

-# Nagios Json Plugin
+# Nagios JSON Plugin
-This is a generic plugin for Nagios which checks json values from a given HTTP
endpoint against argument specified rules and determines the status and
performance data for that service.
+This is a generic plugin for Nagios which checks JSON values from a given HTTP
endpoint against argument specified rules and determines the status and
performance data for that service.
## Installation
@@ -22,34 +22,30 @@
Add the following service definition to your server config (`localhost.cfg`):
```
-
define service {
use local-service
host_name localhost
service_description <command_description>
check_command <command_name>
- }
-
+}
```
Add the following command definition to your commands config
(`commands.config`):
```
-
-define command{
+define command {
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|-w|-c <rules>] [-m <metrics>]
- }
-
+}
```
-### Icinga2
+### Icinga
-An example Icinga2 command definition can be found here:
(`contrib/icinga2_check_command_definition.conf`)
+An example Icinga command definition can be found here: [contrib](contrib/).
## 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] [-d] [-s] -H HOST [-k] [-V] [--cacert CACERT]
@@ -66,25 +62,24 @@
[-y [KEY_VALUE_LIST_NOT [KEY_VALUE_LIST_NOT ...]]]
[-Y [KEY_VALUE_LIST_NOT_CRITICAL
[KEY_VALUE_LIST_NOT_CRITICAL ...]]]
[-m [METRIC_LIST [METRIC_LIST ...]]]
+ [-M KEY=VALUE]
Check HTTP JSON Nagios Plugin
-Generic Nagios plugin which checks json values from a given endpoint against
+Generic Nagios plugin which checks JSON values from a given endpoint against
argument specified rules and determines the status and performance data for
that service.
-Version: 2.2.0 (2024-05-14)
-
options:
- -h, --help show this help message and exit
- -d, --debug debug mode
+ -h, --help Show this help message and exit
+ -d, --debug Debug mode
-v, --verbose Verbose mode. Multiple -v options increase the
verbosity
-s, --ssl use TLS to connect to remote host
- -H HOST, --host HOST remote host to query
- -k, --insecure do not check server SSL certificate
+ -H HOST, --host HOST Remote host to query
+ -k, --insecure Do not check server SSL certificate
-X {GET,POST}, --request {GET,POST}
Specifies a custom request method to use when
communicating with the HTTP server
- -V, --version print version of this plugin
+ -V, --version Print version of this plugin
--cacert CACERT SSL CA certificate
--cert CERT SSL client certificate
--key KEY SSL client key ( if not bundled into the cert )
@@ -98,9 +93,9 @@
Exit with specified code when no valid JSON is
returned. Examples: 1 for Warning, 2 for Critical, 3 for Unknown (default: 3)
-B AUTH, --basic-auth AUTH
Basic auth string "username:password"
- -D DATA, --data DATA The http payload to send as a POST
+ -D DATA, --data DATA The HTTP payload to send as a POST
-A HEADERS, --headers HEADERS
- The http headers in JSON format.
+ The HTTP headers in JSON format.
-f SEPARATOR, --field_separator SEPARATOR
JSON Field separator, defaults to "."; Select element
in an array with "(" ")"
-F VALUE_SEPARATOR, --value_separator VALUE_SEPARATOR
@@ -125,7 +120,7 @@
(key[>alias],value key2,value2) to determine status.
Multiple key values can be delimited with colon
(key,value1:value2). Return warning if the key is older
- than the value (ex.: 30s,10m,2h,3d,...).
+ than the value (ex.: 30s,10m,2h,3d,...).
With at it return warning if the key is jounger
than the value (ex.: @30s,@10m,@2h,@3d,...).
With Minus you can shift the time in the future.
@@ -141,9 +136,11 @@
Same as -q but return critical if equality check
succeeds.
-m [METRIC_LIST ...], --key_metric [METRIC_LIST ...]
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-
+ 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).
+ -M KEY=VALUE Map the values of the gathered metric to the given
values. This can be used to map non-numeric values to numeric values, e.g. -M
+ Up=1. Can used multiple times. This flag is meant to
be used with the -m flag.
```
The check plugin respects the environment variables `HTTP_PROXY`,
`HTTPS_PROXY`.
@@ -154,129 +151,193 @@
**Data for key** `value`:
- { "value": 1000 }
+```json
+{ "value": 1000 }
+```
**Data for key** `capacity.value`:
- {
- "capacity": {
- "value": 1000
- }
+```json
+{
+ "capacity": {
+ "value": 1000
}
+}
+```
**Data for key** `(0).capacity.value`:
- [
- {
- "capacity": {
- "value": 1000
- }
+```json
+[
+ {
+ "capacity": {
+ "value": 1000
}
- ]
+ }
+]
+```
**Data for keys of all items in a list** `(*).capacity.value`:
- [
- {
- "capacity": {
- "value": 1000
- }
- },
- {
- "capacity": {
- "value": 2200
- }
+```json
+[
+ {
+ "capacity": {
+ "value": 1000
}
- ]
+ },
+ {
+ "capacity": {
+ "value": 2200
+ }
+ }
+]
+```
**Data for separator** `-f _` **and key**
`(0)_gauges_jvm.buffers.direct.capacity_value`:
- [
- {
- "gauges": {
- "jvm.buffers.direct.capacity":
- "value": 1000
- }
+```json
+[
+ {
+ "gauges": {
+ "jvm.buffers.direct.capacity": {
+ "value": 1000
}
}
- ]
+ }
+]
+```
**Data for keys** `ring_members(0)`, `ring_members(1)`, `ring_members(2)`:
- {
- "ring_members": [
- "[email protected]",
- "[email protected]",
- "[email protected]"
- ]
- }
-
+```json
+{
+ "ring_members": [
+ "[email protected]",
+ "[email protected]",
+ "[email protected]"
+ ]
+}
+```
**Data for multiple keys for an object** `-q capacity1.value,True
capacity2.value,True capacity3.value,True`
- {
- "capacity1": {
- "value": true
- },
- "capacity2": {
- "value": true
- },
- "capacity3": {
- "value": true
- }
- }
-
+```json
+{
+ "capacity1": {
+ "value": true
+ },
+ "capacity2": {
+ "value": true
+ },
+ "capacity3": {
+ "value": true
+ }
+}
+```
### Thresholds and Ranges
**Data**:
- { "metric": 1000 }
+```json
+{ "metric": 1000 }
+```
#### Relevant Commands
-* **Warning:** `./check_http_json.py -H <host>:<port> -p <path> -w
"metric,RANGE"`
-* **Critical:** `./check_http_json.py -H <host>:<port> -p <path> -c
"metric,RANGE"`
-* **Metrics with Warning:** `./check_http_json.py -H <host>:<port> -p <path>
-w "metric,RANGE"`
-* **Metrics with Critical:**
+**Warning:**
- ./check_http_json.py -H <host>:<port> -p <path> -w "metric,,,RANGE"
- ./check_http_json.py -H <host>:<port> -p <path> -w "metric,,,,MIN,MAX"
+```bash
+check_http_json.py -H <host>:<port> -p <path> -w "metric,RANGE"
+```
+
+**Critical:**
+
+```bash
+check_http_json.py -H <host>:<port> -p <path> -c "metric,RANGE"
+```
+
+**Metrics with Warning:**
+
+```bash
+check_http_json.py -H <host>:<port> -p <path> -w "metric,RANGE"
+```
+
+**Metrics with Critical:**
+
+```bash
+check_http_json.py -H <host>:<port> -p <path> -w "metric,,,RANGE"
+check_http_json.py -H <host>:<port> -p <path> -w "metric,,,,MIN,MAX"
+```
#### Range Definitions
-* **Format:** [@]START:END
-* **Generates a Warning or Critical if...**
- * **Value is less than 0 or greater than 1000:** `1000` or `0:1000`
- * **Value is greater than or equal to 1000, or less than or equal to 0:**
`@1000` or `@0:1000`
- * **Value is less than 1000:** `1000:`
- * **Value is greater than 1000:** `~:1000`
- * **Value is greater than or equal to 1000:** `@1000:`
+* Format: [@]START:END
+* Generates a Warning or Critical if:
+ * Value is less than 0 or greater than 1000: `1000` or `0:1000`
+ * Value is greater than or equal to 1000, or less than or equal to 0:
`@1000` or `@0:1000`
+ * Value is less than 1000: `1000:`
+ * Value is greater than 1000: `~:1000`
+ * Value is greater than or equal to 1000: `@1000:`
+
+More info about Nagios Range format and Units of Measure can be found at
[https://www.monitoring-plugins.org/doc/guidelines.html](https://www.monitoring-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).
+### Performance Data Metrics
+
+The `-m` and `-M` flags can be used to generate performance data from the JSON
data.
+
+```bash
+check_http_json.py -H host.internal --path example-data.json -e
'service.requests' -m 'service.requests'
+OK: 'service.requests'=12 Status OK. |'service.requests'=12
+
+check_http_json.py -H host.internal --path example-data.json -e
'service.requests' -m 'service.requests>webshopRequests,c'
+OK: 'webshopRequests'=12c Status OK. |'webshopRequests'=12c
+```
+
+The `-M` flag can be used to map non-numeric values to numeric values.
+
+```bash
+check_http_json.py -H host.internal --path example-data.json -e
'service.status' -m 'service.status'
+OK: 'service.status'=Up Status OK. |'service.requests'=Up
+
+check_http_json.py -H host.internal --path example-data.json -e
'service.status' -m 'service.status' -M Up=1
+OK: 'service.status'=1 Status OK. |'service.requests'=1
+```
### Timestamp
**Data**:
- { "metric": "2020-01-01 10:10:00.000000+00:00" }
+```json
+{ "metric": "2020-01-01 10:10:00.000000+00:00" }
+```
#### Relevant Commands
-* **Warning:** `./check_http_json.py -H <host>:<port> -p <path> --key_time
"metric,TIME"`
-* **Critical:** `./check_http_json.py -H <host>:<port> -p <path>
--key_time_critical "metric,TIME"`
+**Warning:**
+
+```bash
+check_http_json.py -H <host>:<port> -p <path> --key_time "metric,TIME"
+```
+
+**Critical:**
+
+```bash
+check_http_json.py -H <host>:<port> -p <path> --key_time_critical "metric,TIME"
+```
#### TIME Definitions
-* **Format:** [@][-]TIME
-* **Generates a Warning or Critical if...**
- * **Timestamp is more than 30 seconds in the past:** `30s`
- * **Timestamp is more than 5 minutes in the past:** `5m`
- * **Timestamp is more than 12 hours in the past:** `12h`
- * **Timestamp is more than 2 days in the past:** `2d`
- * **Timestamp is more than 30 minutes in the future:** `-30m`
- * **Timestamp is not more than 30 minutes in the future:** `@-30m`
- * **Timestamp is not more than 30 minutes in the past:** `@30m`
+* Format: [@][-]TIME
+* Generates a Warning or Critical if:
+ * Timestamp is more than 30 seconds in the past: `30s`
+ * Timestamp is more than 5 minutes in the past: `5m`
+ * Timestamp is more than 12 hours in the past: `12h`
+ * Timestamp is more than 2 days in the past: `2d`
+ * Timestamp is more than 30 minutes in the future: `-30m`
+ * Timestamp is not more than 30 minutes in the future: `@-30m`
+ * Timestamp is not more than 30 minutes in the past: `@30m`
##### Timestamp Format
@@ -296,7 +357,9 @@
#### Using Headers
-* `./check_http_json.py -H <host>:<port> -p <path> -A '{"content-type":
"application/json"}' -w "metric,RANGE"`
+```
+check_http_json.py -H <host>:<port> -p <path> -A '{"content-type":
"application/json"}' -w "metric,RANGE"
+```
## License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/nagios-http-json-2.3.1/check_http_json.py
new/nagios-http-json-2.4.0/check_http_json.py
--- old/nagios-http-json-2.3.1/check_http_json.py 2026-02-25
08:29:54.000000000 +0100
+++ new/nagios-http-json-2.4.0/check_http_json.py 2026-05-04
08:54:24.000000000 +0200
@@ -15,7 +15,7 @@
"""
Check HTTP JSON Nagios Plugin
-Generic Nagios plugin which checks json values from a given endpoint against
+Generic check plugin which checks JSON values from a given endpoint against
argument specified rules and determines the status and performance data for
that service.
"""
@@ -25,8 +25,8 @@
CRITICAL_CODE = 2
UNKNOWN_CODE = 3
-__version__ = '2.3.1'
-__version_date__ = '2026-02-25'
+__version__ = '2.4.0'
+__version_date__ = '2026-05-04'
class NagiosHelper:
"""
@@ -71,6 +71,8 @@
return code
def append_message(self, code, msg):
+ # Just to be sure that the | char is not included
+ msg = msg.replace('|', ' ')
if code > 2 or code < 0:
self.unknown_message += msg
if code == 1:
@@ -108,8 +110,7 @@
def getSubArrayElement(self, key, data):
subElemKey = key[:key.find(self.arrayOpener)]
- index = int(key[key.find(self.arrayOpener) +
- 1:key.find(self.arrayCloser)])
+ 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:
@@ -128,8 +129,7 @@
return (None, 'not_found')
def equals(self, key, value):
- return self.exists(key) and \
- str(self.get(key)) in value.split(self.value_separator)
+ return self.exists(key) and str(self.get(key)) in
value.split(self.value_separator)
def lte(self, key, value):
return self.exists(key) and float(self.get(key)) <= float(value)
@@ -151,30 +151,34 @@
Can navigate nested json keys with a dot format
(Element.Key.NestedKey). Returns (None, 'not_found') if not found
"""
-
if temp_data != '':
data = temp_data
else:
data = self.data
+
if len(key) <= 0:
return data
- if key.find(self.separator) != -1 and \
- key.find(self.arrayOpener) != -1:
+
+ if key.find(self.separator) != -1 and key.find(self.arrayOpener) != -1:
if key.find(self.separator) < key.find(self.arrayOpener):
return self.getSubElement(key, data)
else:
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 isinstance(data, dict) and key in data:
- return data[key]
- else:
- return (None, 'not_found')
+
+ if key.find(self.separator) != -1:
+ return self.getSubElement(key, data)
+
+ if key.find(self.arrayOpener) != -1:
+ # If we got an arrayOpener but it's not ([0-9*]) then it might
just be a string
+ if key[key.find(self.arrayOpener)+1].isnumeric() or
key[key.find(self.arrayOpener)+1] == "*":
+ return self.getSubArrayElement(key, data)
+ elif key in data:
+ return data[key]
+
+ if isinstance(data, dict) and key in data:
+ return data[key]
+
+ return (None, 'not_found')
def expandKey(self, key, keys):
if '(*)' not in key:
@@ -229,23 +233,17 @@
debugPrint(rules_args.debug, "separator: %s" % separator)
debugPrint(rules_args.debug, "value_separator: %s" % value_separator)
self.metric_list = self.expandKeys(self.rules.metric_list)
- self.key_threshold_warning = self.expandKeys(
- self.rules.key_threshold_warning)
- self.key_threshold_critical = self.expandKeys(
- self.rules.key_threshold_critical)
+ self.key_threshold_warning =
self.expandKeys(self.rules.key_threshold_warning)
+ self.key_threshold_critical =
self.expandKeys(self.rules.key_threshold_critical)
self.key_value_list = self.expandKeys(self.rules.key_value_list)
- self.key_value_list_not = self.expandKeys(
- self.rules.key_value_list_not)
+ self.key_value_list_not =
self.expandKeys(self.rules.key_value_list_not)
self.key_time_list = self.expandKeys(self.rules.key_time_list)
self.key_list = self.expandKeys(self.rules.key_list)
- self.key_value_list_critical = self.expandKeys(
- self.rules.key_value_list_critical)
- self.key_value_list_not_critical = self.expandKeys(
- self.rules.key_value_list_not_critical)
+ self.key_value_list_critical =
self.expandKeys(self.rules.key_value_list_critical)
+ self.key_value_list_not_critical =
self.expandKeys(self.rules.key_value_list_not_critical)
self.key_time_list_critical =
self.expandKeys(self.rules.key_time_list_critical)
self.key_list_critical = self.expandKeys(self.rules.key_list_critical)
- self.key_value_list_unknown = self.expandKeys(
- self.rules.key_value_list_unknown)
+ self.key_value_list_unknown =
self.expandKeys(self.rules.key_value_list_unknown)
def expandKeys(self, src):
if src is None:
@@ -271,8 +269,7 @@
k, v = kv.split(',')
key, alias = _getKeyAlias(k)
if not self.helper.equals(key, v):
- failure += " Key %s mismatch. %s != %s" % (alias, v,
-
self.helper.get(key))
+ failure += " Key %s mismatch. %s != %s" % (alias, v,
self.helper.get(key))
return failure
def checkNonEquality(self, equality_list):
@@ -281,8 +278,7 @@
k, v = kv.split(',')
key, alias = _getKeyAlias(k)
if self.helper.equals(key, v):
- failure += " Key %s match found. %s == %s" % (alias, v,
-
self.helper.get(key))
+ failure += " Key %s match found. %s == %s" % (alias, v,
self.helper.get(key))
return failure
def checkThreshold(self, key, alias, r):
@@ -302,27 +298,19 @@
end = vals[1]
if(start == '~'):
if (invert and self.helper.lte(key, end)):
- failure += " Value (%s) for key %s was less than or equal to
%s." % \
- (self.helper.get(key), alias, end)
+ failure += " Value (%s) for key %s was less than or equal to
%s." % (self.helper.get(key), alias, end)
elif (not invert and self.helper.gt(key, end)):
- failure += " Value (%s) for key %s was greater than %s." % \
- (self.helper.get(key), alias, end)
+ failure += " Value (%s) for key %s was greater than %s." %
(self.helper.get(key), alias, end)
elif(end == 'infinity'):
if (invert and self.helper.gte(key, start)):
- failure += " Value (%s) for key %s was greater than or equal
to %s." % \
- (self.helper.get(key), alias, start)
+ failure += " Value (%s) for key %s was greater than or equal
to %s." % (self.helper.get(key), alias, start)
elif (not invert and self.helper.lt(key, start)):
- failure += " Value (%s) for key %s was less than %s." % \
- (self.helper.get(key), alias, start)
+ failure += " Value (%s) for key %s was less than %s." %
(self.helper.get(key), alias, start)
else:
- if (invert and self.helper.gte(key, start) and
- self.helper.lte(key, end)):
- failure += " Value (%s) for key %s was inside the range
%s:%s." % \
- (self.helper.get(key), alias, start, end)
- elif (not invert and (self.helper.lt(key, start) or
- self.helper.gt(key, end))):
- failure += " Value (%s) for key %s was outside the range
%s:%s." % \
- (self.helper.get(key), alias, start, end)
+ if (invert and self.helper.gte(key, start) and
self.helper.lte(key, end)):
+ failure += " Value (%s) for key %s was inside the range
%s:%s." % (self.helper.get(key), alias, start, end)
+ elif (not invert and (self.helper.lt(key, start) or
self.helper.gt(key, end))):
+ failure += " Value (%s) for key %s was outside the range
%s:%s." % (self.helper.get(key), alias, start, end)
return failure
@@ -348,25 +336,23 @@
unit = r[-1]
if unit == 's':
- tiemduration = timedelta(seconds=duration)
+ time_duration = timedelta(seconds=duration)
elif unit == 'm':
- tiemduration = timedelta(minutes=duration)
+ time_duration = timedelta(minutes=duration)
elif unit == 'h':
- tiemduration = timedelta(hours=duration)
+ time_duration = timedelta(hours=duration)
elif unit == 'd':
- tiemduration = timedelta(days=duration)
+ time_duration = timedelta(days=duration)
else:
return " Value (%s) is not a vaild timeduration." % (r)
if not self.helper.exists(key):
- return " Key (%s) for key %s not Exists." % \
- (key, alias)
+ return " Key (%s) for key %s not Exists." % (key, alias)
try:
timestamp = datetime.fromisoformat(self.helper.get(key))
except ValueError as ve:
- return " Value (%s) for key %s is not a Date in ISO format. %s" % \
- (self.helper.get(key), alias, ve)
+ return " Value (%s) for key %s is not a Date in ISO format. %s" %
(self.helper.get(key), alias, ve)
now = datetime.now(timezone.utc)
@@ -376,19 +362,15 @@
age = now - timestamp
if not negative:
- if age > tiemduration and not invert:
- failure += " Value (%s) for key %s is older than now-%s%s." % \
- (self.helper.get(key), alias, duration, unit)
- if not age > tiemduration and invert:
- failure += " Value (%s) for key %s is newer than now-%s%s." % \
- (self.helper.get(key), alias, duration, unit)
+ if age > time_duration and not invert:
+ failure += " Value (%s) for key %s is older than now-%s%s." %
(self.helper.get(key), alias, duration, unit)
+ if not age > time_duration and invert:
+ failure += " Value (%s) for key %s is newer than now-%s%s." %
(self.helper.get(key), alias, duration, unit)
else:
- if age < -tiemduration and not invert:
- failure += " Value (%s) for key %s is newer than now+%s%s." % \
- (self.helper.get(key), alias, duration, unit)
- if not age < -tiemduration and invert:
- failure += " Value (%s) for key %s is older than now+%s%s.." %
\
- (self.helper.get(key), alias, duration, unit)
+ if age < -time_duration and not invert:
+ failure += " Value (%s) for key %s is newer than now+%s%s." %
(self.helper.get(key), alias, duration, unit)
+ if not age < -time_duration and invert:
+ failure += " Value (%s) for key %s is older than now+%s%s.." %
(self.helper.get(key), alias, duration, unit)
return failure
@@ -441,10 +423,12 @@
Return a Nagios specific performance metrics string given keys
and parameter definitions
"""
-
metrics = ''
warning = ''
critical = ''
+
+ kv = dict(self.rules.metric_value_mapping) if hasattr(self.rules,
'metric_value_mapping') else {}
+
if self.metric_list is not None:
for metric in self.metric_list:
key = metric
@@ -457,11 +441,13 @@
if len(vals) == 4:
key, uom, warn_range, crit_range = vals
if len(vals) == 6:
- key, uom, warn_range, crit_range, \
- minimum, maximum = vals
+ key, uom, warn_range, crit_range, minimum, maximum =
vals
key, alias = _getKeyAlias(key)
if self.helper.exists(key):
- metrics += "'%s'=%s" % (alias, self.helper.get(key))
+ value = self.helper.get(key)
+ # Apply the value mapping if it exists
+ v = kv.get(str(value), value)
+ metrics += "'%s'=%s" % (alias, v)
if uom:
metrics += uom
if warn_range is not None:
@@ -471,17 +457,14 @@
critical += self.checkThreshold(key, alias, crit_range)
metrics += ";%s" % crit_range
if minimum is not None:
- critical += self.checkThreshold(key, alias, minimum +
- ':')
+ critical += self.checkThreshold(key, alias, minimum +
':')
metrics += ";%s" % minimum
if maximum is not None:
- critical += self.checkThreshold(key, alias, '~:' +
- maximum)
+ critical += self.checkThreshold(key, alias, '~:' +
maximum)
metrics += ";%s" % maximum
metrics += ' '
return ("%s" % metrics, warning, critical)
-
def parseArgs(args):
"""
CLI argument definitions and parsing
@@ -493,81 +476,52 @@
formatter_class=argparse.RawDescriptionHelpFormatter
)
- parser.add_argument('-d', '--debug', action='store_true',
- help='debug mode')
- parser.add_argument('-v', '--verbose', action='count', default=0,
- help='Verbose mode. Multiple -v options increase the
verbosity')
-
- parser.add_argument('-s', '--ssl', action='store_true',
- help='use TLS to connect to remote host')
- parser.add_argument('-H', '--host', dest='host',
- required=not ('-V' in args or '--version' in args),
- help='remote host to query')
- parser.add_argument('-k', '--insecure', action='store_true',
- help='do not check server SSL certificate')
+ parser.add_argument('-d', '--debug', action='store_true', help='Debug
mode')
+ parser.add_argument('-v', '--verbose', action='count', default=0,
help='Verbose mode. Multiple -v options increase the verbosity')
+ parser.add_argument('-s', '--ssl', action='store_true', help='Use TLS to
connect to remote host')
+ parser.add_argument('-H', '--host', dest='host', required=not ('-V' in
args or '--version' in args), help='Remote host to query')
+ parser.add_argument('-k', '--insecure', action='store_true', help='Do not
check server SSL certificate')
parser.add_argument('-X', '--request', dest='method', default='GET',
choices=['GET', 'POST'],
help='Specifies a custom request method to use when
communicating with the HTTP server')
- parser.add_argument('-V', '--version', action='store_true',
- help='print version of this plugin')
- parser.add_argument('--cacert',
- dest='cacert', help='SSL CA certificate')
- parser.add_argument('--cert',
- dest='cert', help='SSL client certificate')
- parser.add_argument('--key', dest='key',
- help='SSL client key ( if not bundled into the cert )')
+ parser.add_argument('-V', '--version', action='store_true', help='Print
version of this plugin')
+ parser.add_argument('--cacert', dest='cacert', help='SSL CA certificate')
+ parser.add_argument('--cert', dest='cert', help='SSL client certificate')
+ parser.add_argument('--key', dest='key', help='SSL client key ( if not
bundled into the cert )')
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('-t', '--timeout', type=int, help='Connection timeout
(seconds)', default=10)
parser.add_argument('--unreachable-state', type=int, default=3,
help='Exit with specified code when the URL is
unreachable. Examples: 1 for Warning, 2 for Critical, 3 for Unknown (default:
3)')
parser.add_argument('--invalid-json-state', type=int, default=3,
help='Exit with specified code when no valid JSON is
returned. Examples: 1 for Warning, 2 for Critical, 3 for Unknown (default: 3)')
- 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('-A', '--headers', dest='headers',
- help='The http headers in JSON format.')
+ 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('-A', '--headers', dest='headers', help='The HTTP
headers in JSON format.')
parser.add_argument('-f', '--field_separator', dest='separator',
- help='''JSON Field separator, defaults to ".";
- Select element in an array with "(" ")"''')
- parser.add_argument('-F', '--value_separator', dest='value_separator',
- help='''JSON Value separator, defaults to ":"''')
- parser.add_argument('-w', '--warning', dest='key_threshold_warning',
- nargs='*',
- help='''Warning threshold for these values
+ help='''JSON Field separator, defaults to "."; Select
element in an array with "(" ")"''')
+ parser.add_argument('-F', '--value_separator', dest='value_separator',
help='''JSON Value separator, defaults to ":"''')
+ 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='*',
+ 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
+ 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',
- action='extend',
- nargs='*',
+ 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',
action='extend', 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',
- action='extend',
- nargs='*',
- help='''Same as -q but return critical if
- equality check fails.''')
+ parser.add_argument('-Q', '--key_equals_critical',
dest='key_value_list_critical', action='extend', nargs='*',
+ help='Same as -q but return critical if equality check
fails.')
parser.add_argument('--key_time', dest='key_time_list', nargs='*',
help='''Checks a Timestamp of these keys and values
(key[>alias],value key2,value2) to determine status.
@@ -577,41 +531,45 @@
With at it return warning if the key is jounger
than the value (ex.: @30s,@10m,@2h,@3d,...).
With Minus you can shift the time in the future.''')
- parser.add_argument('--key_time_critical',
- dest='key_time_list_critical', nargs='*',
- help='''Same as --key_time but return critical if
- Timestamp age fails.''')
- parser.add_argument('-u', '--key_equals_unknown',
- dest='key_value_list_unknown', nargs='*',
- help='''Same as -q but return unknown if
- equality check fails.''')
- parser.add_argument('-y', '--key_not_equals',
- dest='key_value_list_not', nargs='*',
+ parser.add_argument('--key_time_critical', dest='key_time_list_critical',
nargs='*',
+ help='Same as --key_time but return critical if
timestamp age fails.')
+ parser.add_argument('-u', '--key_equals_unknown',
dest='key_value_list_unknown', nargs='*',
+ help='Same as -q but return unknown if equality check
fails.')
+ parser.add_argument('-y', '--key_not_equals', dest='key_value_list_not',
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 succeeds''')
- parser.add_argument('-Y', '--key_not_equals_critical',
- dest='key_value_list_not_critical', nargs='*',
- help='''Same as -q but return critical if equality
- check succeeds.''')
- parser.add_argument('-m', '--key_metric', dest='metric_list',
- action='extend',
- nargs='*',
+ parser.add_argument('-Y', '--key_not_equals_critical',
dest='key_value_list_not_critical', nargs='*',
+ help='Same as -q but return critical if equality check
succeeds.')
+ parser.add_argument('-m', '--key_metric', dest='metric_list',
action='extend', 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
+ 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).''')
+ parser.add_argument('-M', dest='metric_value_mapping',
metavar="KEY=VALUE", type=key_value_pair, action="append", default=[],
+ help='''Map the values of the gathered metric to the
given values.
+ This can be used to map non-numeric values to numeric
values, e.g. -M Up=1. Can used multiple times.
+ This flag is meant to be used with the -m flag.''')
return parser.parse_args(args)
+def key_value_pair(value):
+ """
+ Small helper to split key=value arguments into a tuple.
+ """
+ parts = value.split('=', 1)
+ if len(parts) != 2:
+ raise argparse.ArgumentTypeError('invalid format %s. Expected
key=value' % (value))
+ return (parts[0], parts[1])
+
def debugPrint(debug_flag, message):
"""
Print debug messages if -d is set.
@@ -683,19 +641,19 @@
debugPrint(args.debug, "Headers:\n %s" % headers)
for header in headers:
req.add_header(header, headers[header])
- if args.timeout and args.data:
- databytes = str(args.data).encode()
- response = urllib.request.urlopen(req, timeout=args.timeout,
- data=databytes, context=context)
- elif args.timeout:
- response = urllib.request.urlopen(req, timeout=args.timeout,
- context=context)
- elif args.data:
- databytes = str(args.data).encode()
- response = urllib.request.urlopen(req, data=databytes, context=context)
- else:
- # pylint: disable=consider-using-with
- response = urllib.request.urlopen(req, context=context)
+
+ try:
+ if args.data:
+ databytes = str(args.data).encode()
+ response = urllib.request.urlopen(req, data=databytes,
timeout=args.timeout, context=context)
+ else:
+ # pylint: disable=consider-using-with
+ response = urllib.request.urlopen(req, timeout=args.timeout,
context=context)
+ except TimeoutError:
+ nagios = NagiosHelper()
+ nagios.append_message(args.unreachable_state, " %s socket timeout
after %s seconds" % (url, args.timeout))
+ print(nagios.getMessage())
+ sys.exit(nagios.getCode())
return response.read()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/nagios-http-json-2.3.1/requirements-dev.txt
new/nagios-http-json-2.4.0/requirements-dev.txt
--- old/nagios-http-json-2.3.1/requirements-dev.txt 2026-02-25
08:29:54.000000000 +0100
+++ new/nagios-http-json-2.4.0/requirements-dev.txt 2026-05-04
08:54:24.000000000 +0200
@@ -1,2 +1,2 @@
-coverage==7.8.0
-pylint==3.3.6
+coverage==7.13.5
+pylint==4.0.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/nagios-http-json-2.3.1/test/test_args.py
new/nagios-http-json-2.4.0/test/test_args.py
--- old/nagios-http-json-2.3.1/test/test_args.py 2026-02-25
08:29:54.000000000 +0100
+++ new/nagios-http-json-2.4.0/test/test_args.py 2026-05-04
08:54:24.000000000 +0200
@@ -32,3 +32,7 @@
parser = parseArgs(['-H', 'foobar', '-f', '_', '-F', '_'])
self.assertEqual(parser.separator, '_')
self.assertEqual(parser.value_separator, '_')
+
+ def test_parser_with_value_mapping(self):
+ parser = parseArgs(['-H', 'foobar', '-M', 'key=value'])
+ self.assertEqual(parser.metric_value_mapping, [('key', 'value')])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/nagios-http-json-2.3.1/test/test_check_http_json.py
new/nagios-http-json-2.4.0/test/test_check_http_json.py
--- old/nagios-http-json-2.3.1/test/test_check_http_json.py 2026-02-25
08:29:54.000000000 +0100
+++ new/nagios-http-json-2.4.0/test/test_check_http_json.py 2026-05-04
08:54:24.000000000 +0200
@@ -234,6 +234,40 @@
self.check_data(RulesHelper().dash_c(['(*).value,@1:5']),
'[{"value": 5},{"value": 1000}]', CRITICAL_CODE)
+ def test_metrics_value_mapping(self):
+ data = json.loads('{"status": "Up"}')
+ expected = ("'status'=0 ", '', '')
+
+ processor = JsonRuleProcessor(data, parseArgs(['-H', 'foobar', '-m',
'status', '-M', 'Up=0']))
+ actual = processor.checkMetrics()
+
+ self.assertEqual(actual, expected)
+
+ data = json.loads('{"status": "Down"}')
+ expected = ("'status'=1 ", '', '')
+
+ processor = JsonRuleProcessor(data, parseArgs(['-H', 'foobar', '-m',
'status', '-M', 'Up=0', '-M', 'Down=1']))
+ actual = processor.checkMetrics()
+
+ self.assertEqual(actual, expected)
+
+ def test_metrics_value_mapping_datatypes(self):
+ data = json.loads('{"status": "123"}')
+ expected = ("'status'=foo ", '', '')
+ # Test with string value as target
+ processor = JsonRuleProcessor(data, parseArgs(['-H', 'foobar', '-m',
'status', '-M', '123=foo']))
+ actual = processor.checkMetrics()
+
+ self.assertEqual(actual, expected)
+
+ data = json.loads('{"status": 123}')
+ expected = ("'status'=foo ", '', '')
+ # Test with string value as source and target
+ processor = JsonRuleProcessor(data, parseArgs(['-H', 'foobar', '-m',
'status', '-M', '123=foo']))
+ actual = processor.checkMetrics()
+
+ self.assertEqual(actual, expected)
+
def test_separator(self):
rules = RulesHelper()
rules.separator = '_'
@@ -319,7 +353,7 @@
data = "{\"timestamp\": \"%s\",\"timestamp2\": \"%s\"}" % (now,
now)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,30s',
'timestamp2,30s']), data, OK_CODE)
-
+
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,30m']), data,
OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,1h']),
data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,3h']), data,
OK_CODE)
@@ -448,3 +482,26 @@
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@-1h']),
data, CRITICAL_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@-3h']), data,
WARNING_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@-2d']),
data, CRITICAL_CODE)
+
+ def test_bracket_in_key(self):
+ """
+ https://github.com/drewkerrigan/nagios-http-json/issues/76
+ """
+
+ rules = RulesHelper()
+
+ # This should work
+ data = '[{"update status": "failure"}]'
+ self.check_data(rules.dash_q(['(*).update status,failure']), data,
OK_CODE)
+
+ data = '[{"update (status)": "failure"}]'
+ self.check_data(rules.dash_q(['(*).update (status),failure']), data,
OK_CODE)
+
+ data = '[{"update (((status)": "failure"}]'
+ self.check_data(rules.dash_q(['(*).update (((status),failure']), data,
OK_CODE)
+
+ data = '[{"update )status)": "failure"}]'
+ self.check_data(rules.dash_q(['(*).update )status),failure']), data,
OK_CODE)
+
+ data = '[{"update (status": "failure"}]'
+ self.check_data(rules.dash_q(['(*).update (status),failure']), data,
WARNING_CODE)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/nagios-http-json-2.3.1/test/test_main.py
new/nagios-http-json-2.4.0/test/test_main.py
--- old/nagios-http-json-2.3.1/test/test_main.py 2026-02-25
08:29:54.000000000 +0100
+++ new/nagios-http-json-2.4.0/test/test_main.py 2026-05-04
08:54:24.000000000 +0200
@@ -129,3 +129,16 @@
self.assertTrue('Error loading SSL CA' in str(mock_print.call_args))
self.assertEqual(test.exception.code, 3)
+
+ @mock.patch('builtins.print')
+ @mock.patch('urllib.request.urlopen')
+ def test_main_with_timeout(self, mock_request, mock_print):
+ args = ['-H', 'localhost']
+
+ mock_request.side_effect = TimeoutError('timeout')
+
+ with self.assertRaises(SystemExit) as test:
+ main(args)
+
+ self.assertTrue('timeout' in str(mock_print.call_args))
+ self.assertEqual(test.exception.code, 3)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/nagios-http-json-2.3.1/test/test_nagioshelper.py
new/nagios-http-json-2.4.0/test/test_nagioshelper.py
--- old/nagios-http-json-2.3.1/test/test_nagioshelper.py 2026-02-25
08:29:54.000000000 +0100
+++ new/nagios-http-json-2.4.0/test/test_nagioshelper.py 2026-05-04
08:54:24.000000000 +0200
@@ -49,3 +49,9 @@
helper = NagiosHelper()
helper.performance_data = 'foobar'
self.assertEqual('OK: foobar Status OK. |foobar', helper.getMessage())
+
+ def test_getmessage_with_perfdata_separator(self):
+
+ helper = NagiosHelper()
+ helper.append_message(1, 'exa|mple')
+ self.assertEqual('WARNING: Status WARNING. exa mple',
helper.getMessage())