64 Commits
v1.4 ... v2.1.2

Author SHA1 Message Date
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
Markus Opolka
c23ebac77a Merge pull request #63 from drewkerrigan/v2-1
Release 2.1
2020-07-03 09:58:55 +02:00
Markus Opolka
a014944981 Merge pull request #62 from drewkerrigan/key-equals-empty
Add handling of empty JSON return values
2020-07-03 09:04:53 +02:00
Markus Opolka
47a37556ba Merge branch 'v2-1' into key-equals-empty 2020-07-03 09:03:21 +02:00
Markus Opolka
41279cad2c Merge pull request #60 from drewkerrigan/http-json
Parse JSON on HTTPError, if JSON in response
2020-07-03 09:02:14 +02:00
Markus Opolka
f7c0472cdc Add JSON parsing on HTTPError
- Only if response contains JSON
2020-07-02 11:35:23 +02:00
Markus Opolka
25fb340bbb Add handling of empty JSON return values
- Will now throw a CRITICAL
2020-06-26 10:39:37 +02:00
Markus Opolka
47bdea7fc5 Add spaces to debug output 2020-06-26 10:31:20 +02:00
Markus Opolka
866a12ea07 Add JSON parsing on HTTPError
- Only if response contains JSON
2020-06-19 14:26:59 +02:00
Markus Opolka
d1e585b2dd Adjust pylint for new function 2020-06-19 13:16:07 +02:00
Markus Opolka
941afeed89 Move main entrypoint to own function for simpler testing 2020-06-19 13:12:51 +02:00
Markus Opolka
b9a583f281 Add Makefile and requirements.txt for easier testing 2020-06-19 13:12:48 +02:00
Markus Opolka
4c89a8a93d Update README 2020-04-03 11:53:00 +02:00
Markus Opolka
73557b3657 Merge pull request #58 from bb-Ricardo/next-release
added icinga2 command definitions
2020-04-03 11:50:29 +02:00
Ricardo Bartels
aad2376ac0 added missing cli args for V2.0 2020-04-02 08:39:42 +02:00
Markus Opolka
219e99386c Merge pull request #57 from drewkerrigan/v2.0
Release V2.0
2020-03-31 18:15:06 +02:00
Markus Opolka
0cbbf41b9c Update README 2020-03-23 09:11:22 +01:00
Markus Opolka
dd952fd571 Replace deprecated encodestring
- Fixes #56
2020-03-18 08:47:27 +01:00
Markus Opolka
83ee5062f5 [wip] Add pylint and fix pylint issues 2020-03-18 08:18:07 +01:00
Markus Opolka
c90b0323f5 Show returned JSON in OK Status when performance data is requested 2020-03-18 07:41:23 +01:00
Markus Opolka
1ac160e8c2 Add boilerplate for CLI tests 2020-03-18 07:41:23 +01:00
Markus Opolka
6fc41612c4 Add unittest for debugprint 2020-03-18 07:41:23 +01:00
Markus Opolka
f567c1ca0c Add unittest for metric key alias 2020-03-18 07:41:23 +01:00
Markus Opolka
2c98e840e8 Extend unittest coverage 2020-03-18 07:41:18 +01:00
Markus Opolka
1a9e1e9048 Add unittest for NagiosHelper 2020-03-15 09:45:18 +01:00
Markus Opolka
4f1d29dc7e Merge pull request #55 from marxin/fix-python38-warnings
Fix new Python3.8 warnings:
2020-03-12 14:17:34 +01:00
Martin Liska
404890d918 Fix new Python3.8 warnings:
./check_http_json.py:186: SyntaxWarning: "is" with a literal. Did you mean "=="?
  if elemData is (None, 'not_found'):
./check_http_json.py:189: SyntaxWarning: "is not" with a literal. Did you mean "!="?
  if subElemKey is not '':
2020-03-12 09:37:53 +01:00
Markus Opolka
e7cf7ca8fb Merge pull request #54 from drewkerrigan/fix-issue-43
Add value_separator option to specify how JSON values are being split
2020-03-09 20:39:00 +01:00
Markus Opolka
71cbd98e79 Add value_separator option to specify how JSON values are being split
- Fixes issue 43
2020-03-08 11:28:27 +01:00
Markus Opolka
5c416cd0c0 Merge pull request #53 from drewkerrigan/fix-issue-34
Add boundary check for SubArrayElement function
2020-03-05 19:51:55 +01:00
Markus Opolka
e4801227bf Add boundary check for SubArrayElement function
- Fixes Issue 34
2020-03-05 10:05:32 +01:00
Markus Opolka
b7c0b0595e Add unittest for argsparse 2020-03-03 12:12:52 +01:00
Markus Opolka
375da5d605 Add test case for key_value_list_unknown 2020-03-03 11:59:36 +01:00
Markus Opolka
95912246a2 Add and format some doc_strings 2020-03-03 11:44:55 +01:00
Markus Opolka
ba9d9b1c39 Use Python3 in GitHub Action 2020-03-03 10:04:03 +01:00
Markus Opolka
3f81e32b29 Add CI Badge to README 2020-03-03 10:02:51 +01:00
Markus Opolka
f97759f1bd Add Coverage report 2020-03-03 10:02:51 +01:00
Markus Opolka
e95daad8ff Add GitHub Action for Unit Test 2020-03-03 10:02:49 +01:00
Markus Opolka
174686a980 Move test to separat file 2020-03-03 09:48:10 +01:00
Markus Opolka
24889384b0 Add gitignore file 2020-02-16 09:55:58 +01:00
Markus Opolka
21f48681c9 Merge pull request #50 from marxin/port-to-python3
Port to Python3.
2020-02-16 09:37:00 +01:00
Markus Opolka
2196dba761 Merge pull request #51 from marxin/document-asterisk
Document syntax of array selector: (*).field_name.
2020-02-03 16:06:54 +01:00
marxin
c2435a8cbf Document syntax of array selector: (*).field_name. 2020-02-01 21:47:08 +01:00
Martin Liska
2289fb2af3 Port to Python3 (#48).
I used 2to3 script and then I clean up result of the conversion.
2020-01-28 12:13:30 +01:00
Markus Opolka
2541223cde Merge pull request #49 from drewkerrigan/fix-ssl
Fix --insecure option
2020-01-28 10:52:11 +01:00
Markus Opolka
209aaef041 Fix inconsistent use of tabs/spaces 2020-01-28 10:42:22 +01:00
Markus Opolka
65c3bd2a25 Set default context variable to -k --insecure option 2020-01-28 10:41:29 +01:00
Ricardo Bartels
26a1b3dbe8 added icinga2 command definitions 2019-05-10 11:21:34 +02:00
15 changed files with 923 additions and 277 deletions

26
.github/workflows/unittest.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: CI
on: [push, pull_request]
jobs:
gitHubActionForPytest:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3,9]
name: GitHub Action
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install dependencies
run: |
python -m pip install -r requirements-dev.txt
- name: Lint
run: |
make lint
- name: Unit Test
run: |
make test
- name: Coverage
run: |
make coverage

64
.gitignore vendored Normal file
View File

@@ -0,0 +1,64 @@
#Emacs
\#*
.\#*
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
.venv/
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
#Ipython Notebook
.ipynb_checkpoints

21
.pylintrc Normal file
View File

@@ -0,0 +1,21 @@
# pylint config
[MASTER]
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

9
Makefile Normal file
View File

@@ -0,0 +1,9 @@
.PHONY: lint test coverage
lint:
python -m pylint check_http_json.py
test:
python -m unittest discover
coverage:
python -m coverage run -m unittest discover
python -m coverage report -m --include check_http_json.py

View File

@@ -1,3 +1,5 @@
![CI](https://github.com/drewkerrigan/nagios-http-json/workflows/CI/badge.svg)
# Nagios Json Plugin
This is a generic plugin for Nagios which checks json values from a given HTTP endpoint against argument specified rules and determines the status and performance data for that service.
@@ -18,7 +20,7 @@ Executing `./check_http_json.py -h` will yield the following details:
usage: check_http_json.py [-h] [-d] [-s] -H HOST [-k] [-V] [--cacert CACERT]
[--cert CERT] [--key KEY] [-P PORT] [-p PATH]
[-t TIMEOUT] [-B AUTH] [-D DATA] [-A HEADERS]
[-f SEPARATOR]
[-f FIELD_SEPARATOR] [-F VALUE_SEPARATOR]
[-w [KEY_THRESHOLD_WARNING [KEY_THRESHOLD_WARNING ...]]]
[-c [KEY_THRESHOLD_CRITICAL [KEY_THRESHOLD_CRITICAL ...]]]
[-e [KEY_LIST [KEY_LIST ...]]]
@@ -36,7 +38,7 @@ Generic Nagios plugin which checks json values from a given endpoint against
argument specified rules and determines the status and performance data for
that service.
Version: 1.4.0 (2019-05-09)
Version: 2.1.2 (2022-09-15)
optional arguments:
-h, --help show this help message and exit
@@ -44,6 +46,8 @@ optional arguments:
-s, --ssl use TLS to connect to remote host
-H HOST, --host HOST remote host to query
-k, --insecure do not check server SSL certificate
-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
--cacert CACERT SSL CA certificate
--cert CERT SSL client certificate
@@ -60,6 +64,8 @@ optional arguments:
-f SEPARATOR, --field_separator SEPARATOR
JSON Field separator, defaults to "."; Select element
in an array with "(" ")"
-F SEPARATOR, --value_separator SEPARATOR
JSON Value separator, defaults to ":";
-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).
@@ -133,6 +139,21 @@ optional arguments:
}
]
**Data for keys of all items in a list** `(*).capacity.value`:
[
{
"capacity": {
"value": 1000
}
},
{
"capacity": {
"value": 2200
}
}
]
**Data for separator** `-f _` **and key** `(0)_gauges_jvm.buffers.direct.capacity_value`:
[
@@ -191,7 +212,7 @@ More info about Nagios Range format and Units of Measure can be found at [https:
### Requirements
* Python 2.7
* Python 3.6+
### Configuration
@@ -226,7 +247,10 @@ define command{
```
More info about options in Usage.
## Icinga2 configuration
The Icinga2 command definition can be found here: (contrib/icinga2_check_command_definition.conf)
## License

View File

@@ -1,4 +1,14 @@
#!/usr/bin/python2.7
#!/usr/bin/env python3
import urllib.request, urllib.error, urllib.parse
import base64
import json
import argparse
import sys
import ssl
from pprint import pprint
from urllib.error import HTTPError
from urllib.error import URLError
plugin_description = \
"""
@@ -9,26 +19,19 @@ argument specified rules and determines the status and performance data for
that service.
"""
import urllib2
import base64
import json
import argparse
import sys
import ssl
from pprint import pprint
from urllib2 import HTTPError
from urllib2 import URLError
OK_CODE = 0
WARNING_CODE = 1
CRITICAL_CODE = 2
UNKNOWN_CODE = 3
__version__ = '1.4.0'
__version_date__ = '2019-05-09'
__version__ = '2.1.2'
__version_date__ = '2022-09-15'
class NagiosHelper:
"""Help with Nagios specific status string formatting."""
"""
Help with Nagios specific status string formatting.
"""
message_prefixes = {OK_CODE: 'OK',
WARNING_CODE: 'WARNING',
CRITICAL_CODE: 'CRITICAL',
@@ -38,17 +41,23 @@ class NagiosHelper:
critical_message = ''
unknown_message = ''
def getMessage(self):
"""Build a status-prefixed message with optional performance data
generated externally"""
text = "%s: Status %s." % (self.message_prefixes[self.getCode()],
self.message_prefixes[self.getCode()])
text += self.warning_message
text += self.critical_message
text += self.unknown_message
def getMessage(self, message=''):
"""
Build a status-prefixed message with optional performance data
generated externally
"""
message += self.warning_message
message += self.critical_message
message += self.unknown_message
code = self.message_prefixes[self.getCode()]
output = "{code}: Status {code}. {message}".format(code=code, message=message.strip())
if self.performance_data:
text += "|%s" % self.performance_data
return text
output = "{code}: {perf_data} Status {code}. {message}|{perf_data}".format(
code=code,
message=message.strip(),
perf_data=self.performance_data)
return output.strip()
def getCode(self):
code = OK_CODE
@@ -69,19 +78,23 @@ class NagiosHelper:
def append_unknown(self, unknown_message):
self.unknown_message += unknown_message
def append_metrics(self, (performance_data,
warning_message, critical_message)):
def append_metrics(self, metrics):
(performance_data, warning_message, critical_message) = metrics
self.performance_data += performance_data
self.append_warning(warning_message)
self.append_critical(critical_message)
class JsonHelper:
"""Perform simple comparison operations against values in a given
JSON dict"""
def __init__(self, json_data, separator):
"""
Perform simple comparison operations against values in a given
JSON dict
"""
def __init__(self, json_data, separator, value_separator):
self.data = json_data
self.separator = separator
self.value_separator = value_separator
self.arrayOpener = '('
self.arrayCloser = ')'
@@ -91,14 +104,14 @@ class JsonHelper:
remainingKey = key[separatorIndex + 1:]
if partialKey in data:
return self.get(remainingKey, data[partialKey])
else:
return (None, 'not_found')
return (None, 'not_found')
def getSubArrayElement(self, key, data):
subElemKey = key[:key.find(self.arrayOpener)]
index = int(key[key.find(self.arrayOpener) +
1:key.find(self.arrayCloser)])
remainingKey = key[key.find(self.arrayCloser + self.separator) + 2:]
if key.find(self.arrayCloser + self.separator) == -1:
remainingKey = key[key.find(self.arrayCloser) + 1:]
if subElemKey in data:
@@ -106,6 +119,8 @@ class JsonHelper:
return self.get(remainingKey, data[subElemKey][index])
else:
return (None, 'not_found')
if index >= len(data):
return (None, 'not_found')
else:
if not subElemKey:
return self.get(remainingKey, data[index])
@@ -114,7 +129,7 @@ class JsonHelper:
def equals(self, key, value):
return self.exists(key) and \
str(self.get(key)) in value.split(':')
str(self.get(key)) in value.split(self.value_separator)
def lte(self, key, value):
return self.exists(key) and float(self.get(key)) <= float(value)
@@ -132,9 +147,12 @@ class JsonHelper:
return (self.get(key) != (None, 'not_found'))
def get(self, key, temp_data=''):
"""Can navigate nested json keys with a dot format
(Element.Key.NestedKey). Returns (None, 'not_found') if not found"""
if temp_data:
"""
Can navigate nested json keys with a dot format
(Element.Key.NestedKey). Returns (None, 'not_found') if not found
"""
if temp_data != '':
data = temp_data
else:
data = self.data
@@ -153,7 +171,7 @@ class JsonHelper:
if key.find(self.arrayOpener) != -1:
return self.getSubArrayElement(key, data)
else:
if key in data:
if isinstance(data, dict) and key in data:
return data[key]
else:
return (None, 'not_found')
@@ -167,10 +185,10 @@ class JsonHelper:
subElemKey = key[:key.find('(*)')-1]
remainingKey = key[key.find('(*)')+3:]
elemData = self.get(subElemKey)
if elemData is (None, 'not_found'):
if elemData == (None, 'not_found'):
keys.append(key)
return keys
if subElemKey is not '':
if subElemKey != '':
subElemKey = subElemKey + '.'
for i in range(len(elemData)):
newKey = subElemKey + '(' + str(i) + ')' + remainingKey
@@ -192,17 +210,24 @@ def _getKeyAlias(original_key):
class JsonRuleProcessor:
"""Perform checks and gather values from a JSON dict given rules
and metrics definitions"""
"""
Perform checks and gather values from a JSON dict given rules
and metrics definitions
"""
def __init__(self, json_data, rules_args):
self.data = json_data
self.rules = rules_args
separator = '.'
value_separator = ':'
if self.rules.separator:
separator = self.rules.separator
self.helper = JsonHelper(self.data, separator)
debugPrint(rules_args.debug, "rules:%s" % rules_args)
debugPrint(rules_args.debug, "separator:%s" % separator)
if self.rules.value_separator:
value_separator = self.rules.value_separator
self.helper = JsonHelper(self.data, separator, value_separator)
debugPrint(rules_args.debug, "rules: %s" % rules_args)
debugPrint(rules_args.debug, "separator: %s" % separator)
debugPrint(rules_args.debug, "value_separator: %s" % value_separator)
self.metric_list = self.expandKeys(self.rules.metric_list)
self.key_threshold_warning = self.expandKeys(
self.rules.key_threshold_warning)
@@ -222,7 +247,7 @@ class JsonRuleProcessor:
def expandKeys(self, src):
if src is None:
return
return []
dest = []
for key in src:
newKeys = self.helper.expandKey(key, [])
@@ -243,9 +268,9 @@ class JsonRuleProcessor:
for kv in equality_list:
k, v = kv.split(',')
key, alias = _getKeyAlias(k)
if (self.helper.equals(key, v) == False):
if not self.helper.equals(key, v):
failure += " Key %s mismatch. %s != %s" % (alias, v,
self.helper.get(key))
self.helper.get(key))
return failure
def checkNonEquality(self, equality_list):
@@ -253,9 +278,9 @@ class JsonRuleProcessor:
for kv in equality_list:
k, v = kv.split(',')
key, alias = _getKeyAlias(k)
if (self.helper.equals(key, v) == True):
if self.helper.equals(key, v):
failure += " Key %s match found. %s == %s" % (alias, v,
self.helper.get(key))
self.helper.get(key))
return failure
def checkThreshold(self, key, alias, r):
@@ -321,6 +346,8 @@ class JsonRuleProcessor:
def checkCritical(self):
failure = ''
if not self.data:
failure = " Empty JSON data."
if self.key_threshold_critical is not None:
failure += self.checkThresholds(self.key_threshold_critical)
if self.key_value_list_critical is not None:
@@ -338,8 +365,11 @@ class JsonRuleProcessor:
return unknown
def checkMetrics(self):
"""Return a Nagios specific performance metrics string given keys
and parameter definitions"""
"""
Return a Nagios specific performance metrics string given keys
and parameter definitions
"""
metrics = ''
warning = ''
critical = ''
@@ -380,21 +410,28 @@ class JsonRuleProcessor:
return ("%s" % metrics, warning, critical)
def parseArgs():
def parseArgs(args):
"""
CLI argument definitions and parsing
"""
parser = argparse.ArgumentParser(
description = plugin_description + '\n\nVersion: %s (%s)'
%(__version__, __version_date__),
formatter_class=argparse.RawDescriptionHelpFormatter)
description=plugin_description + '\n\nVersion: %s (%s)'
%(__version__, __version_date__),
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument('-d', '--debug', action='store_true',
help='debug mode')
parser.add_argument('-s', '--ssl', action='store_true',
help='use TLS to connect to remote host')
parser.add_argument('-H', '--host', dest='host',
required=not ('-V' in sys.argv or '--version' in sys.argv),
required=not ('-V' in args or '--version' in args),
help='remote host to query')
parser.add_argument('-k', '--insecure', action='store_true',
help='do not check server SSL certificate')
parser.add_argument('-X', '--request', dest='method', default='GET', choices=['GET', 'POST'],
help='Specifies a custom request method to use when communicating with the HTTP server')
parser.add_argument('-V', '--version', action='store_true',
help='print version of this plugin')
parser.add_argument('--cacert',
@@ -416,6 +453,8 @@ def parseArgs():
parser.add_argument('-f', '--field_separator', dest='separator',
help='''JSON Field separator, defaults to ".";
Select element in an array with "(" ")"''')
parser.add_argument('-F', '--value_separator', dest='value_separator',
help='''JSON Value separator, defaults to ":"''')
parser.add_argument('-w', '--warning', dest='key_threshold_warning',
nargs='*',
help='''Warning threshold for these values
@@ -474,223 +513,38 @@ def parseArgs():
(key[>alias],UnitOfMeasure,WarnRange,
CriticalRange).''')
return parser.parse_args()
return parser.parse_args(args)
def debugPrint(debug_flag, message, pretty_flag=False):
"""
Print debug messages if -d (debug_flat ) is set.
"""
if debug_flag:
if pretty_flag:
pprint(message)
else:
print(message)
if __name__ == "__main__" and len(sys.argv) >= 2 and sys.argv[1] == 'UnitTest':
import unittest
class RulesHelper:
separator = '.'
debug = False
key_threshold_warning = None
key_value_list = None
key_value_list_not = None
key_list = None
key_threshold_critical = None
key_value_list_critical = None
key_value_list_not_critical = None
key_value_list_unknown = None
key_list_critical = None
metric_list = None
def main(cliargs):
"""
Main entrypoint for CLI
"""
def dash_m(self, data):
self.metric_list = data
return self
def dash_e(self, data):
self.key_list = data
return self
def dash_E(self, data):
self.key_list_critical = data
return self
def dash_q(self, data):
self.key_value_list = data
return self
def dash_Q(self, data):
self.key_value_list_critical = data
return self
def dash_y(self, data):
self.key_value_list_not = data
return self
def dash_Y(self, data):
self.key_value_list_not_critical = data
return self
def dash_w(self, data):
self.key_threshold_warning = data
return self
def dash_c(self, data):
self.key_threshold_critical = data
return self
class UnitTest(unittest.TestCase):
rules = RulesHelper()
def check_data(self, args, jsondata, code):
data = json.loads(jsondata)
nagios = NagiosHelper()
processor = JsonRuleProcessor(data, args)
nagios.append_warning(processor.checkWarning())
nagios.append_critical(processor.checkCritical())
nagios.append_metrics(processor.checkMetrics())
self.assertEqual(code, nagios.getCode())
def test_metrics(self):
self.check_data(RulesHelper().dash_m(['metric,,1:4,1:5']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:4']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:5,6,10']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:5,1,4']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,s,@1:4,@6:10,1,10']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_m(['(*).value,s,1:5,1:5']),
'[{"value": 5},{"value": 100}]', CRITICAL_CODE)
def test_exists(self):
self.check_data(RulesHelper().dash_e(['nothere']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_E(['nothere']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_e(['metric']),
'{"metric": 5}', OK_CODE)
def test_equality(self):
self.check_data(RulesHelper().dash_q(['metric,6']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_Q(['metric,6']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_q(['metric,5']),
'{"metric": 5}', OK_CODE)
def test_non_equality(self):
self.check_data(RulesHelper().dash_y(['metric,6']),
'{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_Y(['metric,6']),
'{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_y(['metric,5']),
'{"metric": 6}', OK_CODE)
def test_warning_thresholds(self):
self.check_data(RulesHelper().dash_w(['metric,5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,5:']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,~:5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,1:5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5:']),
'{"metric": 4}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@~:5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@1:5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,5']),
'{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,5:']),
'{"metric": 4}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,~:5']),
'{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,1:5']),
'{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5:']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@~:5']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@1:5']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['(*).value,@1:5']),
'[{"value": 5},{"value": 1000}]', WARNING_CODE)
def test_critical_thresholds(self):
self.check_data(RulesHelper().dash_c(['metric,5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,5:']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,~:5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,1:5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5:']),
'{"metric": 4}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@~:5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@1:5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,5']),
'{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,5:']),
'{"metric": 4}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,~:5']),
'{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,1:5']),
'{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5:']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@~:5']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@1:5']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['(*).value,@1:5']),
'[{"value": 5},{"value": 1000}]', CRITICAL_CODE)
def test_separator(self):
rules = RulesHelper()
rules.separator = '_'
self.check_data(
rules.dash_q(
['(0)_gauges_jvm.buffers.direct.capacity(1)_value,1234']),
'''[{ "gauges": { "jvm.buffers.direct.capacity": [
{"value": 215415},{"value": 1234}]}}]''',
OK_CODE)
self.check_data(
rules.dash_q(
['(*)_gauges_jvm.buffers.direct.capacity(1)_value,1234']),
'''[{ "gauges": { "jvm.buffers.direct.capacity": [
{"value": 215415},{"value": 1234}]}},
{ "gauges": { "jvm.buffers.direct.capacity": [
{"value": 215415},{"value": 1235}]}}]''',
WARNING_CODE)
unittest.main()
exit(0)
"""Program entry point"""
if __name__ == "__main__":
args = parseArgs()
args = parseArgs(cliargs)
nagios = NagiosHelper()
context = None
if args.version:
print('Version: %s - Date: %s' % (__version__, __version_date__))
exit(0)
sys.exit(0)
if args.ssl:
url = "https://%s" % args.host
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
@@ -698,30 +552,31 @@ if __name__ == "__main__":
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_unknown(
''' Error loading SSL CA cert "%s"!'''
% args.cacert)
'Error loading SSL CA cert "%s"!'
% args.cacert)
if args.cert:
try:
context.load_cert_chain(args.cert,keyfile=args.key)
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))
'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))
'Error loading SSL cert. Make sure "%s" contains the key as well!'
% (args.cert))
if nagios.getCode() != OK_CODE:
print(nagios.getMessage())
exit(nagios.getCode())
sys.exit(nagios.getCode())
else:
url = "http://%s" % args.host
@@ -729,13 +584,16 @@ if __name__ == "__main__":
url += ":%s" % args.port
if args.path:
url += "/%s" % args.path
debugPrint(args.debug, "url:%s" % url)
json_data = ''
try:
req = urllib2.Request(url)
req = urllib.request.Request(url, method=args.method)
req.add_header("User-Agent", "check_http_json")
if args.auth:
base64str = base64.encodestring(args.auth).replace('\n', '')
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)
@@ -743,20 +601,27 @@ if __name__ == "__main__":
for header in headers:
req.add_header(header, headers[header])
if args.timeout and args.data:
response = urllib2.urlopen(req, timeout=args.timeout,
data=args.data, context=context)
databytes = str(args.data).encode()
response = urllib.request.urlopen(req, timeout=args.timeout,
data=databytes, context=context)
elif args.timeout:
response = urllib2.urlopen(req, timeout=args.timeout,
context=context)
response = urllib.request.urlopen(req, timeout=args.timeout,
context=context)
elif args.data:
response = urllib2.urlopen(req, data=args.data, context=context)
databytes = str(args.data).encode()
response = urllib.request.urlopen(req, data=databytes, context=context)
else:
response = urllib2.urlopen(req, context=context)
# pylint: disable=consider-using-with
response = urllib.request.urlopen(req, context=context)
json_data = response.read()
json_data = response.read()
except HTTPError as e:
nagios.append_unknown(" HTTPError[%s], url:%s" % (str(e.code), url))
# Try to recover from HTTP Error, if there is JSON in the response
if "json" in e.info().get_content_subtype():
json_data = e.read()
else:
nagios.append_unknown(" HTTPError[%s], url:%s" % (str(e.code), url))
except URLError as e:
nagios.append_critical(" URLError[%s], url:%s" % (str(e.reason), url))
@@ -777,6 +642,11 @@ if __name__ == "__main__":
# Print Nagios specific string and exit appropriately
print(nagios.getMessage())
exit(nagios.getCode())
sys.exit(nagios.getCode())
if __name__ == "__main__":
# Program entry point
main(sys.argv[1:])
#EOF

View File

@@ -0,0 +1,106 @@
object CheckCommand "http_json" {
import "plugin-check-command"
command = [ PluginDir + "/check_http_json.py" ]
arguments = {
"--host" = {
value = "$address$"
description = "Hostname or address of the interface to query"
required = true
}
"--port" = {
value = "$http_json_port$"
description = "TCP port number"
}
"--path" = {
value = "$http_json_path$"
description = "URL path to query (i.e.: /v1/service/xyz)"
}
"--timeout" = {
value = "$http_json_timeout$"
description = "Connection timeout (seconds)"
}
"--basic-auth" = {
value = "$http_json_basic_auth$"
description = "Basic auth string 'username:password'"
}
"--ssl" = {
set_if = "$http_json_ssl$"
description = "use TLS to connect to remote host"
}
"--insecure" = {
set_if = "$http_json_insecure$"
description = "do not check server SSL certificate"
}
"--cacert" = {
value = "$http_json_cacert_file$"
description = "path of cacert file to validate server cert"
}
"--cert" = {
value = "$http_json_cert_file$"
description = "client certificate in PEM format"
}
"--key" = {
value = "$http_json_key_file$"
description = "client certificate key file in PEM format ( if not bundled into the cert )"
}
"--data" = {
value = "$http_json_post_data$"
description = "the http payload to send as a POST"
}
"--headers" = {
value = "$http_json_headers$"
description = "additional http headers in JSON format to send with the request"
}
"--field_separator" = {
value = "$http_json_field_separator$"
description = "JSON Field separator, defaults to '.'; Select element in an array with '(' ')'"
}
"--value_separator" = {
value = "$http_json_value_separator$"
description = "JSON Value separator, defaults to ':'"
}
"--warning" = {
value = "$http_json_warning$"
description = "Warning threshold for these values, WarningRange is in the format [@]start:end"
}
"--critical" = {
value = "$http_json_critical$"
description = "Critical threshold for these values, CriticalRange is in the format [@]start:end"
}
"--key_exists" = {
value = "$http_json_key_exists$"
description = "Checks existence of these keys to determine status. Return warning if key is not present."
}
"--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."
}
"--key_equals" = {
value = "$http_json_key_equals$"
description = "Checks equality of these keys and values. Return warning if equality check fails"
}
"--key_equals_critical" = {
value = "$http_json_key_equals_critical$"
description = "Checks equality of these keys and values. Return critical if equality check fails"
}
"--key_equals_unknown" = {
value = "$http_json_key_equals_unknown$"
description = "Checks equality of these keys and values. Return unknown if equality check fails"
}
"--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."
}
"--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."
}
"--key_metric" = {
value = "$http_json_key_metric$"
description = "Gathers the values of these keys"
}
}
}

2
requirements-dev.txt Normal file
View File

@@ -0,0 +1,2 @@
coverage==6.4.4
pylint==2.15.2

0
test/__init__.py Normal file
View File

2
test/requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
coverage==5.0.3
pylint==2.4.4

34
test/test_args.py Normal file
View File

@@ -0,0 +1,34 @@
#!/usr/bin/env python3
import unittest
import sys
sys.path.append('..')
from check_http_json import *
class ArgsTest(unittest.TestCase):
"""
Tests for argsparse
"""
def test_parser_defaults(self):
parser = parseArgs(['-H', 'foobar'])
self.assertFalse(parser.debug)
self.assertFalse(parser.ssl)
self.assertFalse(parser.insecure)
def test_parser_with_debug(self):
parser = parseArgs(['-H', 'foobar', '-d'])
self.assertTrue(parser.debug)
def test_parser_with_port(self):
parser = parseArgs(['-H', 'foobar', '-P', '8888'])
self.assertEqual(parser.port, '8888')
def test_parser_with_separator(self):
parser = parseArgs(['-H', 'foobar', '-f', '_', '-F', '_'])
self.assertEqual(parser.separator, '_')
self.assertEqual(parser.value_separator, '_')

View File

@@ -0,0 +1,296 @@
#!/usr/bin/env python3
import json
import unittest
from unittest.mock import patch
import sys
sys.path.append('..')
from check_http_json import *
OK_CODE = 0
WARNING_CODE = 1
CRITICAL_CODE = 2
UNKNOWN_CODE = 3
class RulesHelper:
separator = '.'
value_separator = ':'
debug = False
key_threshold_warning = None
key_value_list = None
key_value_list_not = None
key_list = None
key_threshold_critical = None
key_value_list_critical = None
key_value_list_not_critical = None
key_value_list_unknown = None
key_list_critical = None
metric_list = None
def dash_m(self, data):
self.metric_list = data
return self
def dash_e(self, data):
self.key_list = data
return self
def dash_E(self, data):
self.key_list_critical = data
return self
def dash_q(self, data):
self.key_value_list = data
return self
def dash_Q(self, data):
self.key_value_list_critical = data
return self
def dash_y(self, data):
self.key_value_list_not = data
return self
def dash_Y(self, data):
self.key_value_list_not_critical = data
return self
def dash_U(self, data):
self.key_value_list_unknown = data
return self
def dash_w(self, data):
self.key_threshold_warning = data
return self
def dash_c(self, data):
self.key_threshold_critical = data
return self
class UtilTest(unittest.TestCase):
"""
Tests for the util fucntions
"""
rules = RulesHelper()
def check_data(self, args, jsondata, code):
data = json.loads(jsondata)
nagios = NagiosHelper()
processor = JsonRuleProcessor(data, args)
nagios.append_warning(processor.checkWarning())
nagios.append_critical(processor.checkCritical())
nagios.append_metrics(processor.checkMetrics())
nagios.append_unknown(processor.checkUnknown())
self.assertEqual(code, nagios.getCode())
def test_metrics(self):
self.check_data(RulesHelper().dash_m(['metric,,1:4,1:5']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:4']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:5,6,10']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,,1:5,1:5,1,4']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric,s,@1:4,@6:10,1,10']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_m(['(*).value,s,1:5,1:5']),
'[{"value": 5},{"value": 100}]', CRITICAL_CODE)
self.check_data(RulesHelper().dash_m(['metric>foobar,,1:4,1:5']),
'{"metric": 5}', WARNING_CODE)
def test_unknown(self):
self.check_data(RulesHelper().dash_U(['metric,0']),
'{"metric": 3}', UNKNOWN_CODE)
def test_exists(self):
self.check_data(RulesHelper().dash_e(['nothere']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_E(['nothere']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_e(['metric']),
'{"metric": 5}', OK_CODE)
def test_equality(self):
self.check_data(RulesHelper().dash_q(['metric,6']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_Q(['metric,6']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_q(['metric,5']),
'{"metric": 5}', OK_CODE)
def test_equality_colon(self):
"""
See https://github.com/drewkerrigan/nagios-http-json/issues/43
"""
rules = RulesHelper()
rules.value_separator = '_'
# This should not fail
self.check_data(rules.dash_q(['metric,foo:bar']),
'{"metric": "foo:bar"}', OK_CODE)
def test_non_equality(self):
self.check_data(RulesHelper().dash_y(['metric,6']),
'{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_Y(['metric,6']),
'{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_y(['metric,5']),
'{"metric": 6}', OK_CODE)
def test_warning_thresholds(self):
self.check_data(RulesHelper().dash_w(['metric,5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,5:']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,~:5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,1:5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5:']),
'{"metric": 4}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@~:5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,@1:5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_w(['metric,5']),
'{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,5:']),
'{"metric": 4}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,~:5']),
'{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,1:5']),
'{"metric": 6}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@5:']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@~:5']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['metric,@1:5']),
'{"metric": 5}', WARNING_CODE)
self.check_data(RulesHelper().dash_w(['(*).value,@1:5']),
'[{"value": 5},{"value": 1000}]', WARNING_CODE)
def test_critical_thresholds(self):
self.check_data(RulesHelper().dash_c(['metric,5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,5:']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,~:5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,1:5']),
'{"metric": 5}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5:']),
'{"metric": 4}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@~:5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,@1:5']),
'{"metric": 6}', OK_CODE)
self.check_data(RulesHelper().dash_c(['metric,5']),
'{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,5:']),
'{"metric": 4}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,~:5']),
'{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,1:5']),
'{"metric": 6}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@5:']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@~:5']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['metric,@1:5']),
'{"metric": 5}', CRITICAL_CODE)
self.check_data(RulesHelper().dash_c(['(*).value,@1:5']),
'[{"value": 5},{"value": 1000}]', CRITICAL_CODE)
def test_separator(self):
rules = RulesHelper()
rules.separator = '_'
self.check_data(
rules.dash_q(
['(0)_gauges_jvm.buffers.direct.capacity(1)_value,1234']),
'''[{ "gauges": { "jvm.buffers.direct.capacity": [
{"value": 215415},{"value": 1234}]}}]''',
OK_CODE)
self.check_data(
rules.dash_q(
['(*)_gauges_jvm.buffers.direct.capacity(1)_value,1234']),
'''[{ "gauges": { "jvm.buffers.direct.capacity": [
{"value": 215415},{"value": 1234}]}},
{ "gauges": { "jvm.buffers.direct.capacity": [
{"value": 215415},{"value": 1235}]}}]''',
WARNING_CODE)
def test_array_with_missing_element(self):
"""
See https://github.com/drewkerrigan/nagios-http-json/issues/34
"""
rules = RulesHelper()
# This should simply work
data = '[{"Node": "there"}]'
self.check_data(rules.dash_q(['(0).Node,there']), data, OK_CODE)
# This should warn us
data = '[{"Node": "othervalue"}]'
self.check_data(rules.dash_q(['(0).Node,there']), data, WARNING_CODE)
# # This should not throw an IndexError
data = '[{"Node": "foobar"}]'
self.check_data(rules.dash_q(['(0).Node,foobar', '(1).Node,missing']), data, WARNING_CODE)
self.check_data(rules.dash_q(['(0).Node,foobar', '(1).Node,missing', '(2).Node,alsomissing']), data, WARNING_CODE)
# This should not throw a KeyError
data = '{}'
self.check_data(rules.dash_q(['(0).Node,foobar', '(1).Node,missing']), data, CRITICAL_CODE)
def test_subelem(self):
rules = RulesHelper()
data = '{"foo": {"foo": {"foo": "bar"}}}'
self.check_data(rules.dash_E(['foo.foo.foo.foo.foo']), data, CRITICAL_CODE)
def test_subarrayelem_missing_elem(self):
rules = RulesHelper()
data = '[{"capacity": {"value": 1000}},{"capacity": {"value": 2200}}]'
self.check_data(rules.dash_E(['(*).capacity.value']), data, OK_CODE)
self.check_data(rules.dash_E(['(*).capacity.value.too_deep']), data, CRITICAL_CODE)
# Should not throw keyerror
self.check_data(rules.dash_E(['foo']), data, CRITICAL_CODE)
def test_empty_key_value_array(self):
"""
https://github.com/drewkerrigan/nagios-http-json/issues/61
"""
rules = RulesHelper()
# This should simply work
data = '[{"update_status": "finished"},{"update_status": "finished"}]'
self.check_data(rules.dash_q(['(*).update_status,finished']), data, OK_CODE)
# This should warn us
data = '[{"update_status": "finished"},{"update_status": "failure"}]'
self.check_data(rules.dash_q(['(*).update_status,finished']), data, WARNING_CODE)
# This should throw an error
data = '[]'
self.check_data(rules.dash_q(['(*).update_status,warn_me']), data, CRITICAL_CODE)

44
test/test_cli.py Normal file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
import unittest
import unittest.mock as mock
import sys
import os
sys.path.append('..')
from check_http_json import debugPrint
class CLITest(unittest.TestCase):
"""
Tests for CLI
"""
def setUp(self):
"""
Defining the exitcodes
"""
self.exit_0 = 0 << 8
self.exit_1 = 1 << 8
self.exit_2 = 2 << 8
self.exit_3 = 3 << 8
def test_debugprint(self):
with mock.patch('builtins.print') as mock_print:
debugPrint(True, 'debug')
mock_print.assert_called_once_with('debug')
def test_debugprint_pprint(self):
with mock.patch('check_http_json.pprint') as mock_pprint:
debugPrint(True, 'debug', True)
mock_pprint.assert_called_once_with('debug')
def test_cli_without_params(self):
command = '/usr/bin/env python3 check_http_json.py > /dev/null 2>&1'
status = os.system(command)
self.assertEqual(status, self.exit_2)

97
test/test_main.py Normal file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
import unittest
import unittest.mock as mock
import sys
import os
sys.path.append('..')
from check_http_json import main
class MockResponse():
def __init__(self, status_code=200, content='{"foo": "bar"}'):
self.status_code = status_code
self.content = content
def read(self):
return self.content
class MainTest(unittest.TestCase):
"""
Tests for Main
"""
@mock.patch('builtins.print')
def test_main_version(self, mock_print):
args = ['--version']
with self.assertRaises(SystemExit) as test:
main(args)
mock_print.assert_called_once()
self.assertEqual(test.exception.code, 0)
@mock.patch('builtins.print')
@mock.patch('urllib.request.urlopen')
def test_main_with_ssl(self, mock_request, mock_print):
args = '-H localhost --ssl'.split(' ')
mock_request.return_value = MockResponse()
with self.assertRaises(SystemExit) as test:
main(args)
self.assertEqual(test.exception.code, 0)
@mock.patch('builtins.print')
@mock.patch('urllib.request.urlopen')
def test_main_with_parse_error(self, mock_request, mock_print):
args = '-H localhost'.split(' ')
mock_request.return_value = MockResponse(content='not JSON')
with self.assertRaises(SystemExit) as test:
main(args)
self.assertTrue('Parser error' in str(mock_print.call_args))
self.assertEqual(test.exception.code, 3)
@mock.patch('builtins.print')
def test_main_with_url_error(self, mock_print):
args = '-H localhost'.split(' ')
with self.assertRaises(SystemExit) as test:
main(args)
self.assertTrue('URLError' in str(mock_print.call_args))
self.assertEqual(test.exception.code, 3)
@mock.patch('builtins.print')
@mock.patch('urllib.request.urlopen')
def test_main_with_http_error_no_json(self, mock_request, mock_print):
args = '-H localhost'.split(' ')
mock_request.return_value = MockResponse(content='not JSON', status_code=503)
with self.assertRaises(SystemExit) as test:
main(args)
self.assertTrue('Parser error' in str(mock_print.call_args))
self.assertEqual(test.exception.code, 3)
@mock.patch('builtins.print')
@mock.patch('urllib.request.urlopen')
def test_main_with_http_error_valid_json(self, mock_request, mock_print):
args = '-H localhost'.split(' ')
mock_request.return_value = MockResponse(status_code=503)
with self.assertRaises(SystemExit) as test:
main(args)
self.assertEqual(test.exception.code, 0)

51
test/test_nagioshelper.py Normal file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/env python3
import json
import unittest
from unittest.mock import patch
import sys
sys.path.append('..')
from check_http_json import *
class NagiosHelperTest(unittest.TestCase):
"""
Tests for the NagiosHelper
"""
def test_getcode_default(self):
helper = NagiosHelper()
self.assertEqual(0, helper.getCode())
def test_getcode_warning(self):
helper = NagiosHelper()
helper.warning_message = 'foobar'
self.assertEqual(1, helper.getCode())
def test_getcode_critical(self):
helper = NagiosHelper()
helper.critical_message = 'foobar'
self.assertEqual(2, helper.getCode())
def test_getcode_unknown(self):
helper = NagiosHelper()
helper.unknown_message = 'foobar'
self.assertEqual(3, helper.getCode())
def test_getmessage_default(self):
helper = NagiosHelper()
self.assertEqual('OK: Status OK.', helper.getMessage())
def test_getmessage_perfomance_data(self):
helper = NagiosHelper()
helper.performance_data = 'foobar'
self.assertEqual('OK: foobar Status OK. |foobar', helper.getMessage())