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