From 2289fb2af37b2f54da98a0c03173309fa72ed78c Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Tue, 28 Jan 2020 12:10:42 +0100 Subject: [PATCH 01/22] Port to Python3 (#48). I used 2to3 script and then I clean up result of the conversion. --- README.md | 2 +- check_http_json.py | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b021554..d6f450e 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ More info about Nagios Range format and Units of Measure can be found at [https: ### Requirements -* Python 2.7 +* Python 3 ### Configuration diff --git a/check_http_json.py b/check_http_json.py index d743e6b..b89ed65 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2.7 +#!/usr/bin/env python3 plugin_description = \ """ @@ -9,15 +9,15 @@ argument specified rules and determines the status and performance data for that service. """ -import urllib2 +import urllib.request, urllib.error, urllib.parse import base64 import json import argparse import sys import ssl from pprint import pprint -from urllib2 import HTTPError -from urllib2 import URLError +from urllib.error import HTTPError +from urllib.error import URLError OK_CODE = 0 WARNING_CODE = 1 @@ -69,8 +69,8 @@ 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) @@ -734,7 +734,7 @@ if __name__ == "__main__": debugPrint(args.debug, "url:%s" % url) json_data = '' try: - req = urllib2.Request(url) + req = urllib.request.Request(url) req.add_header("User-Agent", "check_http_json") if args.auth: base64str = base64.encodestring(args.auth).replace('\n', '') @@ -745,15 +745,15 @@ 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, + response = urllib.request.urlopen(req, timeout=args.timeout, data=args.data, context=context) elif args.timeout: - response = urllib2.urlopen(req, timeout=args.timeout, + response = urllib.request.urlopen(req, timeout=args.timeout, context=context) elif args.data: - response = urllib2.urlopen(req, data=args.data, context=context) + response = urllib.request.urlopen(req, data=args.data, context=context) else: - response = urllib2.urlopen(req, context=context) + response = urllib.request.urlopen(req, context=context) json_data = response.read() From 24889384b0753a22b9ec702ac8c585d5c42db583 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Sun, 16 Feb 2020 09:55:58 +0100 Subject: [PATCH 02/22] Add gitignore file --- .gitignore | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d55d97 --- /dev/null +++ b/.gitignore @@ -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 From 174686a98052890c845d42916470726fa1264717 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Sun, 16 Feb 2020 10:04:47 +0100 Subject: [PATCH 03/22] Move test to separat file --- .travis.yml | 11 ++ check_http_json.py | 198 +------------------------------- test/__init__.py | 0 test/test_check_http_json.py | 213 +++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 196 deletions(-) create mode 100644 .travis.yml create mode 100644 test/__init__.py create mode 100644 test/test_check_http_json.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..32c17af --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: python +python: + - "3.5" + - "3.6" + - "3.7" +install: + - pip install coverage +script: + - python -m unittest discover + - coverage run -m unittest discover + - coverage report -m --include check_http_json.py diff --git a/check_http_json.py b/check_http_json.py index b89ed65..750c65c 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -24,8 +24,8 @@ WARNING_CODE = 1 CRITICAL_CODE = 2 UNKNOWN_CODE = 3 -__version__ = '1.4.0' -__version_date__ = '2019-05-09' +__version__ = '2.0.0' +__version_date__ = '2022-02-16' class NagiosHelper: """Help with Nagios specific status string formatting.""" @@ -484,200 +484,6 @@ def debugPrint(debug_flag, message, pretty_flag=False): 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 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__": diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_check_http_json.py b/test/test_check_http_json.py new file mode 100644 index 0000000..2485a91 --- /dev/null +++ b/test/test_check_http_json.py @@ -0,0 +1,213 @@ +#!/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 = '.' + 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_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()) + 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) From e95daad8ffd0b1930cbd642bb696755e6fc6ee59 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Sun, 16 Feb 2020 11:00:33 +0100 Subject: [PATCH 04/22] Add GitHub Action for Unit Test --- .github/workflows/unittest.yml | 17 +++++++++++++++++ .travis.yml | 11 ----------- 2 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/unittest.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml new file mode 100644 index 0000000..c8819c7 --- /dev/null +++ b/.github/workflows/unittest.yml @@ -0,0 +1,17 @@ +name: CI + +on: [push, pull_request] + +jobs: + gitHubActionForPytest: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.6, 3.7, 3.8] + name: GitHub Action + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Unit Test + run: | + python -m unittest discover diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 32c17af..0000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: python -python: - - "3.5" - - "3.6" - - "3.7" -install: - - pip install coverage -script: - - python -m unittest discover - - coverage run -m unittest discover - - coverage report -m --include check_http_json.py From f97759f1bd31392476ea42a67b338228180ff4e1 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 3 Mar 2020 09:48:44 +0100 Subject: [PATCH 05/22] Add Coverage report --- .github/workflows/unittest.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index c8819c7..f8dd61a 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -15,3 +15,8 @@ jobs: - name: Unit Test run: | python -m unittest discover + - name: Coverage + run: | + pip install coverage + python -m coverage run -m unittest discover + python -m coverage report -m --include check_http_json.py From 3f81e32b292ebf8943d6688b746c0cb8bda5dbbb Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 3 Mar 2020 09:49:40 +0100 Subject: [PATCH 06/22] Add CI Badge to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3c5e662..cce13b9 100644 --- a/README.md +++ b/README.md @@ -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. From ba9d9b1c3961cd50b0b97e4e3432780d008df553 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 3 Mar 2020 10:04:03 +0100 Subject: [PATCH 07/22] Use Python3 in GitHub Action --- .github/workflows/unittest.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index f8dd61a..b823d25 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -14,9 +14,9 @@ jobs: uses: actions/checkout@v2 - name: Unit Test run: | - python -m unittest discover + python3 -m unittest discover - name: Coverage run: | - pip install coverage - python -m coverage run -m unittest discover - python -m coverage report -m --include check_http_json.py + pip3 install coverage + python3 -m coverage run -m unittest discover + python3 -m coverage report -m --include check_http_json.py From 95912246a296d41fb0798f315d63296def7fedd7 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 3 Mar 2020 11:44:55 +0100 Subject: [PATCH 08/22] Add and format some doc_strings --- check_http_json.py | 57 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/check_http_json.py b/check_http_json.py index 750c65c..0b5e908 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -28,7 +28,10 @@ __version__ = '2.0.0' __version_date__ = '2022-02-16' 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', @@ -39,8 +42,11 @@ class NagiosHelper: unknown_message = '' def getMessage(self): - """Build a status-prefixed message with optional performance data - generated externally""" + """ + Build a status-prefixed message with optional performance data + generated externally + """ + text = "%s: Status %s." % (self.message_prefixes[self.getCode()], self.message_prefixes[self.getCode()]) text += self.warning_message @@ -77,8 +83,11 @@ class NagiosHelper: class JsonHelper: - """Perform simple comparison operations against values in a given - JSON dict""" + """ + Perform simple comparison operations against values in a given + JSON dict + """ + def __init__(self, json_data, separator): self.data = json_data self.separator = separator @@ -132,8 +141,11 @@ 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""" + """ + 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: @@ -192,8 +204,11 @@ 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 @@ -338,8 +353,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 = '' @@ -381,10 +399,15 @@ class JsonRuleProcessor: def parseArgs(): + """ + 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') @@ -478,6 +501,10 @@ def parseArgs(): 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) @@ -537,8 +564,10 @@ if __name__ == "__main__": url += ":%s" % args.port if args.path: url += "/%s" % args.path + debugPrint(args.debug, "url:%s" % url) json_data = '' + try: req = urllib.request.Request(url) req.add_header("User-Agent", "check_http_json") From 375da5d60539ce7ee414524496f264bdd027beac Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 3 Mar 2020 11:59:36 +0100 Subject: [PATCH 09/22] Add test case for key_value_list_unknown --- test/test_check_http_json.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test_check_http_json.py b/test/test_check_http_json.py index 2485a91..678b998 100644 --- a/test/test_check_http_json.py +++ b/test/test_check_http_json.py @@ -59,6 +59,10 @@ class RulesHelper: 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 @@ -82,6 +86,7 @@ class UtilTest(unittest.TestCase): 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): @@ -98,6 +103,10 @@ class UtilTest(unittest.TestCase): self.check_data(RulesHelper().dash_m(['(*).value,s,1:5,1:5']), '[{"value": 5},{"value": 100}]', CRITICAL_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) From b7c0b0595e233da7b41c233c21d2bd96912e4279 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 3 Mar 2020 12:12:52 +0100 Subject: [PATCH 10/22] Add unittest for argsparse --- check_http_json.py | 7 ++++--- test/test_args.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 test/test_args.py diff --git a/check_http_json.py b/check_http_json.py index 0b5e908..e4d70c3 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -398,7 +398,7 @@ class JsonRuleProcessor: return ("%s" % metrics, warning, critical) -def parseArgs(): +def parseArgs(args): """ CLI argument definitions and parsing """ @@ -497,7 +497,7 @@ def parseArgs(): (key[>alias],UnitOfMeasure,WarnRange, CriticalRange).''') - return parser.parse_args() + return parser.parse_args(args) def debugPrint(debug_flag, message, pretty_flag=False): @@ -514,7 +514,8 @@ def debugPrint(debug_flag, message, pretty_flag=False): """Program entry point""" if __name__ == "__main__": - args = parseArgs() + + args = parseArgs(sys.argv[1:]) nagios = NagiosHelper() context = None diff --git a/test/test_args.py b/test/test_args.py new file mode 100644 index 0000000..b2d90e4 --- /dev/null +++ b/test/test_args.py @@ -0,0 +1,29 @@ +#!/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') From e4801227bf90b8b55386e5b036324a39353313d4 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 3 Mar 2020 15:02:09 +0100 Subject: [PATCH 11/22] Add boundary check for SubArrayElement function - Fixes Issue 34 --- check_http_json.py | 3 +++ test/test_check_http_json.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/check_http_json.py b/check_http_json.py index e4d70c3..3708266 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -108,6 +108,7 @@ class JsonHelper: 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: @@ -115,6 +116,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]) diff --git a/test/test_check_http_json.py b/test/test_check_http_json.py index 678b998..c7d61f9 100644 --- a/test/test_check_http_json.py +++ b/test/test_check_http_json.py @@ -220,3 +220,26 @@ class UtilTest(unittest.TestCase): { "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, WARNING_CODE) From 71cbd98e799f49c87cfd1e6f28fc7e63e3f9f25e Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 3 Mar 2020 15:15:54 +0100 Subject: [PATCH 12/22] Add value_separator option to specify how JSON values are being split - Fixes issue 43 --- check_http_json.py | 13 ++++++++++--- test/test_args.py | 5 +++++ test/test_check_http_json.py | 12 ++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/check_http_json.py b/check_http_json.py index 3708266..5c4a80a 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -88,9 +88,10 @@ class JsonHelper: JSON dict """ - def __init__(self, json_data, separator): + def __init__(self, json_data, separator, value_separator): self.data = json_data self.separator = separator + self.value_separator = value_separator self.arrayOpener = '(' self.arrayCloser = ')' @@ -126,7 +127,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) @@ -216,11 +217,15 @@ class JsonRuleProcessor: 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) + 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) @@ -442,6 +447,8 @@ def parseArgs(args): 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 diff --git a/test/test_args.py b/test/test_args.py index b2d90e4..cecd9b4 100644 --- a/test/test_args.py +++ b/test/test_args.py @@ -27,3 +27,8 @@ class ArgsTest(unittest.TestCase): 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, '_') diff --git a/test/test_check_http_json.py b/test/test_check_http_json.py index c7d61f9..710cb46 100644 --- a/test/test_check_http_json.py +++ b/test/test_check_http_json.py @@ -19,6 +19,7 @@ UNKNOWN_CODE = 3 class RulesHelper: separator = '.' + value_separator = ':' debug = False key_threshold_warning = None key_value_list = None @@ -123,6 +124,17 @@ class UtilTest(unittest.TestCase): 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) From 404890d9187b5787cdcfa23848b9b942296792f7 Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Thu, 12 Mar 2020 09:37:53 +0100 Subject: [PATCH 13/22] 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 '': --- check_http_json.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check_http_json.py b/check_http_json.py index 5c4a80a..bf1becd 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -183,10 +183,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 From 1a9e1e9048d36d80937592cf0591553be61aa028 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Sun, 15 Mar 2020 09:45:18 +0100 Subject: [PATCH 14/22] Add unittest for NagiosHelper --- test/test_nagioshelper.py | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 test/test_nagioshelper.py diff --git a/test/test_nagioshelper.py b/test/test_nagioshelper.py new file mode 100644 index 0000000..fb8c9ec --- /dev/null +++ b/test/test_nagioshelper.py @@ -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: Status OK.|foobar', helper.getMessage()) From 2c98e840e82c398ba28485cd5feda5c9594fd689 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Sun, 15 Mar 2020 10:26:46 +0100 Subject: [PATCH 15/22] Extend unittest coverage --- check_http_json.py | 2 +- test/test_check_http_json.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/check_http_json.py b/check_http_json.py index bf1becd..80a4b7b 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -169,7 +169,7 @@ class JsonHelper: if key.find(self.arrayOpener) != -1: return self.getSubArrayElement(key, data) else: - if key in data: + if type(data) == dict and key in data: return data[key] else: return (None, 'not_found') diff --git a/test/test_check_http_json.py b/test/test_check_http_json.py index 710cb46..6320c60 100644 --- a/test/test_check_http_json.py +++ b/test/test_check_http_json.py @@ -255,3 +255,20 @@ class UtilTest(unittest.TestCase): # This should not throw a KeyError data = '{}' self.check_data(rules.dash_q(['(0).Node,foobar', '(1).Node,missing']), data, WARNING_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) From f567c1ca0ce93b38cb17bf25dae444524da9e3d1 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Sun, 15 Mar 2020 10:35:51 +0100 Subject: [PATCH 16/22] Add unittest for metric key alias --- test/test_check_http_json.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_check_http_json.py b/test/test_check_http_json.py index 6320c60..489213a 100644 --- a/test/test_check_http_json.py +++ b/test/test_check_http_json.py @@ -103,6 +103,8 @@ class UtilTest(unittest.TestCase): '{"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']), From 6fc41612c4dbbf7f9c0d7c521388a5561b5305b3 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Sun, 15 Mar 2020 10:53:00 +0100 Subject: [PATCH 17/22] Add unittest for debugprint --- test/test_main.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/test_main.py diff --git a/test/test_main.py b/test/test_main.py new file mode 100644 index 0000000..ca74043 --- /dev/null +++ b/test/test_main.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + + +import unittest +import unittest.mock as mock +import sys + +sys.path.append('..') + +from check_http_json import debugPrint + + +class MainTest(unittest.TestCase): + """ + Tests for main + """ + + 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') From 1ac160e8c23c658539efa1b6860706eab3e0186a Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Sun, 15 Mar 2020 11:04:50 +0100 Subject: [PATCH 18/22] Add boilerplate for CLI tests --- test/test_main.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/test_main.py b/test/test_main.py index ca74043..873c62d 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -4,6 +4,7 @@ import unittest import unittest.mock as mock import sys +import os sys.path.append('..') @@ -15,6 +16,16 @@ class MainTest(unittest.TestCase): Tests for main """ + 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') @@ -24,3 +35,10 @@ class MainTest(unittest.TestCase): 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) From c90b0323f56ddd8f64ec857096a2648657716fc3 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Tue, 17 Mar 2020 13:30:37 +0100 Subject: [PATCH 19/22] Show returned JSON in OK Status when performance data is requested --- check_http_json.py | 19 +++++++++++-------- test/test_nagioshelper.py | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/check_http_json.py b/check_http_json.py index 80a4b7b..51b0928 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -41,20 +41,23 @@ class NagiosHelper: critical_message = '' unknown_message = '' - def getMessage(self): + def getMessage(self, message=''): """ 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 + 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 diff --git a/test/test_nagioshelper.py b/test/test_nagioshelper.py index fb8c9ec..61aff88 100644 --- a/test/test_nagioshelper.py +++ b/test/test_nagioshelper.py @@ -48,4 +48,4 @@ class NagiosHelperTest(unittest.TestCase): helper = NagiosHelper() helper.performance_data = 'foobar' - self.assertEqual('OK: Status OK.|foobar', helper.getMessage()) + self.assertEqual('OK: foobar Status OK. |foobar', helper.getMessage()) From 83ee5062f5acfa5bb8cf6a825742f72a3cd4d112 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Wed, 18 Mar 2020 08:09:35 +0100 Subject: [PATCH 20/22] [wip] Add pylint and fix pylint issues --- .github/workflows/unittest.yml | 5 +++ .pylintrc | 5 +++ check_http_json.py | 66 +++++++++++++++++----------------- 3 files changed, 43 insertions(+), 33 deletions(-) create mode 100644 .pylintrc diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index b823d25..015cf1a 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -12,6 +12,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 + - name: Lint + run: | + pip3 install --upgrade pip wheel setuptools + pip3 install pylint + python3 -m pylint check_http_json.py - name: Unit Test run: | python3 -m unittest discover diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..9e4eb71 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,5 @@ +# 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 +[MASTER] +ignore-patterns=^test.* diff --git a/check_http_json.py b/check_http_json.py index 51b0928..1671e67 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -1,14 +1,5 @@ #!/usr/bin/env python3 -plugin_description = \ -""" -Check HTTP JSON Nagios Plugin - -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. -""" - import urllib.request, urllib.error, urllib.parse import base64 import json @@ -19,13 +10,22 @@ from pprint import pprint from urllib.error import HTTPError from urllib.error import URLError +plugin_description = \ +""" +Check HTTP JSON Nagios Plugin + +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. +""" + OK_CODE = 0 WARNING_CODE = 1 CRITICAL_CODE = 2 UNKNOWN_CODE = 3 __version__ = '2.0.0' -__version_date__ = '2022-02-16' +__version_date__ = '2020-03-22' class NagiosHelper: """ @@ -104,8 +104,7 @@ 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)] @@ -121,7 +120,7 @@ class JsonHelper: else: return (None, 'not_found') if index >= len(data): - return (None, 'not_found') + return (None, 'not_found') else: if not subElemKey: return self.get(remainingKey, data[index]) @@ -172,7 +171,7 @@ class JsonHelper: if key.find(self.arrayOpener) != -1: return self.getSubArrayElement(key, data) else: - if type(data) == dict and key in data: + if isinstance(data, dict) and key in data: return data[key] else: return (None, 'not_found') @@ -248,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, []) @@ -269,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): @@ -279,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): @@ -415,7 +414,7 @@ def parseArgs(args): """ parser = argparse.ArgumentParser( - description = plugin_description + '\n\nVersion: %s (%s)' + description=plugin_description + '\n\nVersion: %s (%s)' %(__version__, __version_date__), formatter_class=argparse.RawDescriptionHelpFormatter ) @@ -525,7 +524,7 @@ def debugPrint(debug_flag, message, pretty_flag=False): print(message) -"""Program entry point""" +# Program entry point if __name__ == "__main__": args = parseArgs(sys.argv[1:]) @@ -534,7 +533,7 @@ if __name__ == "__main__": if args.version: print('Version: %s - Date: %s' % (__version__, __version_date__)) - exit(0) + sys.exit(0) if args.ssl: url = "https://%s" % args.host @@ -552,25 +551,25 @@ if __name__ == "__main__": 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 @@ -586,6 +585,7 @@ if __name__ == "__main__": req = urllib.request.Request(url) req.add_header("User-Agent", "check_http_json") if args.auth: + # TODO: replace deprecated encodestring base64str = base64.encodestring(args.auth).replace('\n', '') req.add_header('Authorization', 'Basic %s' % base64str) if args.headers: @@ -595,10 +595,10 @@ if __name__ == "__main__": 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) + data=args.data, context=context) elif args.timeout: response = urllib.request.urlopen(req, timeout=args.timeout, - context=context) + context=context) elif args.data: response = urllib.request.urlopen(req, data=args.data, context=context) else: @@ -628,6 +628,6 @@ if __name__ == "__main__": # Print Nagios specific string and exit appropriately print(nagios.getMessage()) - exit(nagios.getCode()) + sys.exit(nagios.getCode()) #EOF From dd952fd571ba1e3487f57361b5bae519b7fcb749 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Wed, 18 Mar 2020 08:47:27 +0100 Subject: [PATCH 21/22] Replace deprecated encodestring - Fixes #56 --- check_http_json.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check_http_json.py b/check_http_json.py index 1671e67..3b8d6d9 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -585,8 +585,8 @@ if __name__ == "__main__": req = urllib.request.Request(url) req.add_header("User-Agent", "check_http_json") if args.auth: - # TODO: replace deprecated encodestring - 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) From 0cbbf41b9ca50df9d9eb0d0db7a95b7a1a5ec811 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Mon, 23 Mar 2020 09:11:22 +0100 Subject: [PATCH 22/22] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cce13b9..c700147 100644 --- a/README.md +++ b/README.md @@ -38,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.0.0 (2020-03-22) optional arguments: -h, --help show this help message and exit