From 2128f40f13b3b739f13c10810066d83e88cd6f0d Mon Sep 17 00:00:00 2001 From: Dennis Ahrens <dennis.ahrens@hs-hannover.de> Date: Thu, 4 Mar 2021 14:09:01 +0100 Subject: [PATCH] php_fpm_pools --- README.md | 4 + checkmk/checkmk-files/php_fpm_pools | 155 ++++++++++++++++++ checkmk/debian/php_fpm_pools/init.sls | 15 ++ .../debian/php_fpm_pools/php_fpm_pools.cfg | 3 + 4 files changed, 177 insertions(+) create mode 100644 checkmk/checkmk-files/php_fpm_pools create mode 100644 checkmk/debian/php_fpm_pools/init.sls create mode 100644 checkmk/debian/php_fpm_pools/php_fpm_pools.cfg diff --git a/README.md b/README.md index 05714d2..599ac78 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,10 @@ Installs the [netstat.linux](https://checkmk.com/cms_check_netstat.html) agent p Installs the [nginx_status](https://checkmk.com/cms_check_nginx_status.html) agent plugin. +### `checkmk.debian.php_fpm_pools` + +Install the [php_fpm_pools](https://exchange.checkmk.com/p/php-fpm) agent plugin. + ### `checkmk.debian.radsec` Installs the radsec plugin. diff --git a/checkmk/checkmk-files/php_fpm_pools b/checkmk/checkmk-files/php_fpm_pools new file mode 100644 index 0000000..588fe8d --- /dev/null +++ b/checkmk/checkmk-files/php_fpm_pools @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# -*- encoding: utf-8; py-indent-offset: 4 -*- +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2016 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +# Check_MK-Agent-Plugin - php-fpm Status +# +# By default this plugin tries to detect all locally running php-fpm processes +# and to monitor them. If this is not good for your environment you might +# create an php-fpm.cfg file in MK_CONFDIR and populate the servers +# list to prevent executing the detection mechanism. + +# sample configuration: +# php_fpm = [ +# {"socket": ("localhost", 9000)}, +# {"socket": "/var/run/php/path_to.sock", "path": "/fpm_status"}, +# ] + +import os, json, sys, socket, struct, traceback + +# thanks to Milosz Galazka for sharing this code +# https://blog.sleeplessbeastie.eu/2019/04/01/how-to-display-php-fpm-pool-information-using-unix-socket-and-python-script/ +class FCGIStatusClient: + # FCGI protocol version + FCGI_VERSION = 1 + + # FCGI record types + FCGI_BEGIN_REQUEST = 1 + FCGI_PARAMS = 4 + + # FCGI roles + FCGI_RESPONDER = 1 + + # FCGI header length + FCGI_HEADER_LENGTH = 8 + + def __init__(self, socket_path, socket_timeout = 5.0, status_path = "/status" ): + # socket_path of type tuple is TCP + if type(socket_path) is tuple: + self.socket = socket.create_connection(socket_path, socket_timeout) + else: + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket.settimeout(socket_timeout) + self.socket.connect(socket_path) + self.status_path = status_path + self.request_id = 1 + + self.params = { + 'SCRIPT_NAME': status_path, + 'SCRIPT_FILENAME': status_path, + 'QUERY_STRING': 'json', + 'REQUEST_METHOD': 'GET', + } + + def close(self): + self.socket.close() + + def define_begin_request(self): + fcgi_begin_request = struct.pack("!HB5x", self.FCGI_RESPONDER, 0) + fcgi_header = struct.pack("!BBHHBx", self.FCGI_VERSION, self.FCGI_BEGIN_REQUEST, self.request_id, len(fcgi_begin_request), 0) + self.fcgi_begin_request = fcgi_header + fcgi_begin_request + + def define_parameters(self): + parameters = [] + for name, value in self.params.items(): + parameters.append(chr(len(name)) + chr(len(value)) + name + value) + + parameters = ''.join(parameters) + parameters_length = len(parameters) + parameters_padding_req = parameters_length & 7 + parameters_padding = b'\x00' * parameters_padding_req + + fcgi_header_start = struct.pack("!BBHHBx", self.FCGI_VERSION, self.FCGI_PARAMS, self.request_id, parameters_length , parameters_padding_req) + fcgi_header_end = struct.pack("!BBHHBx", self.FCGI_VERSION, self.FCGI_PARAMS, self.request_id, 0, 0) + self.fcgi_params = fcgi_header_start + parameters.encode() + parameters_padding + fcgi_header_end + + def execute(self): + try: + self.socket.send(self.fcgi_begin_request) + self.socket.send(self.fcgi_params) + + header = self.socket.recv(self.FCGI_HEADER_LENGTH) + fcgi_version, request_type, request_id, request_length, request_padding = struct.unpack("!BBHHBx", header) + + if request_type == 6: + self.raw_status_data=self.socket.recv(request_length) + self.status_data = self.raw_status_data.decode().split("\r\n\r\n")[1] + else: + self.raw_status_data = "" + if request_type == 7: + raise Exception("Received an error packet.") + else: + raise Exception("Received unexpected packet type.") + + except Exception as e: + sys.stderr.write(str(e) + "\n") + + def make_request(self): + self.define_begin_request() + self.define_parameters() + self.execute() + self.close() + + def print_status(self): + if hasattr(self, 'status_data'): + data = json.loads(self.status_data) + pool_name = data.pop('pool', None) + pm_type = data.pop('process manager', None) + for key in data: + spaceless_key = "_".join( key.split() ) + sys.stdout.write("%s %s %s %s\n" % (pool_name, pm_type, spaceless_key, str(data[key]))) + +config_file=os.path.join(os.environ.get("MK_CONFDIR", "/etc/check_mk"), "php_fpm_pools.cfg") + +# None or list of urls. +php_fpm = None + +if os.path.exists(config_file): + execfile(config_file) + +if not php_fpm: + print("Config file does not exist or is empty!") + sys.exit(3) + +sys.stdout.write('<<<php_fpm_pools>>>\n') +for fpm_status_raw in php_fpm: + try: + fpm_status = dict(fpm_status_raw) + fcgi_client = FCGIStatusClient( socket_path = fpm_status.get('socket'), status_path = fpm_status.get('path', '/status') ) + fcgi_client.make_request() + fcgi_client.print_status() + + except Exception as e: + sys.stderr.write('Exception (%s): %s\n' % (fpm_status.get('socket'), e)) diff --git a/checkmk/debian/php_fpm_pools/init.sls b/checkmk/debian/php_fpm_pools/init.sls new file mode 100644 index 0000000..ad8b1d6 --- /dev/null +++ b/checkmk/debian/php_fpm_pools/init.sls @@ -0,0 +1,15 @@ +phpfpm_status_plugin: + file.managed: + - name: /usr/lib/check_mk_agent/plugins/php_fpm_pools + - source: salt://checkmk/checkmk-files/php_fpm_pools + - mode: 755 + - user: root + - group: root + +phpfpm_status_plugin_conf: + file.managed: + - name: /etc/check_mk/php_fpm_pools.cfg + - source: salt://checkmk/debian/php_fpm_pools/php_fpm_pools.cfg + - mode: 755 + - user: root + - group: root \ No newline at end of file diff --git a/checkmk/debian/php_fpm_pools/php_fpm_pools.cfg b/checkmk/debian/php_fpm_pools/php_fpm_pools.cfg new file mode 100644 index 0000000..0d7eb9f --- /dev/null +++ b/checkmk/debian/php_fpm_pools/php_fpm_pools.cfg @@ -0,0 +1,3 @@ +php_fpm = [ + {"socket": "/var/run/php/php7-fpm.sock", "path": "/status"}, +] \ No newline at end of file -- GitLab