Projects
jsj
check_mk-agent-plugins-apache_status
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 4
View file
check_mk-agent-plugins-apache_status.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Sun Sep 20 08:26:24 UTC 2026 - Stefan Botter <obs@botter.cc> + +- newer version from 2.4.x branch + +------------------------------------------------------------------- Fri Jul 29 12:51:09 UTC 2022 - Stefan Botter <obs@botter.cc> - initial version with apache_status.py from github 20220729
View file
apache_status.py
Changed
@@ -1,11 +1,9 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2 +# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2 # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. -__version__ = "2.2.0i1" - # Checkmk-Agent-Plugin - Apache Server Status # # Fetches the server-status page from detected or configured apache @@ -23,35 +21,32 @@ # It is also possible to override or extend the ssl_ports variable to make the # check contact other ports than 443 with HTTPS requests. +import contextlib import os import re import sys +from urllib.error import HTTPError, URLError +from urllib.request import build_opener, HTTPSHandler, install_opener, Request, urlopen + +__version__ = "2.4.0p36" + +USER_AGENT = "checkmk-agent-apache_status-" + __version__ if sys.version_info < (2, 6): sys.stderr.write("ERROR: Python 2.5 is not supported. Please use Python 2.6 or newer.\n") sys.exit(1) -if sys.version_info0 == 2: - from urllib2 import HTTPError, Request, URLError # pylint: disable=import-error - from urllib2 import urlopen as _urlopen # pylint: disable=import-error - - def urltype(request): - return request.get_type() -else: - from urllib.error import HTTPError, URLError # pylint: disable=import-error,no-name-in-module - from urllib.request import Request # pylint: disable=import-error,no-name-in-module - from urllib.request import urlopen as _urlopen # pylint: disable=import-error,no-name-in-module - - def urltype(request): - return request.type - - -def urlopen(request, timeout=5, cafile=None, context=None): - scheme = urltype(request) +def urlopen_(request, timeout=5, context=None): + if sys.version_info0 == 2: + scheme = request.get_type() + else: + scheme = request.type if scheme not in "http", "https": raise ValueError("Scheme '%s' is not allowed" % scheme) - return _urlopen(request, timeout=timeout, cafile=cafile, context=context) # nosec - B310 + return urlopen( # nosec B310 # BNS:6b61d9 + request, timeout=timeout, context=context + ) def get_config(): @@ -72,7 +67,7 @@ } if os.path.exists(config_file): with open(config_file) as config_file_obj: - exec(config_file_obj.read(), config) + exec(config_file_obj.read(), config) # nosec B102 # BNS:a29406 return config @@ -89,57 +84,102 @@ return "" +def parse_address_and_port(address_and_port, ssl_ports): + """ + parse address:port section from netstat or ss + return scheme (http|https), address, port + """ + server_address, _server_port = address_and_port.rsplit(":", 1) + server_port = int(_server_port) + + # Use localhost when listening globally + if server_address == "0.0.0.0": # nosec B104 # BNS:537c43 + server_address = "127.0.0.1" + elif server_address in ("::", "*", "::"): + server_address = "::1" + elif ":" in server_address and server_address0 != "": + server_address = "%s" % server_address + + # Switch protocol if port is SSL port. In case you use SSL on another + # port you would have to change/extend the ssl_port list + if server_port in ssl_ports: + scheme = "https" + else: + scheme = "http" + + return scheme, server_address, server_port + + def try_detect_servers(ssl_ports): results = - for netstat_line in os.popen("netstat -tlnp 2>/dev/null").readlines(): - parts = netstat_line.split() + procs = + "apache2", + "httpd", + "httpd-prefork", + "httpd2-prefork", + "httpd2-worker", + "httpd-worker", + "httpd.worker", + "httpd-event", + "fcgi-pm", + + + # ss lists parent and first level child processes + # last process in line is the parent: + # users:(("apache2",pid=123456,fd=3),...,("apache2",pid=123,fd=3)) + # capture content of last brackets (...)) + pattern = re.compile(r"users:.*\((^\(\)*?)\)\)$") + + for ss_line in os.popen("ss -tlnp 2>/dev/null").readlines(): + parts = ss_line.split() # Skip lines with wrong format - if len(parts) < 7 or "/" not in parts6: + if len(parts) < 6 or "users:" not in parts5: continue - pid, proc = parts6.split("/", 1) - to_replace = re.compile("^.*/") - proc = to_replace.sub("", proc) - - procs = - "apache2", - "httpd", - "httpd-prefork", - "httpd2-prefork", - "httpd2-worker", - "httpd.worker", - "httpd-event", - "fcgi-pm", - - # the pid/proc field length is limited to 19 chars. Thus in case of - # long PIDs, the process names are stripped of by that length. - # Workaround this problem here - procs = p: 19 - len(pid) - 1 for p in procs + match = re.match(pattern, parts5) + if match is None: + continue + proc_info = match.group(1) + proc, pid, _fd = proc_info.split(",") + proc = proc.replace('"', "") + pid = pid.replace("pid=", "") # Skip unwanted processes if proc not in procs: continue - server_address, _server_port = parts3.rsplit(":", 1) - server_port = int(_server_port) + scheme, server_address, server_port = parse_address_and_port(parts3, ssl_ports) - # Use localhost when listening globally - if server_address == "0.0.0.0": # nosec - B104 - server_address = "127.0.0.1" - elif server_address == "::": - server_address = "::1" - elif ":" in server_address: - server_address = "%s" % server_address + results.append((scheme, server_address, server_port)) - # Switch protocol if port is SSL port. In case you use SSL on another - # port you would have to change/extend the ssl_port list - if server_port in ssl_ports: - scheme = "https" - else: - scheme = "http" + if not results: + # if ss output was empty (maybe not installed), try netstat instead + # (plugin silently fails without any section output, + # if neither netstat nor ss are installed.) - results.append((scheme, server_address, server_port)) + for netstat_line in os.popen("netstat -tlnp 2>/dev/null").readlines(): + parts = netstat_line.split() + # Skip lines with wrong format + if len(parts) < 7 or "/" not in parts6: + continue + + pid, proc = parts6.split("/", 1) + to_replace = re.compile("^.*/") + proc = to_replace.sub("", proc) + + # the pid/proc field length is limited to 19 chars. Thus in case of + # long PIDs, the process names are stripped of by that length. + # Workaround this problem here + stripped_procs = p: 19 - len(pid) - 1 for p in procs + + # Skip unwanted processes + if proc not in stripped_procs: + continue + + scheme, server_address, server_port = parse_address_and_port(parts3, ssl_ports) + + results.append((scheme, server_address, server_port)) return results @@ -171,6 +211,13 @@ return context +def get_ssl_cafile_context(cafile): + import ssl + + context = ssl.create_default_context(cafile=cafile) + return context + + def get_response_body(proto, cafile, address, portspec, page): response = get_response(proto, cafile, address, portspec, page) return response.read().decode(get_response_charset(response)) @@ -182,42 +229,30 @@ if (sys.version_info0 == 3 and sys.version_info >= (3, 5)) or ( sys.version_info0 == 2 and sys.version_info >= (2, 7) ): - result = urlopen(request, context=get_ssl_no_verify_context(), timeout=timeout) + result = urlopen_(request, context=get_ssl_no_verify_context(), timeout=timeout) else: - if sys.version_info0 == 2: - from urllib2 import ( # pylint: disable=import-error # isort: skip - build_opener, - HTTPSHandler, - install_opener, - ) - else: - from urllib.request import ( # pylint: disable=import-error,no-name-in-module # isort: skip - build_opener, - HTTPSHandler, - install_opener, - ) install_opener(build_opener(HTTPSHandler())) - result = urlopen(request, timeout=timeout) + result = urlopen_(request, timeout=timeout) return result def get_response(proto, cafile, address, portspec, page): url = "%s://%s%s/%s?auto" % (proto, address, portspec, page) - request = Request(url, headers={"Accept": "text/plain"}) + request = Request(url, headers={"Accept": "text/plain", "User-Agent": USER_AGENT}) is_local = address in ("127.0.0.1", "::1", "localhost") # Try to fetch the status page for each server try: if proto == "https" and cafile: - return urlopen(request, cafile=cafile, timeout=5) + return urlopen_(request, context=get_ssl_cafile_context(cafile=cafile), timeout=5) if proto == "https" and is_local: return urlopen_with_ssl(request, timeout=5) - return urlopen(request, timeout=5) + return urlopen_(request, timeout=5) except URLError as exc: if "unknown protocol" in str(exc): # HACK: workaround misconfigurations where port 443 is used for # serving non ssl secured http url = "http://%s%s/server-status?auto" % (address, portspec) - return urlopen(url, timeout=5) + return urlopen_(url, timeout=5) raise @@ -235,10 +270,11 @@ for line in os.popen("omd sites").readlines(): sitename = line.split()0 path = "/opt/omd/sites/%s/etc/apache/listen-port.conf" % sitename - with open(path) as site_cfg_handle: - site_raw_conf = site_cfg_handle.readlines() + instance_name_map.setdefault("omd_sites", {}) + with contextlib.suppress(PermissionError, FileNotFoundError): + with open(path) as site_cfg_handle: + site_raw_conf = site_cfg_handle.readlines() site_conf = site_raw_conf-2.strip().split()1 - instance_name_map.setdefault("omd_sites", {}) instance_name_map"omd_sites"site_conf = sitename return instance_name_map @@ -262,7 +298,7 @@ try: response_body = get_response_body(proto, cafile, address, portspec, page) for line in response_body.split("\n"): - if not line.strip(): + if not line or line.isspace(): continue if line.lstrip()0 == "<": # Seems to be html output. Skip this server.
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.