1 Commits

Author SHA1 Message Date
Markus Opolka
dfcdf4d872 Rework JsonHelper.get() to work with bracket in keys 2024-03-22 16:19:43 +01:00
21 changed files with 252 additions and 843 deletions

View File

@@ -1,22 +1,17 @@
name: CI name: CI
on: on: [push, pull_request]
push:
branches: [main, master]
tags:
- v*
pull_request:
jobs: jobs:
gitHubActionForPytest: gitHubActionForPytest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: [3.8, 3.11, 3.12] python-version: [3.8, 3,9]
name: GitHub Action name: GitHub Action
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install -r requirements-dev.txt python -m pip install -r requirements-dev.txt

1
.gitignore vendored
View File

@@ -25,7 +25,6 @@ var/
.installed.cfg .installed.cfg
*.egg *.egg
.venv/ .venv/
venv/
# PyInstaller # PyInstaller
# Usually these files are written by a python script from a template # Usually these files are written by a python script from a template

View File

@@ -1,11 +1,9 @@
.PHONY: lint test coverage .PHONY: lint test coverage
PYTHON_PATH?=python3
lint: lint:
$(PYTHON_PATH) -m pylint check_http_json.py python -m pylint check_http_json.py
test: test:
$(PYTHON_PATH) -m unittest discover python -m unittest discover
coverage: coverage:
$(PYTHON_PATH) -m coverage run -m unittest discover python -m coverage run -m unittest discover
$(PYTHON_PATH) -m coverage report -m --include check_http_json.py python -m coverage report -m --include check_http_json.py

242
README.md
View File

@@ -4,50 +4,15 @@
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 ## Links
Requirements: * [CLI Usage](#cli-usage)
* [Examples](#examples)
* [Riak Stats](docs/RIAK.md)
* [Docker](docs/DOCKER.md)
* [Nagios Installation](#nagios-installation)
* Python 3.8+ ## CLI Usage
### Nagios
Assuming a standard installation of Nagios, the plugin can be executed from the machine that Nagios is running on.
```bash
cp check_http_json.py /usr/local/nagios/libexec/plugins/check_http_json.py
chmod +x /usr/local/nagios/libexec/plugins/check_http_json.py
```
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{
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
An example Icinga2 command definition can be found here: (`contrib/icinga2_check_command_definition.conf`)
## Usage
Executing `./check_http_json.py -h` will yield the following details: Executing `./check_http_json.py -h` will yield the following details:
@@ -73,12 +38,11 @@ Generic Nagios plugin which checks json values from a given endpoint against
argument specified rules and determines the status and performance data for argument specified rules and determines the status and performance data for
that service. that service.
Version: 2.2.0 (2024-05-14) Version: 2.1.2 (2022-09-15)
options: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
-d, --debug debug mode -d, --debug debug mode
-v, --verbose Verbose mode. Multiple -v options increase the verbosity
-s, --ssl use TLS to connect to remote host -s, --ssl use TLS to connect to remote host
-H HOST, --host HOST remote host to query -H HOST, --host HOST remote host to query
-k, --insecure do not check server SSL certificate -k, --insecure do not check server SSL certificate
@@ -92,62 +56,63 @@ options:
-p PATH, --path PATH Path -p PATH, --path PATH Path
-t TIMEOUT, --timeout TIMEOUT -t TIMEOUT, --timeout TIMEOUT
Connection timeout (seconds) Connection timeout (seconds)
--unreachable-state UNREACHABLE_STATE
Exit with specified code when the URL is unreachable. Examples: 1 for Warning, 2 for Critical, 3 for Unknown (default: 3)
--invalid-json-state INVALID_JSON_STATE
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 -B AUTH, --basic-auth AUTH
Basic auth string "username:password" 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 -A HEADERS, --headers HEADERS
The http headers in JSON format. The http headers in JSON format.
-f SEPARATOR, --field_separator SEPARATOR -f SEPARATOR, --field_separator SEPARATOR
JSON Field separator, defaults to "."; Select element in an array with "(" ")" JSON Field separator, defaults to "."; Select element
-F VALUE_SEPARATOR, --value_separator VALUE_SEPARATOR in an array with "(" ")"
JSON Value separator, defaults to ":" -F SEPARATOR, --value_separator SEPARATOR
-w [KEY_THRESHOLD_WARNING ...], --warning [KEY_THRESHOLD_WARNING ...] JSON Value separator, defaults to ":";
Warning threshold for these values (key1[>alias],WarnRange key2[>alias],WarnRange). WarnRange is in the format -w [KEY_THRESHOLD_WARNING [KEY_THRESHOLD_WARNING ...]], --warning [KEY_THRESHOLD_WARNING [KEY_THRESHOLD_WARNING ...]]
[@]start:end, more information at nagios-plugins.org/doc/guidelines.html. Warning threshold for these values
-c [KEY_THRESHOLD_CRITICAL ...], --critical [KEY_THRESHOLD_CRITICAL ...] (key1[>alias],WarnRange key2[>alias],WarnRange).
Critical threshold for these values (key1[>alias],CriticalRange key2[>alias],CriticalRange. CriticalRange is in WarnRange is in the format [@]start:end, more
the format [@]start:end, more information at nagios-plugins.org/doc/guidelines.html. information at nagios-plugins.org/doc/guidelines.html.
-e [KEY_LIST ...], --key_exists [KEY_LIST ...] -c [KEY_THRESHOLD_CRITICAL [KEY_THRESHOLD_CRITICAL ...]], --critical [KEY_THRESHOLD_CRITICAL [KEY_THRESHOLD_CRITICAL ...]]
Checks existence of these keys to determine status. Return warning if key is not present. Critical threshold for these values
-E [KEY_LIST_CRITICAL ...], --key_exists_critical [KEY_LIST_CRITICAL ...] (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 ...]]
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. Same as -e but return critical if key is not present.
-q [KEY_VALUE_LIST ...], --key_equals [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[>alias],value key2,value2) to determine status. Multiple key values Checks equality of these keys and values
can be delimited with colon (key,value1:value2). Return warning if equality check fails
-Q [KEY_VALUE_LIST_CRITICAL ...], --key_equals_critical [KEY_VALUE_LIST_CRITICAL ...]
Same as -q but return critical if equality check fails.
--key_time [KEY_TIME_LIST ...],
Checks a Timestamp of these keys and values
(key[>alias],value key2,value2) to determine status. (key[>alias],value key2,value2) to determine status.
Multiple key values can be delimited with colon Multiple key values can be delimited with colon
(key,value1:value2). Return warning if the key is older (key,value1:value2). Return warning if equality check
than the value (ex.: 30s,10m,2h,3d,...). fails
With at it return warning if the key is jounger -Q [KEY_VALUE_LIST_CRITICAL [KEY_VALUE_LIST_CRITICAL ...]], --key_equals_critical [KEY_VALUE_LIST_CRITICAL [KEY_VALUE_LIST_CRITICAL ...]]
than the value (ex.: @30s,@10m,@2h,@3d,...). Same as -q but return critical if equality check
With Minus you can shift the time in the future. fails.
--key_time_critical [KEY_TIME_LIST_CRITICAL ...], -u [KEY_VALUE_LIST_UNKNOWN [KEY_VALUE_LIST_UNKNOWN ...]], --key_equals_unknown [KEY_VALUE_LIST_UNKNOWN [KEY_VALUE_LIST_UNKNOWN ...]]
Same as --key_time but return critical if
Timestamp age fails.
-u [KEY_VALUE_LIST_UNKNOWN ...], --key_equals_unknown [KEY_VALUE_LIST_UNKNOWN ...]
Same as -q but return unknown if equality check fails. Same as -q but return unknown if equality check fails.
-y [KEY_VALUE_LIST_NOT ...], --key_not_equals [KEY_VALUE_LIST_NOT ...] -y [KEY_VALUE_LIST_NOT [KEY_VALUE_LIST_NOT ...]], --key_not_equals [KEY_VALUE_LIST_NOT [KEY_VALUE_LIST_NOT ...]]
Checks equality of these keys and values (key[>alias],value key2,value2) to determine status. Multiple key values Checks equality of these keys and values
can be delimited with colon (key,value1:value2). Return warning if equality check succeeds (key[>alias],value key2,value2) to determine status.
-Y [KEY_VALUE_LIST_NOT_CRITICAL ...], --key_not_equals_critical [KEY_VALUE_LIST_NOT_CRITICAL ...] Multiple key values can be delimited with colon
Same as -q but return critical if equality check succeeds. (key,value1:value2). Return warning if equality check
-m [METRIC_LIST ...], --key_metric [METRIC_LIST ...] succeeds
Gathers the values of these keys (key[>alias], UnitOfMeasure,WarnRange,CriticalRange,Min,Max) for Nagios -Y [KEY_VALUE_LIST_NOT_CRITICAL [KEY_VALUE_LIST_NOT_CRITICAL ...]], --key_not_equals_critical [KEY_VALUE_LIST_NOT_CRITICAL [KEY_VALUE_LIST_NOT_CRITICAL ...]]
performance data. More information about Range format and units of measure for nagios can be found at nagios- Same as -q but return critical if equality check
plugins.org/doc/guidelines.html Additional formats for this parameter are: (key[>alias]), succeeds.
(key[>alias],UnitOfMeasure), (key[>alias],UnitOfMeasure,WarnRange, CriticalRange). -m [METRIC_LIST [METRIC_LIST ...]], --key_metric [METRIC_LIST [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-plugins.org/doc/guidelines.html Additional
formats for this parameter are: (key[>alias]),
(key[>alias],UnitOfMeasure),
(key[>alias],UnitOfMeasure,WarnRange, CriticalRange).
``` ```
The check plugin respects the environment variables `HTTP_PROXY`, `HTTPS_PROXY`.
## Examples ## Examples
### Key Naming ### Key Naming
@@ -211,22 +176,6 @@ The check plugin respects the environment variables `HTTP_PROXY`, `HTTPS_PROXY`.
] ]
} }
**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
}
}
### Thresholds and Ranges ### Thresholds and Ranges
**Data**: **Data**:
@@ -255,49 +204,54 @@ The check plugin respects the environment variables `HTTP_PROXY`, `HTTPS_PROXY`.
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).
### Timestamp
**Data**:
{ "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"`
#### 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`
##### Timestamp Format
This plugin uses the Python function 'datetime.fromisoformat'.
Since Python 3.11 any valid ISO 8601 format is supported, with the following exceptions:
* Time zone offsets may have fractional seconds.
* The T separator may be replaced by any single unicode character.
* Fractional hours and minutes are not supported.
* Reduced precision dates are not currently supported (YYYY-MM, YYYY).
* Extended date representations are not currently supported (±YYYYYY-MM-DD).
* Ordinal dates are not currently supported (YYYY-OOO).
Before Python 3.11, this method only supported the format YYYY-MM-DD
More info and examples the about Timestamp Format can be found at [https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat](https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat).
#### Using Headers #### 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"`
## Nagios Installation
### Requirements
* Python 3.6+
### Configuration
Assuming a standard installation of Nagios, the plugin can be executed from the machine that Nagios is running on.
```bash
cp check_http_json.py /usr/local/nagios/libexec/plugins/check_http_json.py
chmod +x /usr/local/nagios/libexec/plugins/check_http_json.py
```
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{
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 configuration
The Icinga2 command definition can be found here: (contrib/icinga2_check_command_definition.conf)
## License ## License
Copyright 2014-2015 Drew Kerrigan. Copyright 2014-2015 Drew Kerrigan.

View File

@@ -6,10 +6,9 @@ import json
import argparse import argparse
import sys import sys
import ssl import ssl
import traceback from pprint import pprint
from urllib.error import HTTPError from urllib.error import HTTPError
from urllib.error import URLError from urllib.error import URLError
from datetime import datetime, timedelta, timezone
plugin_description = \ plugin_description = \
""" """
@@ -25,8 +24,8 @@ WARNING_CODE = 1
CRITICAL_CODE = 2 CRITICAL_CODE = 2
UNKNOWN_CODE = 3 UNKNOWN_CODE = 3
__version__ = '2.3.0' __version__ = '2.1.2'
__version_date__ = '2025-04-11' __version_date__ = '2022-09-15'
class NagiosHelper: class NagiosHelper:
""" """
@@ -70,19 +69,20 @@ class NagiosHelper:
code = UNKNOWN_CODE code = UNKNOWN_CODE
return code return code
def append_message(self, code, msg): def append_warning(self, warning_message):
if code > 2 or code < 0: self.warning_message += warning_message
self.unknown_message += msg
if code == 1: def append_critical(self, critical_message):
self.warning_message += msg self.critical_message += critical_message
if code == 2:
self.critical_message += msg def append_unknown(self, unknown_message):
self.unknown_message += unknown_message
def append_metrics(self, metrics): def append_metrics(self, metrics):
(performance_data, warning_message, critical_message) = metrics (performance_data, warning_message, critical_message) = metrics
self.performance_data += performance_data self.performance_data += performance_data
self.append_message(WARNING_CODE, warning_message) self.append_warning(warning_message)
self.append_message(CRITICAL_CODE, critical_message) self.append_critical(critical_message)
class JsonHelper: class JsonHelper:
@@ -156,24 +156,29 @@ class JsonHelper:
data = temp_data data = temp_data
else: else:
data = self.data data = self.data
if len(key) <= 0: if len(key) <= 0:
return data 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): if key.find(self.separator) < key.find(self.arrayOpener):
return self.getSubElement(key, data) return self.getSubElement(key, data)
else: else:
return self.getSubArrayElement(key, data) return self.getSubArrayElement(key, data)
else:
if key.find(self.separator) != -1: if key.find(self.separator) != -1:
return self.getSubElement(key, data) return self.getSubElement(key, data)
else:
if key.find(self.arrayOpener) != -1: if key.find(self.arrayOpener) != -1:
# If we got an arrayOpener but the next char is not [0-9] or * then it might just be a string
# This isn't optimal since this 'update (foobar)(0)' still won't work
if key[key.find(self.arrayOpener)+1].isnumeric() or key[key.find(self.arrayOpener)+1] == "*":
return self.getSubArrayElement(key, data) return self.getSubArrayElement(key, data)
else:
if isinstance(data, dict) and key in data: if isinstance(data, dict) and key in data:
return data[key] return data[key]
else:
return (None, 'not_found') return (None, 'not_found')
def expandKey(self, key, keys): def expandKey(self, key, keys):
@@ -236,13 +241,11 @@ class JsonRuleProcessor:
self.key_value_list = self.expandKeys(self.rules.key_value_list) self.key_value_list = self.expandKeys(self.rules.key_value_list)
self.key_value_list_not = self.expandKeys( self.key_value_list_not = self.expandKeys(
self.rules.key_value_list_not) 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_list = self.expandKeys(self.rules.key_list)
self.key_value_list_critical = self.expandKeys( self.key_value_list_critical = self.expandKeys(
self.rules.key_value_list_critical) self.rules.key_value_list_critical)
self.key_value_list_not_critical = self.expandKeys( self.key_value_list_not_critical = self.expandKeys(
self.rules.key_value_list_not_critical) 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_list_critical = self.expandKeys(self.rules.key_list_critical)
self.key_value_list_unknown = self.expandKeys( self.key_value_list_unknown = self.expandKeys(
self.rules.key_value_list_unknown) self.rules.key_value_list_unknown)
@@ -334,72 +337,6 @@ class JsonRuleProcessor:
failure += self.checkThreshold(key, alias, r) failure += self.checkThreshold(key, alias, r)
return failure return failure
def checkTimestamp(self, key, alias, r):
failure = ''
invert = False
negative = False
if r.startswith('@'):
invert = True
r = r[1:]
if r.startswith('-'):
negative = True
r = r[1:]
duration = int(r[:-1])
unit = r[-1]
if unit == 's':
tiemduration = timedelta(seconds=duration)
elif unit == 'm':
tiemduration = timedelta(minutes=duration)
elif unit == 'h':
tiemduration = timedelta(hours=duration)
elif unit == 'd':
tiemduration = 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)
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)
now = datetime.now(timezone.utc)
if timestamp.tzinfo is None:
timestamp = timestamp.replace(tzinfo=timezone.utc)
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)
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)
return failure
def checkTimestamps(self, threshold_list):
failure = ''
for threshold in threshold_list:
k, r = threshold.split(',')
key, alias = _getKeyAlias(k)
failure += self.checkTimestamp(key, alias, r)
return failure
def checkWarning(self): def checkWarning(self):
failure = '' failure = ''
if self.key_threshold_warning is not None: if self.key_threshold_warning is not None:
@@ -408,8 +345,6 @@ class JsonRuleProcessor:
failure += self.checkEquality(self.key_value_list) failure += self.checkEquality(self.key_value_list)
if self.key_value_list_not is not None: if self.key_value_list_not is not None:
failure += self.checkNonEquality(self.key_value_list_not) failure += self.checkNonEquality(self.key_value_list_not)
if self.key_time_list is not None:
failure += self.checkTimestamps(self.key_time_list)
if self.key_list is not None: if self.key_list is not None:
failure += self.checkExists(self.key_list) failure += self.checkExists(self.key_list)
return failure return failure
@@ -424,8 +359,6 @@ class JsonRuleProcessor:
failure += self.checkEquality(self.key_value_list_critical) failure += self.checkEquality(self.key_value_list_critical)
if self.key_value_list_not_critical is not None: if self.key_value_list_not_critical is not None:
failure += self.checkNonEquality(self.key_value_list_not_critical) failure += self.checkNonEquality(self.key_value_list_not_critical)
if self.key_time_list_critical is not None:
failure += self.checkTimestamps(self.key_time_list_critical)
if self.key_list_critical is not None: if self.key_list_critical is not None:
failure += self.checkExists(self.key_list_critical) failure += self.checkExists(self.key_list_critical)
return failure return failure
@@ -495,9 +428,6 @@ def parseArgs(args):
parser.add_argument('-d', '--debug', action='store_true', parser.add_argument('-d', '--debug', action='store_true',
help='debug mode') 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', parser.add_argument('-s', '--ssl', action='store_true',
help='use TLS to connect to remote host') help='use TLS to connect to remote host')
parser.add_argument('-H', '--host', dest='host', parser.add_argument('-H', '--host', dest='host',
@@ -519,10 +449,6 @@ def parseArgs(args):
parser.add_argument('-p', '--path', dest='path', help='Path') parser.add_argument('-p', '--path', dest='path', help='Path')
parser.add_argument('-t', '--timeout', type=int, parser.add_argument('-t', '--timeout', type=int,
help='Connection timeout (seconds)') help='Connection timeout (seconds)')
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', parser.add_argument('-B', '--basic-auth', dest='auth',
help='Basic auth string "username:password"') help='Basic auth string "username:password"')
parser.add_argument('-D', '--data', dest='data', parser.add_argument('-D', '--data', dest='data',
@@ -551,36 +477,21 @@ def parseArgs(args):
parser.add_argument('-e', '--key_exists', dest='key_list', nargs='*', parser.add_argument('-e', '--key_exists', dest='key_list', nargs='*',
help='''Checks existence of these keys to determine help='''Checks existence of these keys to determine
status. Return warning if key is not present.''') status. Return warning if key is not present.''')
parser.add_argument('-E', '--key_exists_critical', dest='key_list_critical', parser.add_argument('-E', '--key_exists_critical',
dest='key_list_critical',
nargs='*', nargs='*',
help='''Same as -e but return critical if key is help='''Same as -e but return critical if key is
not present.''') not present.''')
parser.add_argument('-q', '--key_equals', dest='key_value_list', parser.add_argument('-q', '--key_equals', dest='key_value_list', nargs='*',
action='extend',
nargs='*',
help='''Checks equality of these keys and values help='''Checks equality of these keys and values
(key[>alias],value key2,value2) to determine status. (key[>alias],value key2,value2) to determine status.
Multiple key values can be delimited with colon Multiple key values can be delimited with colon
(key,value1:value2). Return warning if equality (key,value1:value2). Return warning if equality
check fails''') check fails''')
parser.add_argument('-Q', '--key_equals_critical', dest='key_value_list_critical', parser.add_argument('-Q', '--key_equals_critical',
action='extend', dest='key_value_list_critical', nargs='*',
nargs='*',
help='''Same as -q but return critical if help='''Same as -q but return critical if
equality check fails.''') 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.
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,...).
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', parser.add_argument('-u', '--key_equals_unknown',
dest='key_value_list_unknown', nargs='*', dest='key_value_list_unknown', nargs='*',
help='''Same as -q but return unknown if help='''Same as -q but return unknown if
@@ -596,9 +507,7 @@ def parseArgs(args):
dest='key_value_list_not_critical', nargs='*', dest='key_value_list_not_critical', nargs='*',
help='''Same as -q but return critical if equality help='''Same as -q but return critical if equality
check succeeds.''') check succeeds.''')
parser.add_argument('-m', '--key_metric', dest='metric_list', parser.add_argument('-m', '--key_metric', dest='metric_list', nargs='*',
action='extend',
nargs='*',
help='''Gathers the values of these keys (key[>alias], help='''Gathers the values of these keys (key[>alias],
UnitOfMeasure,WarnRange,CriticalRange,Min,Max) for UnitOfMeasure,WarnRange,CriticalRange,Min,Max) for
Nagios performance data. More information about Range Nagios performance data. More information about Range
@@ -612,30 +521,33 @@ def parseArgs(args):
return parser.parse_args(args) return parser.parse_args(args)
def debugPrint(debug_flag, message): def debugPrint(debug_flag, message, pretty_flag=False):
""" """
Print debug messages if -d is set. Print debug messages if -d (debug_flat ) is set.
""" """
if not debug_flag:
return
if debug_flag:
if pretty_flag:
pprint(message)
else:
print(message) print(message)
def verbosePrint(verbose_flag, when, message):
"""
Print verbose messages if -v is set.
Since -v can be used multiple times, the when parameter sets the required amount before printing
"""
if not verbose_flag:
return
if verbose_flag >= when:
print(message)
def prepare_context(args): def main(cliargs):
""" """
Prepare TLS Context Main entrypoint for CLI
""" """
args = parseArgs(cliargs)
nagios = NagiosHelper() nagios = NagiosHelper()
context = None
if args.version:
print('Version: %s - Date: %s' % (__version__, __version_date__))
sys.exit(0)
if args.ssl:
url = "https://%s" % args.host
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.options |= ssl.OP_NO_SSLv2 context.options |= ssl.OP_NO_SSLv2
@@ -651,27 +563,38 @@ def prepare_context(args):
try: try:
context.load_verify_locations(args.cacert) context.load_verify_locations(args.cacert)
except ssl.SSLError: except ssl.SSLError:
nagios.append_message(UNKNOWN_CODE, 'Error loading SSL CA cert "%s"!' % args.cacert) nagios.append_unknown(
'Error loading SSL CA cert "%s"!'
% args.cacert)
if args.cert: if args.cert:
try: try:
context.load_cert_chain(args.cert, keyfile=args.key) context.load_cert_chain(args.cert, keyfile=args.key)
except ssl.SSLError: except ssl.SSLError:
if args.key: if args.key:
nagios.append_message(UNKNOWN_CODE, 'Error loading SSL cert. Make sure key "%s" belongs to cert "%s"!' % (args.key, args.cert)) nagios.append_unknown(
'Error loading SSL cert. Make sure key "%s" belongs to cert "%s"!'
% (args.key, args.cert))
else: else:
nagios.append_message(UNKNOWN_CODE, 'Error loading SSL cert. Make sure "%s" contains the key as well!' % (args.cert)) nagios.append_unknown(
'Error loading SSL cert. Make sure "%s" contains the key as well!'
% (args.cert))
if nagios.getCode() != OK_CODE: if nagios.getCode() != OK_CODE:
print(nagios.getMessage()) print(nagios.getMessage())
sys.exit(nagios.getCode()) sys.exit(nagios.getCode())
return context else:
url = "http://%s" % args.host
if args.port:
url += ":%s" % args.port
if args.path:
url += "/%s" % args.path
debugPrint(args.debug, "url:%s" % url)
json_data = ''
def make_request(args, url, context): try:
"""
Performs the actual request to the given URL
"""
req = urllib.request.Request(url, method=args.method) req = urllib.request.Request(url, method=args.method)
req.add_header("User-Agent", "check_http_json") req.add_header("User-Agent", "check_http_json")
if args.auth: if args.auth:
@@ -697,80 +620,37 @@ def make_request(args, url, context):
# pylint: disable=consider-using-with # pylint: disable=consider-using-with
response = urllib.request.urlopen(req, context=context) response = urllib.request.urlopen(req, context=context)
return response.read() json_data = response.read()
def main(cliargs):
"""
Main entrypoint for CLI
"""
args = parseArgs(cliargs)
nagios = NagiosHelper()
context = None
if args.version:
print('Version: %s - Date: %s' % (__version__, __version_date__))
sys.exit(0)
if args.ssl:
url = "https://%s" % args.host
context = prepare_context(args)
else:
url = "http://%s" % args.host
if args.port:
url += ":%s" % args.port
if args.path:
url += "/%s" % args.path
debugPrint(args.debug, "url: %s" % url)
json_data = ''
try:
# Requesting the data from the URL
json_data = make_request(args, url, context)
except HTTPError as e: except HTTPError as e:
# Try to recover from HTTP Error, if there is JSON in the response # Try to recover from HTTP Error, if there is JSON in the response
if "json" in e.info().get_content_subtype(): if "json" in e.info().get_content_subtype():
json_data = e.read() json_data = e.read()
else: else:
exit_code = args.invalid_json_state nagios.append_unknown(" HTTPError[%s], url:%s" % (str(e.code), url))
nagios.append_message(exit_code, " Could not find JSON in HTTP body. HTTPError[%s], url:%s" % (str(e.code), url))
except URLError as e: except URLError as e:
# Some users might prefer another exit code if the URL wasn't reached nagios.append_critical(" URLError[%s], url:%s" % (str(e.reason), url))
exit_code = args.unreachable_state
nagios.append_message(exit_code, " URLError[%s], url:%s" % (str(e.reason), url))
# Since we don't got any data, we can simply exit
print(nagios.getMessage())
sys.exit(nagios.getCode())
try: try:
# Loading the JSON data from the request
data = json.loads(json_data) data = json.loads(json_data)
except ValueError as e: except ValueError as e:
exit_code = args.invalid_json_state nagios.append_unknown(" Parser error: %s" % str(e))
debugPrint(args.debug, traceback.format_exc())
nagios.append_message(exit_code, " JSON Parser error: %s" % str(e))
print(nagios.getMessage())
sys.exit(nagios.getCode())
else:
verbosePrint(args.verbose, 1, json.dumps(data, indent=2))
try: else:
# Applying rules to returned JSON data debugPrint(args.debug, 'json:')
debugPrint(args.debug, data, True)
# Apply rules to returned JSON data
processor = JsonRuleProcessor(data, args) processor = JsonRuleProcessor(data, args)
nagios.append_message(WARNING_CODE, processor.checkWarning()) nagios.append_warning(processor.checkWarning())
nagios.append_message(CRITICAL_CODE, processor.checkCritical()) nagios.append_critical(processor.checkCritical())
nagios.append_metrics(processor.checkMetrics()) nagios.append_metrics(processor.checkMetrics())
nagios.append_message(UNKNOWN_CODE, processor.checkUnknown()) nagios.append_unknown(processor.checkUnknown())
except Exception as e: # pylint: disable=broad-exception-caught
debugPrint(args.debug, traceback.format_exc())
nagios.append_message(UNKNOWN_CODE, " Rule Parser error: %s" % str(e))
# Print Nagios specific string and exit appropriately # Print Nagios specific string and exit appropriately
print(nagios.getMessage()) print(nagios.getMessage())
sys.exit(nagios.getCode()) sys.exit(nagios.getCode())
if __name__ == "__main__": if __name__ == "__main__":
# Program entry point # Program entry point
main(sys.argv[1:]) main(sys.argv[1:])

View File

@@ -1,6 +1,4 @@
object CheckCommand "http_json" { object CheckCommand "http_json" {
// Example configuration for Icinga
import "plugin-check-command" import "plugin-check-command"
command = [ PluginDir + "/check_http_json.py" ] command = [ PluginDir + "/check_http_json.py" ]
@@ -55,13 +53,6 @@ object CheckCommand "http_json" {
value = "$http_json_headers$" value = "$http_json_headers$"
description = "additional http headers in JSON format to send with the request" description = "additional http headers in JSON format to send with the request"
} }
"--unreachable-state" = {
value = "$http_json_unreachable_state$"
description = "Exit with specified code when the URL is unreachable."
}
"--invalid-json-state" = {
value = "$http_json_invalid_json_state$"
description = "Exit with specified code when no valid JSON is returned."
"--field_separator" = { "--field_separator" = {
value = "$http_json_field_separator$" value = "$http_json_field_separator$"
description = "JSON Field separator, defaults to '.'; Select element in an array with '(' ')'" description = "JSON Field separator, defaults to '.'; Select element in an array with '(' ')'"
@@ -73,62 +64,42 @@ object CheckCommand "http_json" {
"--warning" = { "--warning" = {
value = "$http_json_warning$" value = "$http_json_warning$"
description = "Warning threshold for these values, WarningRange is in the format [@]start:end" description = "Warning threshold for these values, WarningRange is in the format [@]start:end"
repeat_key = true
} }
"--critical" = { "--critical" = {
value = "$http_json_critical$" value = "$http_json_critical$"
description = "Critical threshold for these values, CriticalRange is in the format [@]start:end" description = "Critical threshold for these values, CriticalRange is in the format [@]start:end"
repeat_key = true
} }
"--key_exists" = { "--key_exists" = {
value = "$http_json_key_exists$" value = "$http_json_key_exists$"
description = "Checks existence of these keys to determine status. Return warning if key is not present." description = "Checks existence of these keys to determine status. Return warning if key is not present."
repeat_key = true
} }
"--key_exists_critical" = { "--key_exists_critical" = {
value = "$http_json_key_exists_critical$" value = "$http_json_key_exists_critical$"
description = "Checks existence of these keys to determine status. Return critical if key is not present." description = "Checks existence of these keys to determine status. Return critical if key is not present."
repeat_key = true
} }
"--key_equals" = { "--key_equals" = {
value = "$http_json_key_equals$" value = "$http_json_key_equals$"
description = "Checks equality of these keys and values. Return warning if equality check fails" description = "Checks equality of these keys and values. Return warning if equality check fails"
repeat_key = true
} }
"--key_equals_critical" = { "--key_equals_critical" = {
value = "$http_json_key_equals_critical$" value = "$http_json_key_equals_critical$"
description = "Checks equality of these keys and values. Return critical if equality check fails" description = "Checks equality of these keys and values. Return critical if equality check fails"
repeat_key = true
} }
"--key_equals_unknown" = { "--key_equals_unknown" = {
value = "$http_json_key_equals_unknown$" value = "$http_json_key_equals_unknown$"
description = "Checks equality of these keys and values. Return unknown if equality check fails" description = "Checks equality of these keys and values. Return unknown if equality check fails"
repeat_key = true
} }
"--key_not_equals" = { "--key_not_equals" = {
value = "$http_json_key_not_equals$" value = "$http_json_key_not_equals$"
description = "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." description = "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."
repeat_key = true
} }
"--key_not_equals_critical" = { "--key_not_equals_critical" = {
value = "$http_json_key_not_equals_critical$" value = "$http_json_key_not_equals_critical$"
description = "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 critical if equality check succeeds." description = "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 critical if equality check succeeds."
repeat_key = true
} }
"--key_metric" = { "--key_metric" = {
value = "$http_json_key_metric$" value = "$http_json_key_metric$"
description = "Gathers the values of these keys" description = "Gathers the values of these keys"
repeat_key = true
}
"--key_time" = {
value = "$http_json_key_time$"
description = " Checks a Timestamp of these keys and values (key[>alias],value key2,value2) to determine status."
repeat_key = true
}
"--key_time_critical" = {
value = "$http_json_key_time_critical$"
description = "Same as --key_time but return critical if Timestamp age fails."
repeat_key = true
} }
} }
} }

View File

@@ -1,2 +1,2 @@
coverage==7.8.0 coverage==6.4.4
pylint==3.3.6 pylint==2.15.2

View File

@@ -28,8 +28,6 @@ class RulesHelper:
key_threshold_critical = None key_threshold_critical = None
key_value_list_critical = None key_value_list_critical = None
key_value_list_not_critical = None key_value_list_not_critical = None
key_time_list = None
key_time_list_critical = None
key_value_list_unknown = None key_value_list_unknown = None
key_list_critical = None key_list_critical = None
metric_list = None metric_list = None
@@ -74,13 +72,6 @@ class RulesHelper:
self.key_threshold_critical = data self.key_threshold_critical = data
return self return self
def dash_dash_key_time(self, data):
self.key_time_list = data
return self
def dash_dash_key_time_critical(self, data):
self.key_time_list_critical = data
return self
class UtilTest(unittest.TestCase): class UtilTest(unittest.TestCase):
""" """
@@ -93,10 +84,10 @@ class UtilTest(unittest.TestCase):
data = json.loads(jsondata) data = json.loads(jsondata)
nagios = NagiosHelper() nagios = NagiosHelper()
processor = JsonRuleProcessor(data, args) processor = JsonRuleProcessor(data, args)
nagios.append_message(WARNING_CODE, processor.checkWarning()) nagios.append_warning(processor.checkWarning())
nagios.append_message(CRITICAL_CODE, processor.checkCritical()) nagios.append_critical(processor.checkCritical())
nagios.append_metrics(processor.checkMetrics()) nagios.append_metrics(processor.checkMetrics())
nagios.append_message(UNKNOWN_CODE, processor.checkUnknown()) nagios.append_unknown(processor.checkUnknown())
self.assertEqual(code, nagios.getCode()) self.assertEqual(code, nagios.getCode())
def test_metrics(self): def test_metrics(self):
@@ -312,139 +303,29 @@ class UtilTest(unittest.TestCase):
data = '[]' data = '[]'
self.check_data(rules.dash_q(['(*).update_status,warn_me']), data, CRITICAL_CODE) self.check_data(rules.dash_q(['(*).update_status,warn_me']), data, CRITICAL_CODE)
def test_key_time(self): def test_bracket_in_key(self):
if sys.version_info[1] >= 11: """
# Test current timestamp. https://github.com/drewkerrigan/nagios-http-json/issues/76
now = datetime.now(timezone.utc) """
data = "{\"timestamp\": \"%s\",\"timestamp2\": \"%s\"}" % (now, now)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,30s', 'timestamp2,30s']), data, OK_CODE) rules = RulesHelper()
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,30m']), data, OK_CODE) # This should work
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,1h']), data, OK_CODE) data = '[{"update status": "failure"}]'
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,3h']), data, OK_CODE) self.check_data(rules.dash_q(['(*).update status,failure']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,2d']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@30m']), data, WARNING_CODE) data = '[{"update (status)": "failure"}]'
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@1h']), data, CRITICAL_CODE) self.check_data(rules.dash_q(['(*).update (status),failure']), data, OK_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)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,-30m']), data, OK_CODE) data = '[{"update (((status)": "failure"}]'
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,-1h']), data, OK_CODE) self.check_data(rules.dash_q(['(*).update (((status),failure']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,-3h']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,-2d']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@-30m']), data, WARNING_CODE) data = '[{"update )status)": "failure"}]'
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@-1h']), data, CRITICAL_CODE) self.check_data(rules.dash_q(['(*).update )status),failure']), data, OK_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)
# Test 31 minute in the past. data = '[{"update (status": "failure"}]'
data = "{\"timestamp\": \"%s\"}" % (now - timedelta(minutes=31)) self.check_data(rules.dash_q(['(*).update (status),failure']), data, WARNING_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,30m']), data, WARNING_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)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,2d']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@30m']), data, OK_CODE) # Does not yet work
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@1h']), data, CRITICAL_CODE) # data = '{"update (foobar)": ["bar"]}'
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@3h']), data, WARNING_CODE) # self.check_data(rules.dash_q(['update (foobar)(0),bar']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@2d']), data, CRITICAL_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)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,-2d']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@-30m']), data, WARNING_CODE)
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)
# Test two hours and one minute in the past.
data = "{\"timestamp\": \"%s\"}" % (now - timedelta(hours=2) - timedelta(minutes=1))
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,30m']), data, WARNING_CODE)
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, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,2d']), 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, WARNING_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@2d']), data, CRITICAL_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)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,-2d']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@-30m']), data, WARNING_CODE)
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)
# Test one day and one minute in the past.
data = "{\"timestamp\": \"%s\"}" % (now - timedelta(days=1) - timedelta(minutes=1))
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,30m']), data, WARNING_CODE)
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, 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)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@2d']), data, CRITICAL_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)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,-2d']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@-30m']), data, WARNING_CODE)
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)
# Test two hours and one minute in the future.
data = "{\"timestamp\": \"%s\"}" % (now + timedelta(hours=2) + timedelta(minutes=1))
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)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,2d']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@30m']), data, WARNING_CODE)
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)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,-30m']), data, WARNING_CODE)
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, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,-2d']), 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, WARNING_CODE)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@-2d']), data, CRITICAL_CODE)
else:
data = "{\"timestamp\": \"2020-01-01\"}"
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,30m']), data, WARNING_CODE)
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)
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)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,@2d']), 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)
self.check_data(RulesHelper().dash_dash_key_time_critical(['timestamp,-2d']), data, OK_CODE)
self.check_data(RulesHelper().dash_dash_key_time(['timestamp,@-30m']), data, WARNING_CODE)
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)

View File

@@ -9,7 +9,6 @@ import os
sys.path.append('..') sys.path.append('..')
from check_http_json import debugPrint from check_http_json import debugPrint
from check_http_json import verbosePrint
class CLITest(unittest.TestCase): class CLITest(unittest.TestCase):
@@ -32,13 +31,10 @@ class CLITest(unittest.TestCase):
debugPrint(True, 'debug') debugPrint(True, 'debug')
mock_print.assert_called_once_with('debug') mock_print.assert_called_once_with('debug')
def test_verbose(self): def test_debugprint_pprint(self):
with mock.patch('builtins.print') as mock_print: with mock.patch('check_http_json.pprint') as mock_pprint:
verbosePrint(0, 3, 'verbose') debugPrint(True, 'debug', True)
mock_print.assert_not_called() mock_pprint.assert_called_once_with('debug')
verbosePrint(3, 3, 'verbose')
mock_print.assert_called_once_with('verbose')
def test_cli_without_params(self): def test_cli_without_params(self):

View File

@@ -95,37 +95,3 @@ class MainTest(unittest.TestCase):
main(args) main(args)
self.assertEqual(test.exception.code, 0) self.assertEqual(test.exception.code, 0)
@mock.patch('builtins.print')
def test_main_with_tls(self, mock_print):
args = ['-H', 'localhost',
'--ssl',
'--cacert',
'test/tls/ca-root.pem',
'--cert',
'test/tls/cert.pem',
'--key',
'test/tls/key.pem']
with self.assertRaises(SystemExit) as test:
main(args)
self.assertTrue('https://localhost' in str(mock_print.call_args))
self.assertEqual(test.exception.code, 3)
@mock.patch('builtins.print')
def test_main_with_tls_wrong_ca(self, mock_print):
args = ['-H', 'localhost',
'--ssl',
'--cacert',
'test/tls/key.pem',
'--cert',
'test/tls/cert.pem',
'--key',
'test/tls/key.pem']
with self.assertRaises(SystemExit) as test:
main(args)
self.assertTrue('Error loading SSL CA' in str(mock_print.call_args))
self.assertEqual(test.exception.code, 3)

View File

@@ -1,65 +0,0 @@
# Example Data for Testing
Example calls:
```bash
python check_http_json.py -H localhost:8080 -p data0.json -q "age,20"
UNKNOWN: Status UNKNOWN. Could not find JSON in HTTP body.
```
```bash
python check_http_json.py -H localhost:8080 -p data1.json -e date
WARNING: Status WARNING. Key date did not exist.
python check_http_json.py -H localhost:8080 -p data1.json -E age
OK: Status OK.
python check_http_json.py -H localhost:8080 -p data1.json -w "age,30"
OK: Status OK.
python check_http_json.py -H localhost:8080 -p data1.json -w "age,20"
WARNING: Status WARNING. Value (30) for key age was outside the range 0:20.
python check_http_json.py -H localhost:8080 -p data1.json -q "age,20"
WARNING: Status WARNING. Key age mismatch. 20 != 30
```
```bash
python check_http_json.py -H localhost:8080 -p data2.json -q "(1).id,123"
WARNING: Status WARNING. Key (1).id mismatch. 123 != 2
python check_http_json.py -H localhost:8080 -p data2.json -Y "(1).id,2"
CRITICAL: Status CRITICAL. Key (1).id match found. 2 == 2
python check_http_json.py -H localhost:8080 -p data2.json -E "(1).author"
OK: Status OK.
python check_http_json.py -H localhost:8080 -p data2.json -E "(1).pages"
CRITICAL: Status CRITICAL. Key (1).pages did not exist.
```
```bash
python check_http_json.py -H localhost:8080 -p data3.json -q "company.employees.(0).role,Developer"
OK: Status OK.
python check_http_json.py -H localhost:8080 -p data3.json -q "company.employees.(0).role,Dev"
WARNING: Status WARNING. Key company.employees.(0).role mismatch. Dev != Developer
python check_http_json.py -H localhost:8080 -p data3.json -q "company.employees.(0).role,Developer" "company.employees.(1).role,Designer"
OK: Status OK.
```
```bash
python check_http_json.py -H localhost:8080 -p data4.json -u "ratings(0),4.5"
OK: Status OK.
python check_http_json.py -H localhost:8080 -p data4.json -u "ratings(0),4.1"
UNKNOWN: Status UNKNOWN. Key ratings(0) mismatch. 4.1 != 4.5
```
```bash
python check_http_json.py -H localhost:8080 -p data5.json -q service1.status,True service2.status,True service3.status,True
OK: Status OK.
python check_http_json.py -H localhost:8080 -p data5.json -q "service1.status,True" -q "service2.status,True" -q "service3.status,False"
```

View File

@@ -1 +0,0 @@
No JSON

View File

@@ -1,5 +0,0 @@
{
"name": "John Doe",
"age": 30,
"city": "New York"
}

View File

@@ -1,17 +0,0 @@
[
{
"id": 1,
"title": "Book One",
"author": "Author One"
},
{
"id": 2,
"title": "Book Two",
"author": "Author Two"
},
{
"id": 3,
"title": "Book Three",
"author": "Author Three"
}
]

View File

@@ -1,18 +0,0 @@
{
"company": {
"name": "Tech Corp",
"location": "San Francisco",
"employees": [
{
"name": "Alice",
"role": "Developer"
},
{
"name": "Bob",
"role": "Designer"
}
]
},
"founded": 2010,
"industry": "Technology"
}

View File

@@ -1,13 +0,0 @@
{
"id": 123,
"active": true,
"tags": ["tech", "startup", "innovation"],
"details": {
"website": "https://example.com",
"contact": {
"email": "info@example.com",
"phone": "+1-234-567-890"
}
},
"ratings": [4.5, 4.7, 4.8]
}

View File

@@ -1,38 +0,0 @@
{
"service1": {
"status": true
},
"service2": {
"status": true,
"meta": {
"res": "PONG"
}
},
"service3": {
"status": true,
"meta": {
"took": 9,
"timed_out": false,
"_shards": {
"total": 0,
"successful": 0,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 10000,
"relation": "gte"
},
"max_score": null,
"hits": []
}
}
},
"service4": {
"status": true,
"meta": {
"status": "ok"
}
}
}

View File

@@ -1,7 +0,0 @@
services:
nginx:
image: nginx:1-alpine
ports:
- "8080:80"
volumes:
- ./:/usr/share/nginx/html

View File

@@ -1,21 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIUB6EZDl3ajJgJsoLzyC9DrOQQpKowDQYJKoZIhvcNAQEN
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yNDAzMTgwODE5MDhaGA8yMDUx
MDgwMzA4MTkwOFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALVxioj+6zw6Snr+B1JOivC8Of6YptVYym5ICiHX
wjpbSVVe+Py/P2LDb/uQ1QkAENlpvChFqSaRBZU5keXYS/DaFb2Evb2/zf5qIdWU
2ju8B5V13gXSeaNNetyEn1Ivvk0lOCQo2RwEZXuStpLS4Q32rkRBvkoL+RXDc1NX
c3RwcU1p9ybgBqAC7FYdV82sgHGugIrbzkjfFREJXp1AnqvKAdk39b1CnPxfmPZC
nzPPetfr3iivH8yVO5rodU/LDtQNph22JR94YvPB89QO+bZ9bw2GHtPdAKFew9HF
UxM1fmy381Mq2iS3KUq5vsC1jMe8slUAIFYEDzoPvOz+MpcCAwEAAaNTMFEwHQYD
VR0OBBYEFOmCb+JnMzX29hwgtXSzrN+m6mTDMB8GA1UdIwQYMBaAFOmCb+JnMzX2
9hwgtXSzrN+m6mTDMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQENBQADggEB
AAkTD8K4UO4uO4i6p2BCofbhVm9LYA0ulmLO8Uro0U491TeEDOQpgMFAK+b2gZIU
zvDHoCMn3UPVxHKl7XzDgLZVkYYEc2s9vArxk5vSnFmh3XvlDu2SO5gSLB2sf68A
2+Jz2x6z9tjWWdZCGJWU/iwMbG2Y3JMHyv1NMF8cyOclJaSDNBAwF5c5sdlGTLKb
WHGXzVqHSAFlGcHtQrcEKclHiuzw2G3LZzwghGk0XzxwvyKrnAEy408RY0mfNLtz
32KHqYtrip0RYlGWKP7/7q6i0D8muEFW/I4emFI0z0I/1CcYZZS8tQkWaPf/wCN0
llTD1kKJACsIMaqkkyy+EZM=
-----END CERTIFICATE-----

View File

@@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDDzCCAfcCFBOrBcHIH2x9xcUyUeDid0cvBxWtMA0GCSqGSIb3DQEBDQUAMEUx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjQwMzE4MDgxOTM1WhgPMjA1MTA4MDMw
ODE5MzVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEeMA0GCSqGSIb3DQEBAQUA
A4IBCwAwggEGAoH+ALuzyIhEATF5YyAOsXKfr2mttF2HyJvEscGcoA7YetT57bjJ
5lg944kc3QH/wTEdrGda3cwh3OXdUuyR7Wrm9jPw38hMArx/fWPkiISOShrUSHGd
Qyy2bT+zxBaUo+pomyrlqlgwGlbxuwTAlTSFcI+i7yXrckl2HRj40EW4FNsYpPzv
maxRXs0kg0J2JLTYF+fHlqlYbSX/hRU9wz2DYfkRSS0+OYJNSmqK0jayUsdZYurG
gbPwOCgQ0QxLLh7P8z4sOanRowqUzqTI77cyUugEJRyoi+LJr4r0EwMTBX3STgPh
S9B78+LNvwOrLrZFUhr144RfO9QPLnz0uWcCAwEAATANBgkqhkiG9w0BAQ0FAAOC
AQEAeIR21PfLzgpL7WDBE2KgwI78nVc1wY9nwoAxSBzHjS0Olve3r9MaVzAKn5ZS
xHtv8oroXjhTcczCIzxii6Imp6u0iIr3QVBIceofxrH3aWmICURcC9l+dIiY6sk9
Ct8P8gm/Erv2iF/7bnsARwDnw0f41fC9eXtHZ7WLRQrc7tLHpjL0Z7bT77ysQJVK
C1SWtBnq3afmwH3R1wVHENn0JVFQpBp+vqWU5KIlvjcz49yPU+aNODk1rJsHMlgS
x2iddwF31GNOxNfXtw8fdw4UDUl2wYoZ45w2e2pXt4pbN43m0Wys1eQZdk3tyR6G
AZOLP05073mLtbVlFRmcTdXIGg==
-----END CERTIFICATE-----

View File

@@ -1,27 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEqAIBADANBgkqhkiG9w0BAQEFAASCBJIwggSOAgEAAoH+ALuzyIhEATF5YyAO
sXKfr2mttF2HyJvEscGcoA7YetT57bjJ5lg944kc3QH/wTEdrGda3cwh3OXdUuyR
7Wrm9jPw38hMArx/fWPkiISOShrUSHGdQyy2bT+zxBaUo+pomyrlqlgwGlbxuwTA
lTSFcI+i7yXrckl2HRj40EW4FNsYpPzvmaxRXs0kg0J2JLTYF+fHlqlYbSX/hRU9
wz2DYfkRSS0+OYJNSmqK0jayUsdZYurGgbPwOCgQ0QxLLh7P8z4sOanRowqUzqTI
77cyUugEJRyoi+LJr4r0EwMTBX3STgPhS9B78+LNvwOrLrZFUhr144RfO9QPLnz0
uWcCAwEAAQKB/UQAYzMy5/fDkWzoxdLQFV3E56ZG7h+4x+lr0/Ts6rtD/KLIyqHH
ciqXgV4bCSPBK1eabOZqkjvYzhUU3R2wpRu2NWy8VPVzfrr07ZyQbDqCE+jNX6vQ
P44nk2/W0/e1hBmrcOZYLwK2utmC58tKWLhBAEENpq8EkpAcfF/1y9aRHKYwNnH7
vouoQibN5NTs5m8s0VyjRTDwRZja98eWnn5NfU3orqYO8fSlF6CyzDtoyhMco6zR
0skBgMzRYCRTuJpV+KekC7XFYyiJ6XZN5DKLbbqP6Y7YR8wjyFEruoGCS0mZH2H0
9/rhTsJram1B2zohXHPsHJGGGv12/7kCfw5C7yda+8Yv0NmRp1F+EJYb75SCAWIP
kzN/xvjP2bMKa6oSzU0DOga3Wc4ijJHDaND8rqdPqQe3zXFr1nPdBrybLSJ6k5CN
4Dd6ENJWVWino0L460kpLtlBG6TsgmB8bkwhjWVE6Vgt4Vila+a3TGRXeniaRzdw
icNOtMrjYlUCfw0pWEvO2uFq0DbNZbmzC2j5ClFcU96CAl4AqKG2PiGnuSy9TKVZ
c5OiXFmyoig7v4LJzaKLSqVIN4hVBU80/MlhvG+dpeimvLaQKNtlZQethIs5hXlB
R1XfaPhq6BQiYmQ3tufyS/0Es2OY+Cs3LU1uDB8qVzonlmnIi69OwMsCfwRPISfJ
C+4UIIy8v8uVxbk1c6xxo61Xe2jCIQKo+uRoL6PRzoqIgQ3qdI4eTk70tkT/NF6F
aVNVrBOrO78Cd7ihQn/6fX/d/nOExHRpdaELlf70a1NNyEQIsiug8rvonQMP2ENT
ERZ9tmssgG/Tzpc6/1xVcVNFA7spmuL61YkCfwnu2zGTc0PO7kd96rkktIbL9YqD
6NQ0QH8bdildtjSGNc3bLB5ajUytq48Sryk4NogJr8Vt5K8q+qZMrE4kCmgd+C4w
x4b3V9Ncp0k1k/MgdLjyd5aUurbHfpyFapPPg3xpRAR3q/vP8WdIintrECiw1jsr
JFvChtVdQnbTM9MCfw41RcjNwCaIG+uXc8bD6Yf+NyXD8zP6ZDywmBlkMWlGSzx4
xM8J+wQiQsNWthDBbF7inJc+lbtJiEe4YOPkbjCYVZRHribL65HKJlEUv6M9bvQo
3P1DS5tDrwo6z9UPs4tD1SgF9fDu/xA7fwPF1RTvuW07MhFJWlDo4FSWS9c=
-----END PRIVATE KEY-----