[packages/nagios-plugin-check_json_health] Initial (PLD is "upstream" for it)

arekm arekm at pld-linux.org
Mon Mar 30 23:43:06 CEST 2026


commit 3bf64a96bc7c3d3a4c08c58803e497674dddc659
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Mon Mar 30 23:41:47 2026 +0200

    Initial (PLD is "upstream" for it)

 check_json_health.cfg                |  29 ++++++++
 check_json_health.py                 | 132 +++++++++++++++++++++++++++++++++++
 nagios-plugin-check_json_health.spec |  41 +++++++++++
 3 files changed, 202 insertions(+)
---
diff --git a/nagios-plugin-check_json_health.spec b/nagios-plugin-check_json_health.spec
new file mode 100644
index 0000000..1a919f6
--- /dev/null
+++ b/nagios-plugin-check_json_health.spec
@@ -0,0 +1,41 @@
+%define		plugin	check_json_health
+Summary:	Nagios plugin to check JSON health endpoints
+Name:		nagios-plugin-%{plugin}
+Version:	1.0
+Release:	1
+License:	Public Domain (CC0 1.0)
+Group:		Networking
+Source0:	%{plugin}.py
+Source1:	%{plugin}.cfg
+URL:		https://github.com/pld-linux
+Requires:	python3
+Requires:	python3-modules
+BuildArch:	noarch
+BuildRoot:	%{tmpdir}/%{name}-%{version}-root-%(id -u -n)
+
+%define		_sysconfdir	/etc/nagios/plugins
+%define		plugindir	%{_prefix}/lib/nagios/plugins
+
+%description
+Generic Nagios plugin for checking HTTP(S) endpoints that return JSON
+with standard health fields (exit_code, status, message). The server
+owns all threshold logic; the plugin simply fetches, extracts, and
+forwards the status to Nagios.
+
+%prep
+%setup -q -c -T
+cp -p %{SOURCE0} %{plugin}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+install -d $RPM_BUILD_ROOT{%{_sysconfdir},%{plugindir}}
+install -p %{plugin} $RPM_BUILD_ROOT%{plugindir}/%{plugin}
+cp -p %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/%{plugin}.cfg
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(644,root,root,755)
+%config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/%{plugin}.cfg
+%attr(755,root,root) %{plugindir}/%{plugin}
diff --git a/check_json_health.cfg b/check_json_health.cfg
new file mode 100644
index 0000000..6ec6221
--- /dev/null
+++ b/check_json_health.cfg
@@ -0,0 +1,29 @@
+define command {
+	command_name	check_json_health
+	command_line	$USER1$/check_json_health -H $HOSTADDRESS$ -p $ARG1$ -u $ARG2$ $ARG3$
+}
+
+# Example service definitions:
+#
+# define service {
+#	use			generic-service
+#	host_name		myapp.example.com
+#	service_description	JSON Health Check
+#	check_command		check_json_health!443!/health!
+# }
+#
+# With perfdata and insecure SSL:
+# define service {
+#	use			generic-service
+#	host_name		myapp.example.com
+#	service_description	JSON Health Check
+#	check_command		check_json_health!443!/nagios/!-k --perfdata-field details
+# }
+#
+# Plain HTTP:
+# define service {
+#	use			generic-service
+#	host_name		myapp.example.com
+#	service_description	JSON Health Check
+#	check_command		check_json_health!8080!/health!--no-ssl
+# }
diff --git a/check_json_health.py b/check_json_health.py
new file mode 100644
index 0000000..11833c3
--- /dev/null
+++ b/check_json_health.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python3
+#
+# This work is dedicated to the public domain under CC0 1.0.
+# https://creativecommons.org/publicdomain/zero/1.0/
+#
+# To the extent possible under law, the author(s) have dedicated all
+# copyright and related and neighboring rights to this software to the
+# public domain worldwide. This software is distributed without any warranty.
+#
+"""
+check_json_health - Generic Nagios plugin for JSON health endpoints.
+
+Checks any HTTP(S) URL that returns JSON with standard health fields:
+  - exit_code: Nagios exit code (0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN)
+  - status:    Status keyword (OK, WARNING, CRITICAL, UNKNOWN)
+  - message:   Human-readable status message
+
+The server owns all threshold logic. This plugin simply fetches,
+extracts, and forwards the status to Nagios.
+
+Dependencies: Python 3 stdlib only (no pip packages).
+
+Installation:
+  cp check_json_health /usr/lib/nagios/plugins/
+  chmod +x /usr/lib/nagios/plugins/check_json_health
+
+Usage:
+  check_json_health -H <hostname> [-p <port>] [-u <uri>] [-t <timeout>]
+  check_json_health -H k-gw-prod.example.com -u /nagios/ --perfdata-field details
+  check_json_health -H myapp.example.com -u /health -k --no-ssl -p 8080
+"""
+
+import argparse
+import json
+import ssl
+import sys
+import urllib.request
+
+OK, WARNING, CRITICAL, UNKNOWN = 0, 1, 2, 3
+
+
+def sanitize(s):
+    """Remove characters that could break Nagios output parsing.
+
+    Strips pipe (perfdata separator), newlines (Nagios reads first line only),
+    and carriage returns from values originating in the JSON response.
+    """
+    return str(s).replace('|', '/').replace('\n', ' ').replace('\r', '')
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        description='Nagios plugin: check a JSON health endpoint')
+    parser.add_argument('-H', '--hostname', required=True,
+                        help='Server hostname or IP')
+    parser.add_argument('-p', '--port', type=int, default=443,
+                        help='Port (default: 443)')
+    parser.add_argument('-u', '--uri', default='/nagios/',
+                        help='URI path (default: /nagios/)')
+    parser.add_argument('--no-ssl', action='store_true', default=False,
+                        help='Use plain HTTP instead of HTTPS (default: HTTPS)')
+    parser.add_argument('-k', '--insecure', action='store_true',
+                        help='Skip SSL certificate verification')
+    parser.add_argument('-t', '--timeout', type=int, default=15,
+                        help='Connection timeout in seconds (default: 15)')
+    parser.add_argument('--status-field', default='status',
+                        help='JSON field for status keyword (default: status)')
+    parser.add_argument('--exit-code-field', default='exit_code',
+                        help='JSON field for exit code (default: exit_code)')
+    parser.add_argument('--message-field', default='message',
+                        help='JSON field for message (default: message)')
+    parser.add_argument('--perfdata-field', default=None,
+                        help='JSON field for perfdata - dict of key:number '
+                             'or pre-formatted string (default: none)')
+    args = parser.parse_args()
+
+    scheme = 'http' if args.no_ssl else 'https'
+    url = f'{scheme}://{args.hostname}:{args.port}{args.uri}'
+
+    try:
+        if args.no_ssl:
+            ctx = None
+        else:
+            ctx = ssl.create_default_context()
+            if args.insecure:
+                ctx.check_hostname = False
+                ctx.verify_mode = ssl.CERT_NONE
+
+        req = urllib.request.Request(url, headers={'Cache-Control': 'no-cache'})
+        with urllib.request.urlopen(req, timeout=args.timeout, context=ctx) as resp:
+            body = resp.read().decode('utf-8')
+    except Exception as e:
+        # Connectivity/infrastructure failure = UNKNOWN (service state is indeterminate)
+        print(f'UNKNOWN - {url}: {e}')
+        sys.exit(UNKNOWN)
+
+    try:
+        data = json.loads(body)
+
+        exit_code = int(data.get(args.exit_code_field, UNKNOWN))
+        if exit_code not in (OK, WARNING, CRITICAL, UNKNOWN):
+            exit_code = UNKNOWN
+
+        status = sanitize(data.get(args.status_field, 'UNKNOWN'))
+        message = sanitize(data.get(args.message_field, 'No message in response'))
+
+        output = f'{status} - {message}'
+
+        # Optional perfdata from a JSON field
+        if args.perfdata_field:
+            perfdata_raw = data.get(args.perfdata_field)
+            if isinstance(perfdata_raw, dict):
+                pairs = ' '.join(
+                    f'{sanitize(k)}={v}'
+                    for k, v in perfdata_raw.items()
+                    if isinstance(v, (int, float))
+                )
+                if pairs:
+                    output += f' | {pairs}'
+            elif isinstance(perfdata_raw, str) and perfdata_raw:
+                output += f' | {sanitize(perfdata_raw)}'
+
+        print(output)
+        sys.exit(exit_code)
+    except Exception as e:
+        # JSON parse or field extraction failure = CRITICAL (service returned garbage)
+        print(f'CRITICAL - {url}: {e}')
+        sys.exit(CRITICAL)
+
+
+if __name__ == '__main__':
+    main()
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/nagios-plugin-check_json_health.git/commitdiff/3bf64a96bc7c3d3a4c08c58803e497674dddc659



More information about the pld-cvs-commit mailing list