diff options
Diffstat (limited to 'src/op_mode')
-rwxr-xr-x | src/op_mode/container.py | 2 | ||||
-rwxr-xr-x | src/op_mode/dns_dynamic.py (renamed from src/op_mode/dynamic_dns.py) | 2 | ||||
-rwxr-xr-x | src/op_mode/ipsec.py | 2 | ||||
-rwxr-xr-x | src/op_mode/powerctrl.py | 12 | ||||
-rwxr-xr-x | src/op_mode/reverseproxy.py | 239 | ||||
-rwxr-xr-x | src/op_mode/show_xdp_stats.sh | 7 |
6 files changed, 253 insertions, 11 deletions
diff --git a/src/op_mode/container.py b/src/op_mode/container.py index d48766a0c..7f726f076 100755 --- a/src/op_mode/container.py +++ b/src/op_mode/container.py @@ -83,7 +83,7 @@ def restart(name: str): if rc != 0: print(output) return None - print(f'Container name "{name}" restarted!') + print(f'Container "{name}" restarted!') return output diff --git a/src/op_mode/dynamic_dns.py b/src/op_mode/dns_dynamic.py index d41a74db3..76ca5249b 100755 --- a/src/op_mode/dynamic_dns.py +++ b/src/op_mode/dns_dynamic.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2023 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 diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py index db4948d7a..823bd039d 100755 --- a/src/op_mode/ipsec.py +++ b/src/op_mode/ipsec.py @@ -729,7 +729,7 @@ def _get_formatted_output_ra_summary(ra_output_list: list): sa_remotehost = sa['remote-host'] if 'remote-host' in sa else '' sa_remoteid = sa['remote-id'] if 'remote-id' in sa else '' sa_ike_proposal = _get_formatted_ike_proposal(sa) - sa_tunnel_ip = sa['remote-vips'] + sa_tunnel_ip = sa['remote-vips'][0] child_sa_key = _get_last_installed_childsa(sa) if child_sa_key: child_sa = sa['child-sas'][child_sa_key] diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py index fd4f86d88..239f766fd 100755 --- a/src/op_mode/powerctrl.py +++ b/src/op_mode/powerctrl.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2023 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 @@ -102,8 +102,18 @@ def cancel_shutdown(): else: print("Reboot or poweroff is not scheduled") +def check_unsaved_config(): + from vyos.config_mgmt import unsaved_commits + + if unsaved_commits(): + print("Warning: there are unsaved configuration changes!") + print("Run 'save' command if you do not want to lose those changes after reboot/shutdown.") + else: + pass def execute_shutdown(time, reboot=True, ask=True): + check_unsaved_config() + action = "reboot" if reboot else "poweroff" if not ask: if not ask_yes_no(f"Are you sure you want to {action} this system?"): diff --git a/src/op_mode/reverseproxy.py b/src/op_mode/reverseproxy.py new file mode 100755 index 000000000..44ffd7a37 --- /dev/null +++ b/src/op_mode/reverseproxy.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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 <http://www.gnu.org/licenses/>. + +import json +import socket +import sys +import typing + +from sys import exit +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) diff --git a/src/op_mode/show_xdp_stats.sh b/src/op_mode/show_xdp_stats.sh deleted file mode 100755 index a4ef33107..000000000 --- a/src/op_mode/show_xdp_stats.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -if cli-shell-api existsEffective interfaces $1 $2 xdp; then - /usr/sbin/xdp_stats --dev "$2" -else - echo "XDP not enabled on $2" -fi |