Page MenuHomeDevCentral

D2599.id6561.diff
No OneTemporary

D2599.id6561.diff

diff --git a/.arcconfig b/.arcconfig
new file mode 100644
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,4 @@
+{
+ "phabricator.uri": "https://devcentral.nasqueron.org/",
+ "repository.callsign": "CHECKS"
+}
diff --git a/.arclint b/.arclint
new file mode 100644
--- /dev/null
+++ b/.arclint
@@ -0,0 +1,29 @@
+{
+ "linters": {
+ "chmod": {
+ "type": "chmod"
+ },
+ "filename": {
+ "type": "filename"
+ },
+ "json": {
+ "type": "json",
+ "include": [
+ "(^\\.arcconfig$)",
+ "(^\\.arclint$)",
+ "(\\.json$)"
+ ]
+ },
+ "python": {
+ "type": "flake8",
+ "severity": {
+ "E203": "disabled",
+ "F821": "advice"
+ },
+ "include": [
+ "(^bin/check_)",
+ "(\\.py$)"
+ ]
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+# Python package build
+dist/
+*.egg-info/
+
+# Python
+__pycache__/
+*.pyc
+*.pyo
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright 2022 Sébastien Santoro aka Dereckson, from Nasqueron.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,42 @@
+# -------------------------------------------------------------
+# Platform checks
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+RMDIR=rm -rf
+PYTHON=python
+DISCOVER_TESTS=$(PYTHON) -m unittest discover
+REFORMAT=black
+
+# -------------------------------------------------------------
+# Main targets
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+all:
+
+package: dist
+
+clean: clean-package
+
+test:
+ ${DISCOVER_TESTS} tests/
+
+# -------------------------------------------------------------
+# Development helpers
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+reformat:
+ find bin -type f | xargs ${REFORMAT}
+ find src -type f -name '*.py' | xargs ${REFORMAT}
+
+# -------------------------------------------------------------
+# Packaging targets
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+dist:
+ ${PYTHON} -m build
+
+clean-package:
+ ${RMDIR} dist src/platform_checks.egg-info
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+# Platform checks
+
+## Introduction
+
+Allow to perform checks on a PaaS.
+
+This is built for the Nasqueron servers infrastructure,
+and will be useful on modern platform installations
+like microservices, Docker and proxies/load balancers.
+
+Each check is compatible Nagios / NRPE.
+
+## Configuration
+
+Checks can be defined in `/etc/monitoring/checks.yml`.
+
+The format of this file is:
+
+```yaml
+checks:
+ <check_type>:
+ <key>: <value>
+```
+
+You can also use `/usr/local/etc/monitoring/checks.yml` as path,
+or `.checks.yml` in the current working directory.
+
+## Checks reference
+
+### check_http_200
+
+Perform a HTTP request to a specific URL. The check success if HTTP code is 200.
+
+#### Available check types
+
+* check_http_200: for sites you test at the back-end level
+* check_http_200_alive: for sites returning "ALIVE" as body content
+* check_http_200_proxy: flag site as verified at proxy level, not directly at the back-end
+* check_http_200_alive_proxy: combine proxy and alive flags
+
+#### Configuration example
+
+```yaml
+checks:
+ check_http_200:
+ acme: http://localhost:41080/health
+ cachet: http://localhost:39080/api/v1/ping
+
+ check_http_200_proxy:
+ openfire: https://xmpp.nasqueron.org/login.jsp
+
+ check_http_200_alive_proxy:
+ phabricator: https://devcentral.nasqueron.org/status
+```
+
+#### Run the check
+
+From there, you've two ways to run the check:
+
+* one check for all URLs: call `check_http_200` without argument
+* one check, one URL: call `check_http_200 <service name>`, for example `check_http_200 openfire`
+
+You can also create a check calling `check_http_200` without argument,
+and it will test every site.
+
+## Return values of checks
+
+The checks use the standard Nagios/NRPE exit codes:
+
+| Exit code | Description |
+|-----------|-------------------------------------------|
+| 0 | OK |
+| 1 | Warning |
+| 2 | Critical |
+| 3 | Unknown, for example a check config issue |
+
+As such they're compatible with Nagios, Incinga, Sensu, Shinken, etc.
diff --git a/bin/check_http_200 b/bin/check_http_200
new file mode 100755
--- /dev/null
+++ b/bin/check_http_200
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+
+# -------------------------------------------------------------
+# Platform checks - HTTP services
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Check if a service returns a HTTP 200 status code.
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import sys
+
+from platformchecks import exitcode
+from platformchecks.checks import HttpCheck
+from platformchecks.config import get_all_checks, get_check_value
+
+
+# -------------------------------------------------------------
+# Application entry point
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def run_all():
+ success = True
+
+ messages = []
+ for service, (check_type, url) in get_all_checks(prefix="check_http_200").items():
+ check = HttpCheck(service, check_type, url)
+ check.perform()
+
+ if not check.success:
+ success = False
+
+ messages.append(check.build_message())
+
+ print("\n".join(messages))
+
+ if success:
+ return exitcode.OK
+
+ return exitcode.CRITICAL
+
+
+def run(service):
+ try:
+ check_type, url = get_check_value(prefix="check_http_200", key=service)
+ except ValueError:
+ print(f"Service {service} missing in configuration", file=sys.stderr)
+ return exitcode.UNKNOWN
+
+ check = HttpCheck(service, check_type, url)
+ check.perform()
+
+ if check.success:
+ print(check.build_message())
+ return exitcode.OK
+
+ print(check.build_message(), file=sys.stderr)
+ return exitcode.CRITICAL
+
+
+if __name__ == "__main__":
+ argc = len(sys.argv)
+
+ if argc < 2:
+ exitCode = run_all()
+ else:
+ exitCode = run(sys.argv[1])
+
+ sys.exit(exitCode)
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,14 @@
+# -------------------------------------------------------------
+# Platform checks
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+[build-system]
+requires = [
+ "setuptools>=42",
+ "wheel"
+]
+
+build-backend = "setuptools.build_meta"
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,34 @@
+[metadata]
+name = platform-checks
+version = 0.1.0
+author = Sébastien Santoro
+author_email = dereckson@espace-win.org
+description = Platform checks NRPE / Nagios
+long_description = file: README.md
+long_description_content_type = text/markdown
+license = BSD-2-Clause
+license_files = LICENSE
+url = https://devcentral.nasqueron.org/source/platform-checks/
+project_urls =
+ Bug Tracker = https://devcentral.nasqueron.org/tag/monitoring_and_reporting/
+classifiers =
+ Programming Language :: Python :: 3
+ License :: OSI Approved :: BSD License
+ Operating System :: OS Independent
+ Environment :: Console
+ Intended Audience :: System Administrators
+ Topic :: System :: Monitoring
+
+[options]
+package_dir =
+ = src
+packages = find:
+scripts =
+ bin/check_http_200
+python_requires = >=3.6
+install_requires =
+ PyYAML>=6.0,<7.0
+ requests>=2.27,<3.0
+
+[options.packages.find]
+where = src
diff --git a/src/platformchecks/__init__.py b/src/platformchecks/__init__.py
new file mode 100644
diff --git a/src/platformchecks/checks/__init__.py b/src/platformchecks/checks/__init__.py
new file mode 100644
--- /dev/null
+++ b/src/platformchecks/checks/__init__.py
@@ -0,0 +1 @@
+from .http import HttpCheck
diff --git a/src/platformchecks/checks/http.py b/src/platformchecks/checks/http.py
new file mode 100644
--- /dev/null
+++ b/src/platformchecks/checks/http.py
@@ -0,0 +1,67 @@
+# -------------------------------------------------------------
+# Platform checks
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Check if a HTTP resource is alive
+# and return expected HTTP status code.
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import requests
+from requests.exceptions import ConnectionError
+
+
+class HttpCheck:
+ def __init__(self, service, check_type, url, expected_status_code=200):
+ self.service = service
+ self.check_type = check_type
+ self.url = url
+
+ self.can_connect = False
+ self.connection_error = ""
+
+ self.expected_status_code = expected_status_code
+
+ self.status_code = None
+ self.success = None
+
+ def is_alive_check(self):
+ return "_alive" in self.check_type
+
+ def is_proxy_check(self):
+ return self.check_type[-6:] == "_proxy"
+
+ def perform(self):
+ try:
+ r = requests.head(self.url)
+ except ConnectionError as e:
+ self.success = False
+ self.connection_error = str(e)
+ return
+
+ self.can_connect = True
+ self.status_code = r.status_code
+ self.success = r.status_code == self.expected_status_code
+
+ if self.success and self.is_alive_check():
+ r = requests.get(self.url)
+ self.success = r.text.strip() == "ALIVE"
+
+ def build_message(self):
+ if self.success:
+ message = [f"Service {self.service} healthy"]
+ else:
+ message = [f"Service {self.service} NOT healthy"]
+
+ if not self.can_connect:
+ message.append(self.connection_error)
+ elif self.status_code != self.expected_status_code:
+ message.append(f"HTTP {self.status_code}")
+ else:
+ message.append(f"Unexpected body content")
+
+ if self.is_proxy_check():
+ message.append("Checked at PROXY level")
+
+ return " - ".join(message)
diff --git a/src/platformchecks/config/__init__.py b/src/platformchecks/config/__init__.py
new file mode 100644
--- /dev/null
+++ b/src/platformchecks/config/__init__.py
@@ -0,0 +1,10 @@
+from .config import (
+ #
+ # Parse configuration
+ get_configuration_path,
+ parse_config,
+ #
+ # Get relevant information for a check type
+ get_all_checks,
+ get_check_value,
+)
diff --git a/src/platformchecks/config/config.py b/src/platformchecks/config/config.py
new file mode 100644
--- /dev/null
+++ b/src/platformchecks/config/config.py
@@ -0,0 +1,60 @@
+# -------------------------------------------------------------
+# Platform checks
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import os
+import yaml
+
+
+# -------------------------------------------------------------
+# Parse configuration
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def get_configuration_path():
+ candidates = [
+ "/usr/local/etc/monitoring/checks.yml",
+ "/etc/monitoring/checks.yml",
+ ".checks.yml",
+ ]
+
+ for candidate in candidates:
+ if os.path.exists(candidate):
+ return candidate
+
+ raise FileNotFoundError("Can't find monitoring configuration file")
+
+
+def parse_config():
+ with open(get_configuration_path()) as fd:
+ return yaml.safe_load(fd)
+
+
+# -------------------------------------------------------------
+# Extract relevant data for checks
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def get_all_checks(prefix=""):
+ return {
+ key: (check_type, value)
+ for check_type, checks in parse_config()["checks"].items()
+ if check_type.startswith(prefix)
+ for key, value in checks.items()
+ }
+
+
+def get_check_value(prefix="", key=None):
+ for check_type, checks in parse_config()["checks"].items():
+ if not check_type.startswith(prefix):
+ continue
+
+ for current_key, value in checks.items():
+ if current_key == key:
+ return check_type, value
+
+ raise ValueError("Service not defined in configuration")
diff --git a/src/platformchecks/exitcode.py b/src/platformchecks/exitcode.py
new file mode 100644
--- /dev/null
+++ b/src/platformchecks/exitcode.py
@@ -0,0 +1,13 @@
+# -------------------------------------------------------------
+# Platform checks - exit codes
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Exit codes for Nagios / NRPE
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+
+OK = 0
+WARNING = 1
+CRITICAL = 2
+UNKNOWN = 3

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 24, 04:05 (4 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2259251
Default Alt Text
D2599.id6561.diff (14 KB)

Event Timeline