diff options
26 files changed, 282 insertions, 287 deletions
diff --git a/data/op-mode-standardized.json b/data/op-mode-standardized.json index 3205c7d20..abf562984 100644 --- a/data/op-mode-standardized.json +++ b/data/op-mode-standardized.json @@ -2,6 +2,7 @@ "accelppp.py", "bgp.py", "bridge.py", +"config_mgmt.py", "conntrack.py", "container.py", "cpu.py", diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in index 4bb3b1537..6cb40247a 100644 --- a/interface-definitions/high-availability.xml.in +++ b/interface-definitions/high-availability.xml.in @@ -232,16 +232,15 @@ <properties> <help>Virtual IP address</help> <valueHelp> - <format>ipv4</format> - <description>IPv4 virtual address</description> + <format>ipv4net</format> + <description>IPv4 address and prefix length</description> </valueHelp> <valueHelp> - <format>ipv6</format> - <description>IPv6 virtual address</description> + <format>ipv6net</format> + <description>IPv6 address and prefix length</description> </valueHelp> <constraint> - <validator name="ipv4-host"/> - <validator name="ipv6-host"/> + <validator name="ip-host"/> </constraint> </properties> <children> diff --git a/op-mode-definitions/generate-interfaces-debug-archive.xml.in b/op-mode-definitions/generate-interfaces-debug-archive.xml.in new file mode 100644 index 000000000..5e4f4daad --- /dev/null +++ b/op-mode-definitions/generate-interfaces-debug-archive.xml.in @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="generate"> + <children> + <node name="interfaces"> + <properties> + <help>Interface specific commands</help> + </properties> + <children> + <node name="debug-archive"> + <properties> + <help>Generate interfaces debug-archive</help> + </properties> + <command>${vyos_op_scripts_dir}/generate_interfaces_debug_archive.py</command> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/generate-openvpn-config-client.xml.in b/op-mode-definitions/generate-openvpn-config-client.xml.in index 4f9f31bfe..baec0842b 100644 --- a/op-mode-definitions/generate-openvpn-config-client.xml.in +++ b/op-mode-definitions/generate-openvpn-config-client.xml.in @@ -16,7 +16,7 @@ <properties> <help>Local interface used for connection</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py --type openvpn</script> + <path>interfaces openvpn</path> </completionHelp> </properties> <children> diff --git a/op-mode-definitions/generate-wireguard.xml.in b/op-mode-definitions/generate-wireguard.xml.in index 0ef983cd2..6c01619be 100644 --- a/op-mode-definitions/generate-wireguard.xml.in +++ b/op-mode-definitions/generate-wireguard.xml.in @@ -19,7 +19,7 @@ <properties> <help>Local interface used for connection</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py --type wireguard</script> + <path>interfaces wireguard</path> </completionHelp> </properties> <children> diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in index d75caf308..46e416a8a 100644 --- a/op-mode-definitions/ipv6-route.xml.in +++ b/op-mode-definitions/ipv6-route.xml.in @@ -26,7 +26,7 @@ <properties> <help>Show IPv6 neighbor table for specified interface</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py -b</script> + <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script> </completionHelp> </properties> <command>${vyos_op_scripts_dir}/neighbor.py show --family inet6 --interface "$5"</command> diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index b2763da81..0a2657398 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -20,7 +20,7 @@ <properties> <help>Reset OpenVPN process on interface</help> <completionHelp> - <script>sudo ${vyos_completion_dir}/list_interfaces.py --type openvpn</script> + <path>interfaces openvpn</path> </completionHelp> </properties> <command>sudo ${vyos_op_scripts_dir}/openvpn.py reset --interface $4</command> @@ -51,7 +51,7 @@ <properties> <help>Show OpenVPN interface information</help> <completionHelp> - <script>sudo ${vyos_completion_dir}/list_interfaces.py --type openvpn</script> + <path>interfaces openvpn</path> </completionHelp> </properties> <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name=$4</command> diff --git a/op-mode-definitions/show-arp.xml.in b/op-mode-definitions/show-arp.xml.in index 8662549fc..3680c20c6 100644 --- a/op-mode-definitions/show-arp.xml.in +++ b/op-mode-definitions/show-arp.xml.in @@ -12,7 +12,7 @@ <properties> <help>Show Address Resolution Protocol (ARP) cache for specified interface</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py -b</script> + <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script> </completionHelp> </properties> <command>${vyos_op_scripts_dir}/neighbor.py show --family inet --interface "$4"</command> diff --git a/op-mode-definitions/show-bridge.xml.in b/op-mode-definitions/show-bridge.xml.in index dd2a28931..e7a646fdc 100644 --- a/op-mode-definitions/show-bridge.xml.in +++ b/op-mode-definitions/show-bridge.xml.in @@ -25,7 +25,7 @@ <properties> <help>Show bridge information for a given bridge interface</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py --type bridge</script> + <path>interfaces bridge</path> </completionHelp> </properties> <command>bridge -c link show | grep "master $3"</command> diff --git a/op-mode-definitions/show-interfaces-wireguard.xml.in b/op-mode-definitions/show-interfaces-wireguard.xml.in index eba8de568..75b0cc88e 100644 --- a/op-mode-definitions/show-interfaces-wireguard.xml.in +++ b/op-mode-definitions/show-interfaces-wireguard.xml.in @@ -8,7 +8,7 @@ <properties> <help>Show specified WireGuard interface information</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py --type wireguard</script> + <path>interfaces wireguard</path> </completionHelp> </properties> <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=wireguard</command> diff --git a/op-mode-definitions/show-interfaces-wireless.xml.in b/op-mode-definitions/show-interfaces-wireless.xml.in index b0a272225..cdd591f82 100644 --- a/op-mode-definitions/show-interfaces-wireless.xml.in +++ b/op-mode-definitions/show-interfaces-wireless.xml.in @@ -28,7 +28,7 @@ <properties> <help>Show specified wireless interface information</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py --type wireless</script> + <path>interfaces wireless</path> </completionHelp> </properties> <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=wireless</command> diff --git a/op-mode-definitions/show-ip-multicast.xml.in b/op-mode-definitions/show-ip-multicast.xml.in index 80d83b424..605d61e8d 100644 --- a/op-mode-definitions/show-ip-multicast.xml.in +++ b/op-mode-definitions/show-ip-multicast.xml.in @@ -13,13 +13,7 @@ <properties> <help>Show multicast interfaces</help> </properties> - <command>if ps -C igmpproxy &>/dev/null; then ${vyos_op_scripts_dir}/show_igmpproxy.py --interface; else echo IGMP proxy not configured; fi</command> - </leafNode> - <leafNode name="mfc"> - <properties> - <help>Show multicast fowarding cache</help> - </properties> - <command>if ps -C igmpproxy &>/dev/null; then ${vyos_op_scripts_dir}/show_igmpproxy.py --mfc; else echo IGMP proxy not configured; fi</command> + <command>${vyos_op_scripts_dir}/igmp-proxy.py show_interface</command> </leafNode> <leafNode name="summary"> <properties> diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in index 0751c50cb..a710e33d2 100644 --- a/op-mode-definitions/show-ip.xml.in +++ b/op-mode-definitions/show-ip.xml.in @@ -17,7 +17,7 @@ <properties> <help>Show IPv4 neighbor table for specified interface</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py -b</script> + <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script> </completionHelp> </properties> <command>${vyos_op_scripts_dir}/neighbor.py show --family inet --interface "$5"</command> diff --git a/op-mode-definitions/show-protocols.xml.in b/op-mode-definitions/show-protocols.xml.in index 698001b76..27146f90d 100644 --- a/op-mode-definitions/show-protocols.xml.in +++ b/op-mode-definitions/show-protocols.xml.in @@ -22,7 +22,7 @@ <properties> <help>Show Address Resolution Protocol (ARP) cache for specified interface</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py -b</script> + <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script> </completionHelp> </properties> <command>/usr/sbin/arp -e -n -i "$6"</command> diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in index 746601f43..85bfdcdba 100644 --- a/op-mode-definitions/show-system.xml.in +++ b/op-mode-definitions/show-system.xml.in @@ -29,13 +29,13 @@ <properties> <help>Compare config file revisions</help> </properties> - <command>${vyos_op_scripts_dir}/config_mgmt.py show_commit_diff --rev "$5" --revb "$7"</command> + <command>${vyos_op_scripts_dir}/config_mgmt.py show_commit_diff --rev "$5" --rev2 "$7"</command> <children> <leafNode name="commands"> <properties> <help>Compare config file revision commands</help> </properties> - <command>${vyos_op_scripts_dir}/config_mgmt.py show_commit_diff --rev "$5" --revb "$7" --commands</command> + <command>${vyos_op_scripts_dir}/config_mgmt.py show_commit_diff --rev "$5" --rev2 "$7" --commands</command> </leafNode> </children> </tagNode> diff --git a/op-mode-definitions/wireless.xml.in b/op-mode-definitions/wireless.xml.in index 5d9db1544..f8e53ad21 100644 --- a/op-mode-definitions/wireless.xml.in +++ b/op-mode-definitions/wireless.xml.in @@ -21,7 +21,7 @@ <properties> <help>Clear interface information for a given wireless interface</help> <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py --type wireless</script> + <path>interfaces wireless</path> </completionHelp> </properties> <children> diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index 30e893d74..af2c7b28b 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -1,4 +1,4 @@ -# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2022-2023 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,6 +22,10 @@ from humps import decamelize class Error(Exception): """ Any error that makes requested operation impossible to complete for reasons unrelated to the user input or script logic. + + This is the base class, scripts should not use it directly + and should raise more specific errors instead, + whenever possible. """ pass @@ -45,6 +49,13 @@ class PermissionDenied(Error): """ pass +class InsufficientResources(Error): + """ Requested operation and its arguments are valid but the system + does not have enough resources (such as drive space or memory) + to complete it. + """ + pass + class UnsupportedOperation(Error): """ Requested operation is technically valid but is not implemented yet. """ pass @@ -217,6 +228,9 @@ def run(module): if not args["raw"]: return res else: + if not isinstance(res, dict) and not isinstance(res, list): + raise InternalError(f"Bare literal is not an acceptable raw output, must be a list or an object.\ + The output was:{res}") res = decamelize(res) res = _normalize_field_names(res) from json import dumps diff --git a/src/op_mode/config_mgmt.py b/src/op_mode/config_mgmt.py index 0ffb1468b..66de26d1f 100755 --- a/src/op_mode/config_mgmt.py +++ b/src/op_mode/config_mgmt.py @@ -20,17 +20,17 @@ import typing import vyos.opmode from vyos.config_mgmt import ConfigMgmt -def show_commit_diff(raw: bool, rev: int, revb: typing.Optional[int], +def show_commit_diff(raw: bool, rev: int, rev2: typing.Optional[int], commands: bool): config_mgmt = ConfigMgmt() - config_diff = config_mgmt.show_commit_diff(rev, revb, commands) + config_diff = config_mgmt.show_commit_diff(rev, rev2, commands) if raw: - revb = (rev+1) if revb is None else revb + rev2 = (rev+1) if rev2 is None else rev2 if commands: - d = {f'config_command_diff_{revb}_{rev}': config_diff} + d = {f'config_command_diff_{rev2}_{rev}': config_diff} else: - d = {f'config_file_diff_{revb}_{rev}': config_diff} + d = {f'config_file_diff_{rev2}_{rev}': config_diff} return d return config_diff diff --git a/src/op_mode/generate_interfaces_debug_archive.py b/src/op_mode/generate_interfaces_debug_archive.py new file mode 100755 index 000000000..f5767080a --- /dev/null +++ b/src/op_mode/generate_interfaces_debug_archive.py @@ -0,0 +1,115 @@ +#!/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/>. + +from datetime import datetime +from pathlib import Path +from shutil import rmtree +from socket import gethostname +from sys import exit +from tarfile import open as tar_open +from vyos.util import rc_cmd +import os + +# define a list of commands that needs to be executed + +CMD_LIST: list[str] = [ + "journalctl -b -n 500", + "journalctl -b -k -n 500", + "ip -s l", + "cat /proc/interrupts", + "cat /proc/softirqs", + "top -b -d 1 -n 2 -1", + "netstat -l", + "cat /proc/net/dev", + "cat /proc/net/softnet_stat", + "cat /proc/net/icmp", + "cat /proc/net/udp", + "cat /proc/net/tcp", + "cat /proc/net/netstat", + "sysctl net", + "timeout 10 tcpdump -c 500 -eni any port not 22" +] + +CMD_INTERFACES_LIST: list[str] = [ + "ethtool -i ", + "ethtool -S ", + "ethtool -g ", + "ethtool -c ", + "ethtool -a ", + "ethtool -k ", + "ethtool -i ", + "ethtool --phy-statistics " +] + +# get intefaces info +interfaces_list = os.popen('ls /sys/class/net/').read().split() + +# modify CMD_INTERFACES_LIST for all interfaces +CMD_INTERFACES_LIST_MOD=[] +for command_interface in interfaces_list: + for command_interfacev2 in CMD_INTERFACES_LIST: + CMD_INTERFACES_LIST_MOD.append (f'{command_interfacev2}{command_interface}') + +# execute a command and save the output to a file + +def save_stdout(command: str, file: Path) -> None: + rc, stdout = rc_cmd(command) + body: str = f'''### {command} ### +Command: {command} +Exit code: {rc} +Stdout: +{stdout} + +''' + with file.open(mode='a') as f: + f.write(body) + +# get local host name +hostname: str = gethostname() +# get current time +time_now: str = datetime.now().isoformat(timespec='seconds') + +# define a temporary directory for logs and collected data +tmp_dir: Path = Path(f'/tmp/drops-debug_{time_now}') +# set file paths +drops_file: Path = Path(f'{tmp_dir}/drops.txt') +interfaces_file: Path = Path(f'{tmp_dir}/interfaces.txt') +archive_file: str = f'/tmp/packet-drops-debug_{time_now}.tar.bz2' + +# create files +tmp_dir.mkdir() +drops_file.touch() +interfaces_file.touch() + +try: + # execute all commands + for command in CMD_LIST: + save_stdout(command, drops_file) + for command_interface in CMD_INTERFACES_LIST_MOD: + save_stdout(command_interface, interfaces_file) + + # create an archive + with tar_open(name=archive_file, mode='x:bz2') as tar_file: + tar_file.add(tmp_dir) + + # inform user about success + print(f'Debug file is generated and located in {archive_file}') +except Exception as err: + print(f'Error during generating a debug file: {err}') +finally: + # cleanup + rmtree(tmp_dir) + exit() diff --git a/src/op_mode/igmp-proxy.py b/src/op_mode/igmp-proxy.py new file mode 100755 index 000000000..0086c9aa6 --- /dev/null +++ b/src/op_mode/igmp-proxy.py @@ -0,0 +1,99 @@ +#!/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/>. + +# File: show_igmpproxy.py +# Purpose: +# Display istatistics from IPv4 IGMP proxy. +# Used by the "run show ip multicast" command tree. + +import ipaddress +import json +import socket +import sys +import tabulate + +import vyos.config +import vyos.opmode + +from vyos.util import bytes_to_human, print_error + +def _is_configured(): + """Check if IGMP proxy is configured""" + return vyos.config.Config().exists_effective('protocols igmp-proxy') + +def _is_running(): + """Check if IGMP proxy is currently running""" + return not vyos.util.run('ps -C igmpproxy') + +def _kernel_to_ip(addr): + """ + Convert any given address from Linux kernel to a proper, IPv4 address + using the correct host byte order. + """ + # Convert from hex 'FE000A0A' to decimal '4261415434' + addr = int(addr, 16) + # Kernel ABI _always_ uses network byte order. + addr = socket.ntohl(addr) + return str(ipaddress.IPv4Address(addr)) + +def _process_mr_vif(): + """Read rows from /proc/net/ip_mr_vif into dicts.""" + result = [] + with open('/proc/net/ip_mr_vif', 'r') as f: + next(f) + for line in f: + result.append({ + 'Interface': line.split()[1], + 'PktsIn' : int(line.split()[3]), + 'PktsOut' : int(line.split()[5]), + 'BytesIn' : int(line.split()[2]), + 'BytesOut' : int(line.split()[4]), + 'Local' : _kernel_to_ip(line.split()[7]), + }) + return result + +def show_interface(raw: bool): + if data := _process_mr_vif(): + if raw: + # Make the interface name the key for each row. + table = {} + for v in data: + table[v.pop('Interface')] = v + return json.loads(json.dumps(table)) + # Make byte values human-readable for the table. + arr = [] + for x in data: + arr.append({k: bytes_to_human(v) if k.startswith('Bytes') \ + else v for k, v in x.items()}) + return tabulate.tabulate(arr, headers='keys') + + +if not _is_configured(): + print_error('IGMP proxy is not configured.') + sys.exit(0) +if not _is_running(): + print_error('IGMP proxy is not running.') + sys.exit(0) + + +if __name__ == "__main__": + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print_error(e) + sys.exit(1) diff --git a/src/op_mode/show_igmpproxy.py b/src/op_mode/show_igmpproxy.py deleted file mode 100755 index 4714e494b..000000000 --- a/src/op_mode/show_igmpproxy.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018 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/>. - -# File: show_igmpproxy.py -# Purpose: -# Display istatistics from IPv4 IGMP proxy. -# Used by the "run show ip multicast" command tree. - -import sys -import jinja2 -import argparse -import ipaddress -import socket - -import vyos.config - -# Output Template for "show ip multicast interface" command -# -# Example: -# Interface BytesIn PktsIn BytesOut PktsOut Local -# eth0 0.0b 0 0.0b 0 xxx.xxx.xxx.65 -# eth1 0.0b 0 0.0b 0 xxx.xxx.xx.201 -# eth0.3 0.0b 0 0.0b 0 xxx.xxx.x.7 -# tun1 0.0b 0 0.0b 0 xxx.xxx.xxx.2 -vif_out_tmpl = """ -{% for r in data %} -{{ "%-10s"|format(r.interface) }} {{ "%-12s"|format(r.bytes_in) }} {{ "%-12s"|format(r.pkts_in) }} {{ "%-12s"|format(r.bytes_out) }} {{ "%-12s"|format(r.pkts_out) }} {{ "%-15s"|format(r.loc) }} -{% endfor %} -""" - -# Output Template for "show ip multicast mfc" command -# -# Example: -# Group Origin In Out Pkts Bytes Wrong -# xxx.xxx.xxx.250 xxx.xx.xxx.75 -- -# xxx.xxx.xx.124 xx.xxx.xxx.26 -- -mfc_out_tmpl = """ -{% for r in data %} -{{ "%-15s"|format(r.group) }} {{ "%-15s"|format(r.origin) }} {{ "%-12s"|format(r.pkts) }} {{ "%-12s"|format(r.bytes) }} {{ "%-12s"|format(r.wrong) }} {{ "%-10s"|format(r.iif) }} {{ "%-20s"|format(r.oifs|join(', ')) }} -{% endfor %} -""" - -parser = argparse.ArgumentParser() -parser.add_argument("--interface", action="store_true", help="Interface Statistics") -parser.add_argument("--mfc", action="store_true", help="Multicast Forwarding Cache") - -def byte_string(size): - # convert size to integer - size = int(size) - - # One Terrabyte - s_TB = 1024 * 1024 * 1024 * 1024 - # One Gigabyte - s_GB = 1024 * 1024 * 1024 - # One Megabyte - s_MB = 1024 * 1024 - # One Kilobyte - s_KB = 1024 - # One Byte - s_B = 1 - - if size > s_TB: - return str(round((size/s_TB), 2)) + 'TB' - elif size > s_GB: - return str(round((size/s_GB), 2)) + 'GB' - elif size > s_MB: - return str(round((size/s_MB), 2)) + 'MB' - elif size > s_KB: - return str(round((size/s_KB), 2)) + 'KB' - else: - return str(round((size/s_B), 2)) + 'b' - - return None - -def kernel2ip(addr): - """ - Convert any given addr from Linux Kernel to a proper, IPv4 address - using the correct host byte order. - """ - - # Convert from hex 'FE000A0A' to decimal '4261415434' - addr = int(addr, 16) - # Kernel ABI _always_ uses network byteorder - addr = socket.ntohl(addr) - - return ipaddress.IPv4Address( addr ) - -def do_mr_vif(): - """ - Read contents of file /proc/net/ip_mr_vif and print a more human - friendly version to the command line. IPv4 addresses present as - 32bit integers in hex format are converted to IPv4 notation, too. - """ - - with open('/proc/net/ip_mr_vif', 'r') as f: - lines = len(f.readlines()) - if lines < 2: - return None - - result = { - 'data': [] - } - - # Build up table format string - table_format = { - 'interface': 'Interface', - 'pkts_in' : 'PktsIn', - 'pkts_out' : 'PktsOut', - 'bytes_in' : 'BytesIn', - 'bytes_out': 'BytesOut', - 'loc' : 'Local' - } - result['data'].append(table_format) - - # read and parse information from /proc filesystema - with open('/proc/net/ip_mr_vif', 'r') as f: - header_line = next(f) - for line in f: - data = { - 'interface': line.split()[1], - 'pkts_in' : line.split()[3], - 'pkts_out' : line.split()[5], - - # convert raw byte number to something more human readable - # Note: could be replaced by Python3 hurry.filesize module - 'bytes_in' : byte_string( line.split()[2] ), - 'bytes_out': byte_string( line.split()[4] ), - - # convert IP address from hex 'FE000A0A' to decimal '4261415434' - 'loc' : kernel2ip( line.split()[7] ), - } - result['data'].append(data) - - return result - -def do_mr_mfc(): - """ - Read contents of file /proc/net/ip_mr_cache and print a more human - friendly version to the command line. IPv4 addresses present as - 32bit integers in hex format are converted to IPv4 notation, too. - """ - - with open('/proc/net/ip_mr_cache', 'r') as f: - lines = len(f.readlines()) - if lines < 2: - return None - - # We need this to convert from interface index to a real interface name - # Thus we also skip the format identifier on list index 0 - vif = do_mr_vif()['data'][1:] - - result = { - 'data': [] - } - - # Build up table format string - table_format = { - 'group' : 'Group', - 'origin': 'Origin', - 'iif' : 'In', - 'oifs' : ['Out'], - 'pkts' : 'Pkts', - 'bytes' : 'Bytes', - 'wrong' : 'Wrong' - } - result['data'].append(table_format) - - # read and parse information from /proc filesystem - with open('/proc/net/ip_mr_cache', 'r') as f: - header_line = next(f) - for line in f: - data = { - # convert IP address from hex 'FE000A0A' to decimal '4261415434' - 'group' : kernel2ip( line.split()[0] ), - 'origin': kernel2ip( line.split()[1] ), - - 'iif' : '--', - 'pkts' : '', - 'bytes' : '', - 'wrong' : '', - 'oifs' : [] - } - - iif = int( line.split()[2] ) - if not ((iif == -1) or (iif == 65535)): - data['pkts'] = line.split()[3] - data['bytes'] = byte_string( line.split()[4] ) - data['wrong'] = line.split()[5] - - # convert index to real interface name - data['iif'] = vif[iif]['interface'] - - # convert each output interface index to a real interface name - for oif in line.split()[6:]: - idx = int( oif.split(':')[0] ) - data['oifs'].append( vif[idx]['interface'] ) - - result['data'].append(data) - - return result - -if __name__ == '__main__': - args = parser.parse_args() - - # Do nothing if service is not configured - c = vyos.config.Config() - if not c.exists_effective('protocols igmp-proxy'): - print("IGMP proxy is not configured") - sys.exit(0) - - if args.interface: - data = do_mr_vif() - if data: - tmpl = jinja2.Template(vif_out_tmpl) - print(tmpl.render(data)) - - sys.exit(0) - elif args.mfc: - data = do_mr_mfc() - if data: - tmpl = jinja2.Template(mfc_out_tmpl) - print(tmpl.render(data)) - - sys.exit(0) - else: - parser.print_help() - sys.exit(1) - diff --git a/src/services/api/graphql/generate/schema_from_op_mode.py b/src/services/api/graphql/generate/schema_from_op_mode.py index fc63b0100..b320a529e 100755 --- a/src/services/api/graphql/generate/schema_from_op_mode.py +++ b/src/services/api/graphql/generate/schema_from_op_mode.py @@ -25,16 +25,17 @@ from inspect import signature, getmembers, isfunction, isclass, getmro from jinja2 import Template from vyos.defaults import directories +from vyos.opmode import _is_op_mode_function_name as is_op_mode_function_name from vyos.util import load_as_module if __package__ is None or __package__ == '': sys.path.append("/usr/libexec/vyos/services/api") - from graphql.libs.op_mode import is_op_mode_function_name, is_show_function_name + from graphql.libs.op_mode import is_show_function_name from graphql.libs.op_mode import snake_to_pascal_case, map_type_name from vyos.config import Config from vyos.configdict import dict_merge from vyos.xml import defaults else: - from .. libs.op_mode import is_op_mode_function_name, is_show_function_name + from .. libs.op_mode import is_show_function_name from .. libs.op_mode import snake_to_pascal_case, map_type_name from .. import state diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py index 87ea59c43..8254e22b1 100644 --- a/src/services/api/graphql/graphql/mutations.py +++ b/src/services/api/graphql/graphql/mutations.py @@ -15,7 +15,7 @@ from importlib import import_module from typing import Any, Dict, Optional -from ariadne import ObjectType, convert_kwargs_to_snake_case, convert_camel_case_to_snake +from ariadne import ObjectType, convert_camel_case_to_snake from graphql import GraphQLResolveInfo from makefun import with_signature @@ -45,7 +45,6 @@ def make_mutation_resolver(mutation_name, class_name, session_func): func_sig = '(obj: Any, info: GraphQLResolveInfo, data: Optional[Dict]=None)' @mutation.field(mutation_name) - @convert_kwargs_to_snake_case @with_signature(func_sig, func_name=resolver_name) async def func_impl(*args, **kwargs): try: diff --git a/src/services/api/graphql/graphql/queries.py b/src/services/api/graphql/graphql/queries.py index 1ad586428..daccc19b2 100644 --- a/src/services/api/graphql/graphql/queries.py +++ b/src/services/api/graphql/graphql/queries.py @@ -15,7 +15,7 @@ from importlib import import_module from typing import Any, Dict, Optional -from ariadne import ObjectType, convert_kwargs_to_snake_case, convert_camel_case_to_snake +from ariadne import ObjectType, convert_camel_case_to_snake from graphql import GraphQLResolveInfo from makefun import with_signature @@ -45,7 +45,6 @@ def make_query_resolver(query_name, class_name, session_func): func_sig = '(obj: Any, info: GraphQLResolveInfo, data: Optional[Dict]=None)' @query.field(query_name) - @convert_kwargs_to_snake_case @with_signature(func_sig, func_name=resolver_name) async def func_impl(*args, **kwargs): try: diff --git a/src/services/api/graphql/libs/op_mode.py b/src/services/api/graphql/libs/op_mode.py index c1eb493db..c553bbd67 100644 --- a/src/services/api/graphql/libs/op_mode.py +++ b/src/services/api/graphql/libs/op_mode.py @@ -29,11 +29,6 @@ def load_op_mode_as_module(name: str): name = os.path.splitext(name)[0].replace('-', '_') return load_as_module(name, path) -def is_op_mode_function_name(name): - if re.match(r"^(show|clear|reset|restart|add|delete)", name): - return True - return False - def is_show_function_name(name): if re.match(r"^show", name): return True diff --git a/src/services/api/graphql/session/errors/op_mode_errors.py b/src/services/api/graphql/session/errors/op_mode_errors.py index 4029fd0a1..18d555f2d 100644 --- a/src/services/api/graphql/session/errors/op_mode_errors.py +++ b/src/services/api/graphql/session/errors/op_mode_errors.py @@ -1,9 +1,8 @@ - - op_mode_err_msg = { "UnconfiguredSubsystem": "subsystem is not configured or not running", "DataUnavailable": "data currently unavailable", "PermissionDenied": "client does not have permission", + "InsufficientResources": "insufficient system resources", "IncorrectValue": "argument value is incorrect", "UnsupportedOperation": "operation is not supported (yet)", } @@ -11,6 +10,7 @@ op_mode_err_msg = { op_mode_err_code = { "UnconfiguredSubsystem": 2000, "DataUnavailable": 2001, + "InsufficientResources": 2002, "PermissionDenied": 1003, "IncorrectValue": 1002, "UnsupportedOperation": 1004, |