From 90a4827284acd3cb072cdfeef323c522802c6449 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:55:11 +0200 Subject: haproxy: T6745: Rename `reverse-proxy` to `haproxy` --- src/op_mode/load-balancing_haproxy.py | 237 ++++++++++++++++++++++++++++++++++ src/op_mode/restart.py | 10 +- src/op_mode/reverseproxy.py | 237 ---------------------------------- 3 files changed, 242 insertions(+), 242 deletions(-) create mode 100755 src/op_mode/load-balancing_haproxy.py delete mode 100755 src/op_mode/reverseproxy.py (limited to 'src/op_mode') diff --git a/src/op_mode/load-balancing_haproxy.py b/src/op_mode/load-balancing_haproxy.py new file mode 100755 index 000000000..ae6734e16 --- /dev/null +++ b/src/op_mode/load-balancing_haproxy.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023-2024 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import json +import socket +import sys + +from tabulate import tabulate +from vyos.configquery import ConfigTreeQuery + +import vyos.opmode + +socket_path = '/run/haproxy/admin.sock' +timeout = 5 + + +def _execute_haproxy_command(command): + """Execute a command on the HAProxy UNIX socket and retrieve the response. + + Args: + command (str): The command to be executed. + + Returns: + str: The response received from the HAProxy UNIX socket. + + Raises: + socket.error: If there is an error while connecting or communicating with the socket. + + Finally: + Closes the socket connection. + + Notes: + - HAProxy expects a newline character at the end of the command. + - The socket connection is established using the HAProxy UNIX socket. + - The response from the socket is received and decoded. + + Example: + response = _execute_haproxy_command('show stat') + print(response) + """ + try: + # HAProxy expects new line for command + command = f'{command}\n' + + # Connect to the HAProxy UNIX socket + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(socket_path) + + # Set the socket timeout + sock.settimeout(timeout) + + # Send the command + sock.sendall(command.encode()) + + # Receive and decode the response + response = b'' + while True: + data = sock.recv(4096) + if not data: + break + response += data + response = response.decode() + + return (response) + + except socket.error as e: + print(f"Error: {e}") + + finally: + # Close the socket + sock.close() + + +def _convert_seconds(seconds): + """Convert seconds to days, hours, minutes, and seconds. + + Args: + seconds (int): The number of seconds to convert. + + Returns: + tuple: A tuple containing the number of days, hours, minutes, and seconds. + """ + minutes = seconds // 60 + hours = minutes // 60 + days = hours // 24 + + return days, hours % 24, minutes % 60, seconds % 60 + + +def _last_change_format(seconds): + """Format the time components into a string representation. + + Args: + seconds (int): The total number of seconds. + + Returns: + str: The formatted time string with days, hours, minutes, and seconds. + + Examples: + >>> _last_change_format(1434) + '23m54s' + >>> _last_change_format(93734) + '1d0h23m54s' + >>> _last_change_format(85434) + '23h23m54s' + """ + days, hours, minutes, seconds = _convert_seconds(seconds) + time_format = "" + + if days: + time_format += f"{days}d" + if hours: + time_format += f"{hours}h" + if minutes: + time_format += f"{minutes}m" + if seconds: + time_format += f"{seconds}s" + + return time_format + + +def _get_json_data(): + """Get haproxy data format JSON""" + return _execute_haproxy_command('show stat json') + + +def _get_raw_data(): + """Retrieve raw data from JSON and organize it into a dictionary. + + Returns: + dict: A dictionary containing the organized data categorized + into frontend, backend, and server. + """ + + data = json.loads(_get_json_data()) + lb_dict = {'frontend': [], 'backend': [], 'server': []} + + for key in data: + frontend = [] + backend = [] + server = [] + for entry in key: + obj_type = entry['objType'].lower() + position = entry['field']['pos'] + name = entry['field']['name'] + value = entry['value']['value'] + + dict_entry = {'pos': position, 'name': {name: value}} + + if obj_type == 'frontend': + frontend.append(dict_entry) + elif obj_type == 'backend': + backend.append(dict_entry) + elif obj_type == 'server': + server.append(dict_entry) + + if len(frontend) > 0: + lb_dict['frontend'].append(frontend) + if len(backend) > 0: + lb_dict['backend'].append(backend) + if len(server) > 0: + lb_dict['server'].append(server) + + return lb_dict + + +def _get_formatted_output(data): + """ + Format the data into a tabulated output. + + Args: + data (dict): The data to be formatted. + + Returns: + str: The tabulated output representing the formatted data. + """ + table = [] + headers = [ + "Proxy name", "Role", "Status", "Req rate", "Resp time", "Last change" + ] + + for key in data: + for item in data[key]: + row = [None] * len(headers) + + for element in item: + if 'pxname' in element['name']: + row[0] = element['name']['pxname'] + elif 'svname' in element['name']: + row[1] = element['name']['svname'] + elif 'status' in element['name']: + row[2] = element['name']['status'] + elif 'req_rate' in element['name']: + row[3] = element['name']['req_rate'] + elif 'rtime' in element['name']: + row[4] = f"{element['name']['rtime']} ms" + elif 'lastchg' in element['name']: + row[5] = _last_change_format(element['name']['lastchg']) + table.append(row) + + out = tabulate(table, headers, numalign="left") + return out + + +def show(raw: bool): + config = ConfigTreeQuery() + if not config.exists('load-balancing haproxy'): + raise vyos.opmode.UnconfiguredSubsystem('Haproxy is not configured') + + data = _get_raw_data() + if raw: + return data + else: + return _get_formatted_output(data) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) diff --git a/src/op_mode/restart.py b/src/op_mode/restart.py index a83c8b9d8..3b0031f34 100755 --- a/src/op_mode/restart.py +++ b/src/op_mode/restart.py @@ -41,6 +41,10 @@ service_map = { 'systemd_service': 'pdns-recursor', 'path': ['service', 'dns', 'forwarding'], }, + 'haproxy': { + 'systemd_service': 'haproxy', + 'path': ['load-balancing', 'haproxy'], + }, 'igmp_proxy': { 'systemd_service': 'igmpproxy', 'path': ['protocols', 'igmp-proxy'], @@ -53,10 +57,6 @@ service_map = { 'systemd_service': 'avahi-daemon', 'path': ['service', 'mdns', 'repeater'], }, - 'reverse_proxy': { - 'systemd_service': 'haproxy', - 'path': ['load-balancing', 'reverse-proxy'], - }, 'router_advert': { 'systemd_service': 'radvd', 'path': ['service', 'router-advert'], @@ -83,10 +83,10 @@ services = typing.Literal[ 'dhcpv6', 'dns_dynamic', 'dns_forwarding', + 'haproxy', 'igmp_proxy', 'ipsec', 'mdns_repeater', - 'reverse_proxy', 'router_advert', 'snmp', 'ssh', diff --git a/src/op_mode/reverseproxy.py b/src/op_mode/reverseproxy.py deleted file mode 100755 index 19704182a..000000000 --- a/src/op_mode/reverseproxy.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2023-2024 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import json -import socket -import sys - -from tabulate import tabulate -from vyos.configquery import ConfigTreeQuery - -import vyos.opmode - -socket_path = '/run/haproxy/admin.sock' -timeout = 5 - - -def _execute_haproxy_command(command): - """Execute a command on the HAProxy UNIX socket and retrieve the response. - - Args: - command (str): The command to be executed. - - Returns: - str: The response received from the HAProxy UNIX socket. - - Raises: - socket.error: If there is an error while connecting or communicating with the socket. - - Finally: - Closes the socket connection. - - Notes: - - HAProxy expects a newline character at the end of the command. - - The socket connection is established using the HAProxy UNIX socket. - - The response from the socket is received and decoded. - - Example: - response = _execute_haproxy_command('show stat') - print(response) - """ - try: - # HAProxy expects new line for command - command = f'{command}\n' - - # Connect to the HAProxy UNIX socket - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(socket_path) - - # Set the socket timeout - sock.settimeout(timeout) - - # Send the command - sock.sendall(command.encode()) - - # Receive and decode the response - response = b'' - while True: - data = sock.recv(4096) - if not data: - break - response += data - response = response.decode() - - return (response) - - except socket.error as e: - print(f"Error: {e}") - - finally: - # Close the socket - sock.close() - - -def _convert_seconds(seconds): - """Convert seconds to days, hours, minutes, and seconds. - - Args: - seconds (int): The number of seconds to convert. - - Returns: - tuple: A tuple containing the number of days, hours, minutes, and seconds. - """ - minutes = seconds // 60 - hours = minutes // 60 - days = hours // 24 - - return days, hours % 24, minutes % 60, seconds % 60 - - -def _last_change_format(seconds): - """Format the time components into a string representation. - - Args: - seconds (int): The total number of seconds. - - Returns: - str: The formatted time string with days, hours, minutes, and seconds. - - Examples: - >>> _last_change_format(1434) - '23m54s' - >>> _last_change_format(93734) - '1d0h23m54s' - >>> _last_change_format(85434) - '23h23m54s' - """ - days, hours, minutes, seconds = _convert_seconds(seconds) - time_format = "" - - if days: - time_format += f"{days}d" - if hours: - time_format += f"{hours}h" - if minutes: - time_format += f"{minutes}m" - if seconds: - time_format += f"{seconds}s" - - return time_format - - -def _get_json_data(): - """Get haproxy data format JSON""" - return _execute_haproxy_command('show stat json') - - -def _get_raw_data(): - """Retrieve raw data from JSON and organize it into a dictionary. - - Returns: - dict: A dictionary containing the organized data categorized - into frontend, backend, and server. - """ - - data = json.loads(_get_json_data()) - lb_dict = {'frontend': [], 'backend': [], 'server': []} - - for key in data: - frontend = [] - backend = [] - server = [] - for entry in key: - obj_type = entry['objType'].lower() - position = entry['field']['pos'] - name = entry['field']['name'] - value = entry['value']['value'] - - dict_entry = {'pos': position, 'name': {name: value}} - - if obj_type == 'frontend': - frontend.append(dict_entry) - elif obj_type == 'backend': - backend.append(dict_entry) - elif obj_type == 'server': - server.append(dict_entry) - - if len(frontend) > 0: - lb_dict['frontend'].append(frontend) - if len(backend) > 0: - lb_dict['backend'].append(backend) - if len(server) > 0: - lb_dict['server'].append(server) - - return lb_dict - - -def _get_formatted_output(data): - """ - Format the data into a tabulated output. - - Args: - data (dict): The data to be formatted. - - Returns: - str: The tabulated output representing the formatted data. - """ - table = [] - headers = [ - "Proxy name", "Role", "Status", "Req rate", "Resp time", "Last change" - ] - - for key in data: - for item in data[key]: - row = [None] * len(headers) - - for element in item: - if 'pxname' in element['name']: - row[0] = element['name']['pxname'] - elif 'svname' in element['name']: - row[1] = element['name']['svname'] - elif 'status' in element['name']: - row[2] = element['name']['status'] - elif 'req_rate' in element['name']: - row[3] = element['name']['req_rate'] - elif 'rtime' in element['name']: - row[4] = f"{element['name']['rtime']} ms" - elif 'lastchg' in element['name']: - row[5] = _last_change_format(element['name']['lastchg']) - table.append(row) - - out = tabulate(table, headers, numalign="left") - return out - - -def show(raw: bool): - config = ConfigTreeQuery() - if not config.exists('load-balancing reverse-proxy'): - raise vyos.opmode.UnconfiguredSubsystem('Reverse-proxy is not configured') - - data = _get_raw_data() - if raw: - return data - else: - return _get_formatted_output(data) - - -if __name__ == '__main__': - try: - res = vyos.opmode.run(sys.modules[__name__]) - if res: - print(res) - except (ValueError, vyos.opmode.Error) as e: - print(e) - sys.exit(1) -- cgit v1.2.3