Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

haproxy-status: Add unix socket support as alternative to HTTP(S) #767

Open
cruelsmith opened this issue May 30, 2024 · 1 comment
Open
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@cruelsmith
Copy link

Describe the solution you'd like

HAProxy provides the possibility to use unix sockets for stats. In monitoring setups where checks can be executed local on the system that runs haproxy it provides an easy and safe way to monitor it without the need to expose the stats frontend to the network.

Below an patch which is inspired by https://github.com/m-erhardt/check-haproxy/blob/ee6ae35/check_haproxy.py#L133-L170
The patch adds the possibility to set the URL to unix://path/to/socket and receive the CSV stats by calling show stat on the socket. The parsing of the data identical and unchanged.

Additional context

From: cruelsmith <[email protected]>
Date: Fri, 24 May 2024 10:38:02 +0200
Subject: [PATCH] Add socket support for haproxy-status.py

---
 check-plugins/haproxy-status/haproxy-status.py | 65 +++++++++++++++++++++++++++++++--------
 1 file changed, 51 insertions(+), 14 deletions(-)

diff --git a/check-plugins/haproxy-status/haproxy-status.py b/check-plugins/haproxy-status/haproxy-status.py
index 8dd7a88..7101ab9 100755
@@ -14,6 +17,8 @@
 import argparse  # pylint: disable=C0413
 import base64  # pylint: disable=C0413
 import sys  # pylint: disable=C0413
+import socket  # pylint: disable=C0413
+import time  # pylint: disable=C0413
 
 import lib.args  # pylint: disable=C0413
 import lib.base  # pylint: disable=C0413
@@ -139,24 +144,59 @@ def main():
     # fetch data
     if args.TEST is None:
         url = args.URL
-        if url[0:4] != 'http':
-            lib.base.oao('--url parameter has to start with "http://" or https://".', STATE_UNKNOWN)
-        url = url + ';csv'
+        if url[0:4] != 'http' and url[0:7] != 'unix://':
+            lib.base.oao('--url parameter has to start with "http://" or https:// or unix://".', STATE_UNKNOWN)
 
         insecure = getattr(args, 'insecure', False)
 
         # fetch the url
-        if args.USERNAME and args.PASSWORD:
-            auth = '{}:{}'.format(args.USERNAME, args.PASSWORD)
-            encoded_auth = lib.txt.to_text(base64.b64encode(lib.txt.to_bytes(auth)))
-            result = lib.base.coe(
-                lib.url.fetch(
-                    url, insecure=insecure,
-                    timeout=args.TIMEOUT,
-                    header={'Authorization': 'Basic {}'.format(encoded_auth)},
-            ))
-        else:
-            result = lib.base.coe(lib.url.fetch(url, insecure=insecure, timeout=args.TIMEOUT))
+        if url[0:4] == 'http':
+            url = url + ';csv'
+            if args.USERNAME and args.PASSWORD:
+                auth = '{}:{}'.format(args.USERNAME, args.PASSWORD)
+                encoded_auth = lib.txt.to_text(base64.b64encode(lib.txt.to_bytes(auth)))
+                result = lib.base.coe(
+                    lib.url.fetch(
+                        url, insecure=insecure,
+                        timeout=args.TIMEOUT,
+                        header={'Authorization': 'Basic {}'.format(encoded_auth)},
+                ))
+            else:
+                result = lib.base.coe(lib.url.fetch(url, insecure=insecure, timeout=args.TIMEOUT))
+        elif url[0:7] == 'unix://':
+            url = url[6:]
+            try:
+                # Open connection to haproxy socket
+                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+                sock.connect(url)
+
+                # Send command
+                sock.sendall("show stat \n ".encode("ascii"))
+                time.sleep(0.1)
+
+                # Shut down sending
+                sock.shutdown(socket.SHUT_WR)
+
+                # Create buffer for receiving
+                result = ""
+
+                while True:
+                    # Write reply to buffer in chunks of 1024 bytes
+                    data = sock.recv(1024)
+                    if not data:
+                        break
+                    result += data.decode()
+
+                # Close socket connection
+                sock.close()
+            except FileNotFoundError:
+                lib.base.oao('Socket file {} not found!'.format(url), STATE_UNKNOWN)
+            except PermissionError:
+                lib.base.oao('Access to socket file {} denied!'.format(url), STATE_UNKNOWN)
+            except TimeoutError:
+                lib.base.oao('Connection to socket {} timed out!'.format(url), STATE_UNKNOWN)
+            except ConnectionError as err:
+                lib.base.oao('Error during socket connection: {}'.format(err), STATE_UNKNOWN) 
     else:
         # do not call the command, put in test data
         stdout, stderr, retc = lib.test.test(args.TEST)
-- 
2.34.1
@cruelsmith cruelsmith added the enhancement New feature or request label May 30, 2024
@markuslf markuslf self-assigned this May 31, 2024
@markuslf markuslf added this to the M006 milestone May 31, 2024
@markuslf
Copy link
Member

markuslf commented May 31, 2024

That's great, thank you! I will integrate it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants