54 Commits
v2.1 ... master

Author SHA1 Message Date
Markus Opolka
027af5f3f5 Merge pull request #104 from drewkerrigan/fix/multiple-flags
Fix the use of multiple similar CLI flags
2025-06-06 12:21:22 +02:00
Markus Opolka
6eef16b85b Fix the use of multiple similar CLI flags 2025-06-06 12:17:51 +02:00
Markus Opolka
ccf05d469a Merge pull request #101 from drewkerrigan/bump-ci
Bump GH Actions
2025-04-11 15:53:39 +02:00
Markus Opolka
115acc06fd Bump GH Actions 2025-04-11 15:52:16 +02:00
Markus Opolka
164632faa5 Bump dev requirements 2025-04-11 15:51:07 +02:00
Markus Opolka
039cb0adb6 Update example configuration 2025-04-11 15:45:06 +02:00
Markus Opolka
4e0d4e873b Merge pull request #99 from drewkerrigan/release-2-3-0
Release 2.3.0
2025-04-11 15:31:43 +02:00
Markus Opolka
186f081cd7 Bump version 2025-04-11 15:30:36 +02:00
Markus Opolka
e15f0f01ed Update example data 2025-04-11 14:51:54 +02:00
Markus Opolka
b61789e4a4 Merge pull request #100 from drewkerrigan/feature/no-json-state
Add cli invalid-json-state to change exit code for invalid JSON
2025-04-11 12:57:26 +02:00
Markus Opolka
c6daa09ba2 Adds a CLI flag invalid-json-state to change exit code for invalid JSON 2025-04-10 16:54:53 +02:00
Markus Opolka
3a1e7d90d0 Mention proxy variables in README 2025-04-09 17:18:34 +02:00
Markus Opolka
afb2ef7b88 Update README
- Improve structure a bit, moving the installation first and usage second
2025-04-09 17:15:19 +02:00
Markus Opolka
2a6d88bc39 Add testdata to simplify integration testing 2025-04-09 16:57:17 +02:00
Markus Opolka
2dbb38512f Merge pull request #98 from drewkerrigan/fix/improve-error-handling
Improve error handling
2025-04-09 16:46:22 +02:00
Markus Opolka
9ff11308be add unreachable-state option to icinga2 config (#97) 2025-04-09 13:01:19 +02:00
Dirk
c634ae8bb5 add unreachable-state option to icinga2 config 2025-04-09 11:51:35 +02:00
Markus Opolka
d3a2f3ed9e Improve error handling
- Added another try-catch around the CLI Rules parsing
   to make sure that users get a clean exit code and error messages
2024-07-29 11:11:41 +02:00
Markus Opolka
9d344f5a7a Add multiple key example to README 2024-07-29 09:45:30 +02:00
Markus Opolka
5c4a955abd Fix object comparision 2024-06-28 14:40:19 +02:00
aro-lew
b920a65afd Feature: Add Timestamp checks (#87) 2024-06-28 14:39:57 +02:00
Markus Opolka
d9efd1d858 Merge pull request #91 from drewkerrigan/chore/readme
Update README
2024-05-16 10:24:10 +02:00
Markus Opolka
e72030a087 Update README
- Update CLI options
2024-05-16 10:22:33 +02:00
Markus Opolka
6b51e1bb06 Merge pull request #90 from drewkerrigan/chore/update-makefile
Update makefile
2024-05-16 10:16:30 +02:00
Markus Opolka
3f73984f6b Change makefile to use python3
- Introduces a variable to override this if necessary
2024-05-16 10:14:38 +02:00
Markus Opolka
09a7ec080c Update Python versions in GitHub Actions 2024-05-16 10:14:28 +02:00
Markus Opolka
1f52898d10 Merge pull request #88 from drewkerrigan/release/v2-2-0
Bump release to v2.2.0
2024-05-14 17:02:54 +02:00
Markus Opolka
27936784c4 Bump release to v2.2.0 2024-05-14 17:01:53 +02:00
Markus Opolka
fa157753ce Merge pull request #86 from drewkerrigan/feature/verbose-http
Add flag to increase verbosity and flag to override unreachable state
2024-05-14 16:55:46 +02:00
Markus Opolka
0aceabfe91 Add verbose flag and function that can be used to enhance output more precisely
- Before we only had a boolean debug flag, good for debugging errors.
   The verbose flag can be used more precisely (`-v -vvv`) to specify when
   something should be printed. This is useful for adding more output whilst avoiding
   full debug output that contains secrets.
2024-04-09 14:08:02 +02:00
Markus Opolka
4fbb0c828a Add flag to override URL unreachable state
- I refactored the Nagios helper a bit to integrate this functionality a bit simpler.
   Before we had distinct methods on the helper that added warn,crit,unko message, now
   there's a general method that takes an int as parameter.
   This way we avoid if-else structures for the new functionality.
2024-04-09 14:07:45 +02:00
Markus Opolka
e96bba0eb8 Refactor for a leaner main function
- Also added tests for TLS options
2024-04-09 14:07:36 +02:00
Markus Opolka
d9ee817dfc Update dev-requirements 2024-04-09 14:07:29 +02:00
Markus Opolka
ce9c5fdada Merge pull request #85 from drewkerrigan/extend-tests
Extend tests for array syntax
2024-03-22 15:52:10 +01:00
Markus Opolka
27c710b2ea Extend tests for array syntax 2024-03-22 15:45:25 +01:00
Markus Opolka
dddf8432d6 Merge pull request #80 from mho21/master
disabled check_hostname to prevent error message when setting CERT_NONE
2022-10-04 16:26:06 +02:00
Markus Hof
739c093702 disabled check_hostname to prevent error message when setting CERT_NONE 2022-10-04 16:04:12 +02:00
Markus Opolka
46271c961b Bump version to 2.1.2 2022-09-15 15:25:38 +02:00
Markus Opolka
49b338bdb6 Merge pull request #79 from drewkerrigan/feature/http-method
Add CLI Flag to change HTTP method
2022-09-15 15:22:48 +02:00
Markus Opolka
9f41fc491e Add CLI flag to change HTTP method 2022-09-09 17:28:35 +02:00
Markus Opolka
3a22b712ab Fix deprecation of PROTOCOL_TLS 2022-09-09 17:26:23 +02:00
Markus Opolka
9626fc4464 Merge pull request #78 from drewkerrigan/docs/update-repo
Update Makefile and Workflows
2022-09-08 10:09:43 +02:00
Markus Opolka
c54a0040a0 Update pylint config 2022-09-08 10:08:39 +02:00
Markus Opolka
ffd96dd59f Update GitHub Workflow 2022-09-08 10:04:20 +02:00
Markus Opolka
0572c2f494 Update Makefile
- Use python from venv
2022-09-08 10:01:23 +02:00
Markus Opolka
2e6eaeea59 Merge pull request #77 from K0nne/patch-1
fix missing type conversion for --data
2022-09-08 09:59:28 +02:00
K0nne
428a5a6d3a fix missing type conversion for --data
The parameter --data is handled as type string, but the method urlopen() only accepts the datatype byte.
Before this fix you will get: "TypeError: POST data should be  bytes, an iterable of bytes, or a filer object. It cannot be of type str."
This PR solves this.
2022-07-27 13:30:25 +02:00
Markus Opolka
e3ac06864d Merge pull request #68 from ccin2p3/feature/load_default_ca_certs
[TLS] Always load system default C.A files
2021-01-22 10:39:33 +01:00
Rémi Ferrand
63542f3226 If TLS is enabled, context now loads the system default C.A files
* This allows system wide deployed C.A to be used without any further
  configuration.
2021-01-21 12:02:41 +01:00
Markus Opolka
cdb2474ee0 Update README 2020-11-24 20:27:40 +01:00
Markus Opolka
2821a1ae66 Merge pull request #66 from drewkerrigan/array-bug
Fix conditional check on empty data.
2020-09-14 10:36:14 +02:00
Markus Opolka
831bfdf97b Merge pull request #65 from alesc/patch-1
Update icinga2_check_command_definition.conf
2020-09-12 08:21:01 +02:00
alesc
f612277772 Update icinga2_check_command_definition.conf
small error in icinga2 conf definition, --key_metricS does not exist --key_metric does.
2020-09-11 10:18:02 +02:00
Markus Opolka
1f440e0ff5 Fix conditional check on empty data.
Fixes issue #64
2020-07-15 08:07:16 +02:00
23 changed files with 882 additions and 225 deletions

View File

@@ -1,27 +1,31 @@
name: CI name: CI
on: [push, pull_request] on:
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.6, 3.7, 3.8] python-version: [3.8, 3.11, 3.12]
name: GitHub Action name: GitHub Action
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install dependencies
run: |
python -m pip install -r requirements-dev.txt
- name: Lint - name: Lint
run: | run: |
pip3 install --upgrade pip wheel setuptools make lint
pip3 install pylint
python3 -m pylint check_http_json.py
- name: Unit Test - name: Unit Test
run: | run: |
python3 -m unittest discover make test
- name: Coverage - name: Coverage
run: | run: |
pip3 install coverage make coverage
python3 -m coverage run -m unittest discover
python3 -m coverage report -m --include check_http_json.py

1
.gitignore vendored
View File

@@ -25,6 +25,7 @@ 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,5 +1,21 @@
# pylint config # pylint config
[MESSAGES CONTROL]
disable=line-too-long, redefined-outer-name, too-many-arguments, too-many-instance-attributes, fixme, invalid-name, superfluous-parens, missing-function-docstring, missing-module-docstring, multiple-imports, no-else-return, too-many-return-statements, too-many-branches, too-many-statements
[MASTER] [MASTER]
ignore-patterns=^test.* ignore-patterns=^test.*
[MESSAGES CONTROL]
disable=fixme,
consider-using-f-string,
invalid-name,
line-too-long,
missing-function-docstring,
missing-module-docstring,
multiple-imports,
no-else-return,
redefined-outer-name,
superfluous-parens,
too-many-locals,
too-many-arguments,
too-many-branches,
too-many-instance-attributes,
too-many-return-statements,
too-many-statements

11
Makefile Normal file
View File

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

244
README.md
View File

@@ -4,15 +4,50 @@
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.
## Links ## Installation
* [CLI Usage](#cli-usage) Requirements:
* [Examples](#examples)
* [Riak Stats](docs/RIAK.md)
* [Docker](docs/DOCKER.md)
* [Nagios Installation](#nagios-installation)
## CLI Usage * Python 3.8+
### 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:
@@ -38,14 +73,17 @@ 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.0.0 (2020-03-22) Version: 2.2.0 (2024-05-14)
optional arguments: options:
-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
-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 --cacert CACERT SSL CA certificate
--cert CERT SSL client certificate --cert CERT SSL client certificate
@@ -54,63 +92,62 @@ optional arguments:
-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 JSON Field separator, defaults to "."; Select element in an array with "(" ")"
in an array with "(" ")" -F VALUE_SEPARATOR, --value_separator VALUE_SEPARATOR
-F SEPARATOR, --value_separator SEPARATOR JSON Value separator, defaults to ":"
JSON Value separator, defaults to ":"; -w [KEY_THRESHOLD_WARNING ...], --warning [KEY_THRESHOLD_WARNING ...]
-w [KEY_THRESHOLD_WARNING [KEY_THRESHOLD_WARNING ...]], --warning [KEY_THRESHOLD_WARNING [KEY_THRESHOLD_WARNING ...]] Warning threshold for these values (key1[>alias],WarnRange key2[>alias],WarnRange). WarnRange is in the format
Warning threshold for these values [@]start:end, more information at nagios-plugins.org/doc/guidelines.html.
(key1[>alias],WarnRange key2[>alias],WarnRange). -c [KEY_THRESHOLD_CRITICAL ...], --critical [KEY_THRESHOLD_CRITICAL ...]
WarnRange is in the format [@]start:end, more Critical threshold for these values (key1[>alias],CriticalRange key2[>alias],CriticalRange. CriticalRange is in
information at nagios-plugins.org/doc/guidelines.html. the format [@]start:end, more information at nagios-plugins.org/doc/guidelines.html.
-c [KEY_THRESHOLD_CRITICAL [KEY_THRESHOLD_CRITICAL ...]], --critical [KEY_THRESHOLD_CRITICAL [KEY_THRESHOLD_CRITICAL ...]] -e [KEY_LIST ...], --key_exists [KEY_LIST ...]
Critical threshold for these values Checks existence of these keys to determine status. Return warning if key is not present.
(key1[>alias],CriticalRange -E [KEY_LIST_CRITICAL ...], --key_exists_critical [KEY_LIST_CRITICAL ...]
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_VALUE_LIST ...]], --key_equals [KEY_VALUE_LIST [KEY_VALUE_LIST ...]] -q [KEY_VALUE_LIST ...], --key_equals [KEY_VALUE_LIST ...]
Checks equality of these keys and values 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
-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 equality check (key,value1:value2). Return warning if the key is older
fails than the value (ex.: 30s,10m,2h,3d,...).
-Q [KEY_VALUE_LIST_CRITICAL [KEY_VALUE_LIST_CRITICAL ...]], --key_equals_critical [KEY_VALUE_LIST_CRITICAL [KEY_VALUE_LIST_CRITICAL ...]] With at it return warning if the key is jounger
Same as -q but return critical if equality check than the value (ex.: @30s,@10m,@2h,@3d,...).
fails. With Minus you can shift the time in the future.
-u [KEY_VALUE_LIST_UNKNOWN [KEY_VALUE_LIST_UNKNOWN ...]], --key_equals_unknown [KEY_VALUE_LIST_UNKNOWN [KEY_VALUE_LIST_UNKNOWN ...]] --key_time_critical [KEY_TIME_LIST_CRITICAL ...],
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_VALUE_LIST_NOT ...]], --key_not_equals [KEY_VALUE_LIST_NOT [KEY_VALUE_LIST_NOT ...]] -y [KEY_VALUE_LIST_NOT ...], --key_not_equals [KEY_VALUE_LIST_NOT ...]
Checks equality of these keys and values Checks equality of these keys and values (key[>alias],value key2,value2) to determine status. Multiple key values
(key[>alias],value key2,value2) to determine status. can be delimited with colon (key,value1:value2). Return warning if equality check succeeds
Multiple key values can be delimited with colon -Y [KEY_VALUE_LIST_NOT_CRITICAL ...], --key_not_equals_critical [KEY_VALUE_LIST_NOT_CRITICAL ...]
(key,value1:value2). Return warning if equality check Same as -q but return critical if equality check succeeds.
succeeds -m [METRIC_LIST ...], --key_metric [METRIC_LIST ...]
-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 ...]] Gathers the values of these keys (key[>alias], UnitOfMeasure,WarnRange,CriticalRange,Min,Max) for Nagios
Same as -q but return critical if equality check performance data. More information about Range format and units of measure for nagios can be found at nagios-
succeeds. plugins.org/doc/guidelines.html Additional formats for this parameter are: (key[>alias]),
-m [METRIC_LIST [METRIC_LIST ...]], --key_metric [METRIC_LIST [METRIC_LIST ...]] (key[>alias],UnitOfMeasure), (key[>alias],UnitOfMeasure,WarnRange, CriticalRange).
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
@@ -174,6 +211,22 @@ optional arguments:
] ]
} }
**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**:
@@ -202,54 +255,49 @@ optional arguments:
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
### 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,9 +6,10 @@ import json
import argparse import argparse
import sys import sys
import ssl import ssl
from pprint import pprint import traceback
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 = \
""" """
@@ -24,8 +25,8 @@ WARNING_CODE = 1
CRITICAL_CODE = 2 CRITICAL_CODE = 2
UNKNOWN_CODE = 3 UNKNOWN_CODE = 3
__version__ = '2.0.0' __version__ = '2.3.0'
__version_date__ = '2020-03-22' __version_date__ = '2025-04-11'
class NagiosHelper: class NagiosHelper:
""" """
@@ -69,20 +70,19 @@ class NagiosHelper:
code = UNKNOWN_CODE code = UNKNOWN_CODE
return code return code
def append_warning(self, warning_message): def append_message(self, code, msg):
self.warning_message += warning_message if code > 2 or code < 0:
self.unknown_message += msg
def append_critical(self, critical_message): if code == 1:
self.critical_message += critical_message self.warning_message += msg
if code == 2:
def append_unknown(self, unknown_message): self.critical_message += msg
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_warning(warning_message) self.append_message(WARNING_CODE, warning_message)
self.append_critical(critical_message) self.append_message(CRITICAL_CODE, critical_message)
class JsonHelper: class JsonHelper:
@@ -152,7 +152,7 @@ class JsonHelper:
(Element.Key.NestedKey). Returns (None, 'not_found') if not found (Element.Key.NestedKey). Returns (None, 'not_found') if not found
""" """
if temp_data: if temp_data != '':
data = temp_data data = temp_data
else: else:
data = self.data data = self.data
@@ -236,11 +236,13 @@ 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)
@@ -332,6 +334,72 @@ 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:
@@ -340,6 +408,8 @@ 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
@@ -354,6 +424,8 @@ 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
@@ -423,6 +495,9 @@ 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',
@@ -430,6 +505,8 @@ def parseArgs(args):
help='remote host to query') help='remote host to query')
parser.add_argument('-k', '--insecure', action='store_true', parser.add_argument('-k', '--insecure', action='store_true',
help='do not check server SSL certificate') 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', parser.add_argument('-V', '--version', action='store_true',
help='print version of this plugin') help='print version of this plugin')
parser.add_argument('--cacert', parser.add_argument('--cacert',
@@ -442,6 +519,10 @@ 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',
@@ -470,21 +551,36 @@ 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', parser.add_argument('-E', '--key_exists_critical', dest='key_list_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', nargs='*', parser.add_argument('-q', '--key_equals', dest='key_value_list',
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', parser.add_argument('-Q', '--key_equals_critical', dest='key_value_list_critical',
dest='key_value_list_critical', nargs='*', action='extend',
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
@@ -500,7 +596,9 @@ 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', nargs='*', parser.add_argument('-m', '--key_metric', dest='metric_list',
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
@@ -514,17 +612,93 @@ def parseArgs(args):
return parser.parse_args(args) return parser.parse_args(args)
def debugPrint(debug_flag, message, pretty_flag=False): def debugPrint(debug_flag, message):
""" """
Print debug messages if -d (debug_flat ) is set. Print debug messages if -d 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):
"""
Prepare TLS Context
"""
nagios = NagiosHelper()
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
if args.insecure:
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
else:
context.verify_mode = ssl.CERT_OPTIONAL
context.load_default_certs()
if args.cacert:
try:
context.load_verify_locations(args.cacert)
except ssl.SSLError:
nagios.append_message(UNKNOWN_CODE, 'Error loading SSL CA cert "%s"!' % args.cacert)
if args.cert:
try:
context.load_cert_chain(args.cert, keyfile=args.key)
except ssl.SSLError:
if args.key:
nagios.append_message(UNKNOWN_CODE, 'Error loading SSL cert. Make sure key "%s" belongs to cert "%s"!' % (args.key, args.cert))
else:
nagios.append_message(UNKNOWN_CODE, 'Error loading SSL cert. Make sure "%s" contains the key as well!' % (args.cert))
if nagios.getCode() != OK_CODE:
print(nagios.getMessage())
sys.exit(nagios.getCode())
return context
def make_request(args, url, context):
"""
Performs the actual request to the given URL
"""
req = urllib.request.Request(url, method=args.method)
req.add_header("User-Agent", "check_http_json")
if args.auth:
authbytes = str(args.auth).encode()
base64str = base64.encodebytes(authbytes).decode().replace('\n', '')
req.add_header('Authorization', 'Basic %s' % base64str)
if args.headers:
headers = json.loads(args.headers)
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)
return response.read()
def main(cliargs): def main(cliargs):
""" """
@@ -541,40 +715,7 @@ def main(cliargs):
if args.ssl: if args.ssl:
url = "https://%s" % args.host url = "https://%s" % args.host
context = prepare_context(args)
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
if args.insecure:
context.verify_mode = ssl.CERT_NONE
else:
context.verify_mode = ssl.CERT_OPTIONAL
if args.cacert:
try:
context.load_verify_locations(args.cacert)
except ssl.SSLError:
nagios.append_unknown(
'Error loading SSL CA cert "%s"!'
% args.cacert)
if args.cert:
try:
context.load_cert_chain(args.cert, keyfile=args.key)
except ssl.SSLError:
if args.key:
nagios.append_unknown(
'Error loading SSL cert. Make sure key "%s" belongs to cert "%s"!'
% (args.key, args.cert))
else:
nagios.append_unknown(
'Error loading SSL cert. Make sure "%s" contains the key as well!'
% (args.cert))
if nagios.getCode() != OK_CODE:
print(nagios.getMessage())
sys.exit(nagios.getCode())
else: else:
url = "http://%s" % args.host url = "http://%s" % args.host
if args.port: if args.port:
@@ -586,59 +727,50 @@ def main(cliargs):
json_data = '' json_data = ''
try: try:
req = urllib.request.Request(url) # Requesting the data from the URL
req.add_header("User-Agent", "check_http_json") json_data = make_request(args, url, context)
if args.auth:
authbytes = str(args.auth).encode()
base64str = base64.encodebytes(authbytes).decode().replace('\n', '')
req.add_header('Authorization', 'Basic %s' % base64str)
if args.headers:
headers = json.loads(args.headers)
debugPrint(args.debug, "Headers:\n %s" % headers)
for header in headers:
req.add_header(header, headers[header])
if args.timeout and args.data:
response = urllib.request.urlopen(req, timeout=args.timeout,
data=args.data, context=context)
elif args.timeout:
response = urllib.request.urlopen(req, timeout=args.timeout,
context=context)
elif args.data:
response = urllib.request.urlopen(req, data=args.data, context=context)
else:
response = urllib.request.urlopen(req, context=context)
json_data = response.read()
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:
nagios.append_unknown(" HTTPError[%s], url:%s" % (str(e.code), url)) exit_code = args.invalid_json_state
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:
nagios.append_critical(" URLError[%s], url:%s" % (str(e.reason), url)) # Some users might prefer another exit code if the URL wasn't reached
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:
nagios.append_unknown(" Parser error: %s" % str(e)) exit_code = args.invalid_json_state
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: else:
debugPrint(args.debug, 'json:') verbosePrint(args.verbose, 1, json.dumps(data, indent=2))
debugPrint(args.debug, data, True)
# Apply rules to returned JSON data try:
# Applying rules to returned JSON data
processor = JsonRuleProcessor(data, args) processor = JsonRuleProcessor(data, args)
nagios.append_warning(processor.checkWarning()) nagios.append_message(WARNING_CODE, processor.checkWarning())
nagios.append_critical(processor.checkCritical()) nagios.append_message(CRITICAL_CODE, processor.checkCritical())
nagios.append_metrics(processor.checkMetrics()) nagios.append_metrics(processor.checkMetrics())
nagios.append_unknown(processor.checkUnknown()) nagios.append_message(UNKNOWN_CODE, 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,4 +1,6 @@
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" ]
@@ -53,6 +55,13 @@ 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 '(' ')'"
@@ -64,42 +73,62 @@ 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_metrics" = { "--key_metric" = {
value = "$http_json_key_metrics$" 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,9 +0,0 @@
.PHONY: lint test coverage
lint:
python3 -m pylint check_http_json.py
test:
python3 -m unittest discover
coverage:
python3 -m coverage run -m unittest discover
python3 -m coverage report -m --include check_http_json.py

2
requirements-dev.txt Normal file
View File

@@ -0,0 +1,2 @@
coverage==7.8.0
pylint==3.3.6

View File

@@ -28,6 +28,8 @@ 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
@@ -72,6 +74,13 @@ 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):
""" """
@@ -84,10 +93,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_warning(processor.checkWarning()) nagios.append_message(WARNING_CODE, processor.checkWarning())
nagios.append_critical(processor.checkCritical()) nagios.append_message(CRITICAL_CODE, processor.checkCritical())
nagios.append_metrics(processor.checkMetrics()) nagios.append_metrics(processor.checkMetrics())
nagios.append_unknown(processor.checkUnknown()) nagios.append_message(UNKNOWN_CODE, processor.checkUnknown())
self.assertEqual(code, nagios.getCode()) self.assertEqual(code, nagios.getCode())
def test_metrics(self): def test_metrics(self):
@@ -110,6 +119,14 @@ class UtilTest(unittest.TestCase):
self.check_data(RulesHelper().dash_U(['metric,0']), self.check_data(RulesHelper().dash_U(['metric,0']),
'{"metric": 3}', UNKNOWN_CODE) '{"metric": 3}', UNKNOWN_CODE)
def test_array(self):
self.check_data(RulesHelper().dash_q(['foo(0),bar']),
'{"foo": ["bar"]}', OK_CODE)
self.check_data(RulesHelper().dash_q(['foo(0),foo']),
'{"foo": ["bar"]}', WARNING_CODE)
self.check_data(RulesHelper().dash_Q(['foo(1),bar']),
'{"foo": ["bar"]}', CRITICAL_CODE)
def test_exists(self): def test_exists(self):
self.check_data(RulesHelper().dash_e(['nothere']), self.check_data(RulesHelper().dash_e(['nothere']),
'{"metric": 5}', WARNING_CODE) '{"metric": 5}', WARNING_CODE)
@@ -294,3 +311,140 @@ class UtilTest(unittest.TestCase):
# This should throw an error # This should throw an error
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):
if sys.version_info[1] >= 11:
# Test current timestamp.
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)
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, 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 31 minute in the past.
data = "{\"timestamp\": \"%s\"}" % (now - timedelta(minutes=31))
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)
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, 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,6 +9,7 @@ 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):
@@ -31,10 +32,13 @@ 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_debugprint_pprint(self): def test_verbose(self):
with mock.patch('check_http_json.pprint') as mock_pprint: with mock.patch('builtins.print') as mock_print:
debugPrint(True, 'debug', True) verbosePrint(0, 3, 'verbose')
mock_pprint.assert_called_once_with('debug') mock_print.assert_not_called()
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,3 +95,37 @@ 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)

65
test/testdata/README.md vendored Normal file
View File

@@ -0,0 +1,65 @@
# 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"
```

1
test/testdata/data0-invalid.json vendored Normal file
View File

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

5
test/testdata/data1.json vendored Normal file
View File

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

17
test/testdata/data2.json vendored Normal file
View File

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

18
test/testdata/data3.json vendored Normal file
View File

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

13
test/testdata/data4.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"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]
}

38
test/testdata/data5.json vendored Normal file
View File

@@ -0,0 +1,38 @@
{
"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"
}
}
}

7
test/testdata/docker-compose.yml vendored Normal file
View File

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

21
test/tls/ca-root.pem Normal file
View File

@@ -0,0 +1,21 @@
-----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-----

19
test/tls/cert.pem Normal file
View File

@@ -0,0 +1,19 @@
-----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-----

27
test/tls/key.pem Normal file
View File

@@ -0,0 +1,27 @@
-----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-----