From 812a4fc3f3063741da0fa01cbbbf17dead66a664 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Thu, 9 Jun 2022 09:16:53 -0400 Subject: T2719: prototype of an op mode command runner based on type hints and introspection --- op-mode-definitions/ipv6-route.xml.in | 6 +- op-mode-definitions/show-arp.xml.in | 4 +- op-mode-definitions/show-ip.xml.in | 6 +- python/vyos/opmode.py | 123 ++++++++++++++++++++++++++++++++++ src/op_mode/neighbor.py | 104 ++++++++++++++++++++++++++++ src/op_mode/show_neigh.py | 102 ---------------------------- 6 files changed, 235 insertions(+), 110 deletions(-) create mode 100644 python/vyos/opmode.py create mode 100755 src/op_mode/neighbor.py delete mode 100755 src/op_mode/show_neigh.py diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in index 4f8792f9f..50f34dd56 100644 --- a/op-mode-definitions/ipv6-route.xml.in +++ b/op-mode-definitions/ipv6-route.xml.in @@ -20,7 +20,7 @@ Show IPv6 neighbor (NDP) table - ${vyos_op_scripts_dir}/show_neigh.py --family inet6 + ${vyos_op_scripts_dir}/neighbor.py --family inet6 @@ -29,7 +29,7 @@ - ${vyos_op_scripts_dir}/show_neigh.py --family inet6 --interface "$5" + ${vyos_op_scripts_dir}/neighbor.py --family inet6 --interface "$5" @@ -38,7 +38,7 @@ reachable stale failed permanent - ${vyos_op_scripts_dir}/show_neigh.py --family inet6 --state "$5" + ${vyos_op_scripts_dir}/neighbor.py --family inet6 --state "$5" diff --git a/op-mode-definitions/show-arp.xml.in b/op-mode-definitions/show-arp.xml.in index 58cc6e45e..11ac6a252 100644 --- a/op-mode-definitions/show-arp.xml.in +++ b/op-mode-definitions/show-arp.xml.in @@ -6,7 +6,7 @@ Show Address Resolution Protocol (ARP) information - ${vyos_op_scripts_dir}/show_neigh.py --family inet + ${vyos_op_scripts_dir}/neighbor.py --family inet @@ -15,7 +15,7 @@ - ${vyos_op_scripts_dir}/show_neigh.py --family inet --interface "$4" + ${vyos_op_scripts_dir}/neighbor.py --family inet --interface "$4" diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in index d342ac192..1903aca05 100644 --- a/op-mode-definitions/show-ip.xml.in +++ b/op-mode-definitions/show-ip.xml.in @@ -11,7 +11,7 @@ Show IPv4 neighbor (ARP) table - ${vyos_op_scripts_dir}/show_neigh.py --family inet + ${vyos_op_scripts_dir}/neighbor.py --family inet @@ -20,7 +20,7 @@ - ${vyos_op_scripts_dir}/show_neigh.py --family inet --interface "$5" + ${vyos_op_scripts_dir}/neighbor.py --family inet --interface "$5" @@ -29,7 +29,7 @@ reachable stale failed permanent - ${vyos_op_scripts_dir}/show_neigh.py --family inet --state "$5" + ${vyos_op_scripts_dir}/neighbor.py --family inet --state "$5" diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py new file mode 100644 index 000000000..c0812c7fd --- /dev/null +++ b/python/vyos/opmode.py @@ -0,0 +1,123 @@ +# Copyright 2022 VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . + +import typing + + +def _is_op_mode_function_name(name): + from re import match + + if match(r"^(show|clear|reset|restart)", name): + return True + else: + return False + +def _is_show(name): + from re import match + + if match(r"^show", name): + return True + else: + return False + +def _get_op_mode_functions(module): + from inspect import getmembers, isfunction + + # Get all functions in that module + funcs = getmembers(module, isfunction) + + # getmembers returns (name, func) tuples + funcs = list(filter(lambda ft: _is_op_mode_function_name(ft[0]), funcs)) + + funcs_dict = {} + for (name, thunk) in funcs: + funcs_dict[name] = thunk + + return funcs_dict + +def _is_optional_type(t): + # Optional[t] is internally an alias for Union[t, NoneType] + # and there's no easy way to get union members it seems + if (type(t) == typing._UnionGenericAlias): + if (len(t.__args__) == 2): + if t.__args__[1] == type(None): + return True + + return False + +def _get_arg_type(t): + """ Returns the type itself if it's a primitive type, + or the "real" type of typing.Optional + + Doesn't work with anything else at the moment! + """ + if _is_optional_type(t): + t.__args__[0] + else: + return t + +def run(module): + from argparse import ArgumentParser + + functions = _get_op_mode_functions(module) + + parser = ArgumentParser() + subparsers = parser.add_subparsers(dest="subcommand") + + for function_name in functions: + subparser = subparsers.add_parser(function_name, help=functions[function_name].__doc__) + + type_hints = typing.get_type_hints(functions[function_name]) + for opt in type_hints: + th = type_hints[opt] + + # Show commands require an option to choose between raw JSON and human-readable + # formatted output. + # For interactive use, they default to formatted output. + if (function_name == "show") and (opt == "raw"): + subparser.add_argument(f"--raw", action='store_true') + elif _is_optional_type(th): + subparser.add_argument(f"--{opt}", type=_get_arg_type(th), default=None) + else: + subparser.add_argument(f"--{opt}", type=_get_arg_type(th), required=True) + + # Get options as a dict rather than a namespace, + # so that we can modify it and pack for passing to functions + args = vars(parser.parse_args()) + + func = functions[args["subcommand"]] + + # Remove the subcommand from the arguments, + # it would cause an extra argument error when we pass the dict to a function + del args["subcommand"] + + if "raw" not in args: + args["raw"] = False + + if function_name == "show": + # Show commands are slightly special: + # they may return human-formatted output + # or a raw dict that we need to serialize in JSON for printing + res = func(**args) + if not args["raw"]: + return res + else: + from json import dumps + return dumps(res, indent=4) + else: + # Other functions should not return anything, + # although they may print their own warnings or status messages + func(**args) + diff --git a/src/op_mode/neighbor.py b/src/op_mode/neighbor.py new file mode 100755 index 000000000..b50e75d91 --- /dev/null +++ b/src/op_mode/neighbor.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 . + +# Sample output of `ip --json neigh list`: +# +# [ +# { +# "dst": "192.168.1.1", +# "dev": "eth0", # Missing if `dev ...` option is used +# "lladdr": "00:aa:bb:cc:dd:ee", # May be missing for failed entries +# "state": [ +# "REACHABLE" +# ] +# }, +# ] + +import sys +import typing + +import opmode + + +def get_raw_data(family, interface=None, state=None): + from json import loads + from vyos.util import cmd + + if interface: + interface = f"dev {interface}" + else: + interface = "" + + if state: + state = f"nud {state}" + else: + state = "" + + neigh_cmd = f"ip --family {family} --json neighbor list {interface} {state}" + + data = loads(cmd(neigh_cmd)) + + return data + +def format_neighbors(neighs, interface=None): + from tabulate import tabulate + + def entry_to_list(e, intf=None): + dst = e["dst"] + + # State is always a list in the iproute2 output + state = ", ".join(e["state"]) + + # Link layer address is absent from e.g. FAILED entries + if "lladdr" in e: + lladdr = e["lladdr"] + else: + lladdr = None + + # Device field is absent from outputs of `ip neigh list dev ...` + if "dev" in e: + dev = e["dev"] + elif interface: + dev = interface + else: + raise ValueError("interface is not defined") + + return [dst, dev, lladdr, state] + + neighs = map(entry_to_list, neighs) + + headers = ["Address", "Interface", "Link layer address", "State"] + return tabulate(neighs, headers) + +def show(raw: bool, family: str, interface: typing.Optional[str], state: typing.Optional[str]): + """ Display neighbor table contents """ + data = get_raw_data(family, interface, state=state) + + if raw: + return data + else: + return format_neighbors(data, interface) + + +if __name__ == '__main__': + from argparse import ArgumentParser + + try: + print(opmode.run(sys.modules[__name__])) + except ValueError as e: + print(e) + sys.exit(1) + diff --git a/src/op_mode/show_neigh.py b/src/op_mode/show_neigh.py deleted file mode 100755 index d874bd544..000000000 --- a/src/op_mode/show_neigh.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2022 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 . - -# Sample output of `ip --json neigh list`: -# -# [ -# { -# "dst": "192.168.1.1", -# "dev": "eth0", # Missing if `dev ...` option is used -# "lladdr": "00:aa:bb:cc:dd:ee", # May be missing for failed entries -# "state": [ -# "REACHABLE" -# ] -# }, -# ] - -import sys - - -def get_raw_data(family, device=None, state=None): - from json import loads - from vyos.util import cmd - - if device: - device = f"dev {device}" - else: - device = "" - - if state: - state = f"nud {state}" - else: - state = "" - - neigh_cmd = f"ip --family {family} --json neighbor list {device} {state}" - - data = loads(cmd(neigh_cmd)) - - return data - -def get_formatted_output(family, device=None, state=None): - from tabulate import tabulate - - def entry_to_list(e, intf=None): - dst = e["dst"] - - # State is always a list in the iproute2 output - state = ", ".join(e["state"]) - - # Link layer address is absent from e.g. FAILED entries - if "lladdr" in e: - lladdr = e["lladdr"] - else: - lladdr = None - - # Device field is absent from outputs of `ip neigh list dev ...` - if "dev" in e: - dev = e["dev"] - elif device: - dev = device - else: - raise ValueError("interface is not defined") - - return [dst, dev, lladdr, state] - - neighs = get_raw_data(family, device=device, state=state) - neighs = map(entry_to_list, neighs) - - headers = ["Address", "Interface", "Link layer address", "State"] - return tabulate(neighs, headers) - -if __name__ == '__main__': - from argparse import ArgumentParser - - parser = ArgumentParser() - parser.add_argument("-f", "--family", type=str, default="inet", help="Address family") - parser.add_argument("-i", "--interface", type=str, help="Network interface") - parser.add_argument("-s", "--state", type=str, help="Neighbor table entry state") - - args = parser.parse_args() - - if args.state: - if args.state not in ["reachable", "failed", "stale", "permanent"]: - raise ValueError(f"""Incorrect state "{args.state}"! Must be one of: reachable, stale, failed, permanent""") - - try: - print(get_formatted_output(args.family, device=args.interface, state=args.state)) - except ValueError as e: - print(e) - sys.exit(1) -- cgit v1.2.3 From a55a47c6417e8b785396e950e616ce162731fbf1 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 14 Jun 2022 14:28:08 -0400 Subject: T2719: fix commands in the op mode definitions --- op-mode-definitions/show-ip.xml.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in index 1903aca05..0751c50cb 100644 --- a/op-mode-definitions/show-ip.xml.in +++ b/op-mode-definitions/show-ip.xml.in @@ -11,7 +11,7 @@ Show IPv4 neighbor (ARP) table - ${vyos_op_scripts_dir}/neighbor.py --family inet + ${vyos_op_scripts_dir}/neighbor.py show --family inet @@ -20,7 +20,7 @@ - ${vyos_op_scripts_dir}/neighbor.py --family inet --interface "$5" + ${vyos_op_scripts_dir}/neighbor.py show --family inet --interface "$5" @@ -29,7 +29,7 @@ reachable stale failed permanent - ${vyos_op_scripts_dir}/neighbor.py --family inet --state "$5" + ${vyos_op_scripts_dir}/neighbor.py show --family inet --state "$5" -- cgit v1.2.3 From a90faa4ddcd909415e8df6806214471acdc4b17c Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 14 Jun 2022 14:30:33 -0400 Subject: T2719: fix module import path --- src/op_mode/neighbor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/op_mode/neighbor.py b/src/op_mode/neighbor.py index b50e75d91..1f686843d 100755 --- a/src/op_mode/neighbor.py +++ b/src/op_mode/neighbor.py @@ -30,7 +30,7 @@ import sys import typing -import opmode +import vyos.opmode def get_raw_data(family, interface=None, state=None): -- cgit v1.2.3 From f08b850f297422925b8e0a16d67accee6843b9e1 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 15 Jun 2022 04:47:41 -0400 Subject: T2719: handle the case when script subcommand is not given --- python/vyos/opmode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index c0812c7fd..692e5fc98 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +import sys import typing @@ -97,6 +98,11 @@ def run(module): # so that we can modify it and pack for passing to functions args = vars(parser.parse_args()) + if not args["subcommand"]: + print("Subcommand required!") + parser.print_usage() + sys.exit(1) + func = functions[args["subcommand"]] # Remove the subcommand from the arguments, -- cgit v1.2.3 From 3a9d9b4297c56bae42b1fc10a08cfcce58496483 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 15 Jun 2022 06:38:09 -0400 Subject: T2719: correctly handle the raw argument for all show_* commands --- python/vyos/opmode.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index 692e5fc98..10e8770d3 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -103,16 +103,20 @@ def run(module): parser.print_usage() sys.exit(1) - func = functions[args["subcommand"]] + function_name = args["subcommand"] + func = functions[function_name] # Remove the subcommand from the arguments, # it would cause an extra argument error when we pass the dict to a function del args["subcommand"] - if "raw" not in args: + # Show commands must always get the "raw" argument, + # but other commands (clear/reset/restart) should not, + # because they produce no output and it makes no sense for them. + if ("raw" not in args) and re.match(r"^show", function_name): args["raw"] = False - if function_name == "show": + if re.match(r"^show", function_name): # Show commands are slightly special: # they may return human-formatted output # or a raw dict that we need to serialize in JSON for printing -- cgit v1.2.3 From aeb9491a7ac33314c78dce1157cecc4d6645acb6 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 15 Jun 2022 09:45:14 -0400 Subject: T2719: correct script calls in 'show arp' --- op-mode-definitions/show-arp.xml.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op-mode-definitions/show-arp.xml.in b/op-mode-definitions/show-arp.xml.in index 11ac6a252..8662549fc 100644 --- a/op-mode-definitions/show-arp.xml.in +++ b/op-mode-definitions/show-arp.xml.in @@ -6,7 +6,7 @@ Show Address Resolution Protocol (ARP) information - ${vyos_op_scripts_dir}/neighbor.py --family inet + ${vyos_op_scripts_dir}/neighbor.py show --family inet @@ -15,7 +15,7 @@ - ${vyos_op_scripts_dir}/neighbor.py --family inet --interface "$4" + ${vyos_op_scripts_dir}/neighbor.py show --family inet --interface "$4" -- cgit v1.2.3 From db5952f2bb09a3997b3bef1904497a068fff59ce Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 15 Jun 2022 09:48:20 -0400 Subject: T2719: add reset command to the neighbor script --- op-mode-definitions/ipv4-route.xml.in | 10 ++++++++-- src/op_mode/neighbor.py | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/op-mode-definitions/ipv4-route.xml.in b/op-mode-definitions/ipv4-route.xml.in index 8f001d5bb..660b34496 100644 --- a/op-mode-definitions/ipv4-route.xml.in +++ b/op-mode-definitions/ipv4-route.xml.in @@ -39,7 +39,7 @@ <x.x.x.x> - sudo ip neigh flush to "$5" + sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet --address "$5" @@ -48,8 +48,14 @@ - sudo ip neigh flush dev "$5" + sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet --interface "$5" + + + Flush the ARP cache completely + + sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet + diff --git a/src/op_mode/neighbor.py b/src/op_mode/neighbor.py index 1f686843d..13ea46b8f 100755 --- a/src/op_mode/neighbor.py +++ b/src/op_mode/neighbor.py @@ -92,12 +92,25 @@ def show(raw: bool, family: str, interface: typing.Optional[str], state: typing. else: return format_neighbors(data, interface) +def reset(family: str, interface: typing.Optional[str], address: typing.Optional[str]): + from vyos.util import run + + if address and interface: + raise ValueError("interface and address parameters are mutually exclusive") + elif address: + run(f"""ip --family {family} neighbor flush to {address}""") + elif interface: + run(f"""ip --family {family} neighbor flush dev {interface}""") + else: + # Flush an entire neighbor table + run(f"""ip --family {family} neighbor flush""") -if __name__ == '__main__': - from argparse import ArgumentParser +if __name__ == '__main__': try: - print(opmode.run(sys.modules[__name__])) + res = opmode.run(sys.modules[__name__]) + if res: + print(res) except ValueError as e: print(e) sys.exit(1) -- cgit v1.2.3 From f0e0a2393b48359bc5f580bce9730225741c7c90 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Thu, 16 Jun 2022 11:23:36 -0400 Subject: T2719: make re functions usage in vyos.opmode more consistent --- python/vyos/opmode.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index 10e8770d3..270adc658 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -13,22 +13,19 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +import re import sys import typing def _is_op_mode_function_name(name): - from re import match - - if match(r"^(show|clear|reset|restart)", name): + if re.match(r"^(show|clear|reset|restart)", name): return True else: return False def _is_show(name): - from re import match - - if match(r"^show", name): + if re.match(r"^show", name): return True else: return False -- cgit v1.2.3 From 352713d5948d62b84ca76e4f9a644e956f030a49 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Thu, 16 Jun 2022 11:24:40 -0400 Subject: T2719: correct module path in the neighbor script --- src/op_mode/neighbor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/op_mode/neighbor.py b/src/op_mode/neighbor.py index 13ea46b8f..b0b52c082 100755 --- a/src/op_mode/neighbor.py +++ b/src/op_mode/neighbor.py @@ -108,7 +108,7 @@ def reset(family: str, interface: typing.Optional[str], address: typing.Optional if __name__ == '__main__': try: - res = opmode.run(sys.modules[__name__]) + res = vyos.opmode.run(sys.modules[__name__]) if res: print(res) except ValueError as e: -- cgit v1.2.3 From 53c9b500f085394ddfb92f03b6aae99b4e399460 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Fri, 17 Jun 2022 08:37:31 -0400 Subject: T2719: correct neighbor commands for IPv6 --- op-mode-definitions/ipv6-route.xml.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in index 50f34dd56..d75caf308 100644 --- a/op-mode-definitions/ipv6-route.xml.in +++ b/op-mode-definitions/ipv6-route.xml.in @@ -20,7 +20,7 @@ Show IPv6 neighbor (NDP) table - ${vyos_op_scripts_dir}/neighbor.py --family inet6 + ${vyos_op_scripts_dir}/neighbor.py show --family inet6 @@ -29,7 +29,7 @@ - ${vyos_op_scripts_dir}/neighbor.py --family inet6 --interface "$5" + ${vyos_op_scripts_dir}/neighbor.py show --family inet6 --interface "$5" @@ -38,7 +38,7 @@ reachable stale failed permanent - ${vyos_op_scripts_dir}/neighbor.py --family inet6 --state "$5" + ${vyos_op_scripts_dir}/neighbor.py show --family inet6 --state "$5" -- cgit v1.2.3 From 651eb5794d22576d90ae8ff2fe0b1aeda92dd581 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Fri, 17 Jun 2022 09:06:01 -0400 Subject: T2719: handle non-existent interfaces in the neighbor script --- src/op_mode/neighbor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/op_mode/neighbor.py b/src/op_mode/neighbor.py index b0b52c082..d86a372ac 100755 --- a/src/op_mode/neighbor.py +++ b/src/op_mode/neighbor.py @@ -32,12 +32,17 @@ import typing import vyos.opmode +def interface_exists(interface): + import os + return os.path.exists(f'/sys/class/net/{interface}') def get_raw_data(family, interface=None, state=None): from json import loads from vyos.util import cmd if interface: + if not interface_exists(interface): + raise ValueError(f"Interface '{interface}' does not exist in the system") interface = f"dev {interface}" else: interface = "" -- cgit v1.2.3 From 81f7df57eeb0714bda8d9103659e644de48d1dc9 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Mon, 20 Jun 2022 10:26:25 -0400 Subject: T2719: use _is_show for detecting show functions --- python/vyos/opmode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index 270adc658..134f55017 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -84,7 +84,7 @@ def run(module): # Show commands require an option to choose between raw JSON and human-readable # formatted output. # For interactive use, they default to formatted output. - if (function_name == "show") and (opt == "raw"): + if _is_show(function_name) and (opt == "raw"): subparser.add_argument(f"--raw", action='store_true') elif _is_optional_type(th): subparser.add_argument(f"--{opt}", type=_get_arg_type(th), default=None) @@ -110,7 +110,7 @@ def run(module): # Show commands must always get the "raw" argument, # but other commands (clear/reset/restart) should not, # because they produce no output and it makes no sense for them. - if ("raw" not in args) and re.match(r"^show", function_name): + if ("raw" not in args) and _is_show(function_name): args["raw"] = False if re.match(r"^show", function_name): -- cgit v1.2.3 From b8e2a0650168ce4958dc360f857c816f02c6284f Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 5 Jul 2022 06:47:48 -0400 Subject: T2719: add general support for boolean options to generative op mode Since Python as of 3.9 doesn't give us an option to look up argument's default value by its name, this implementation requires that all boolean options must default to false. --- python/vyos/opmode.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index 134f55017..4e68d6e03 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -81,13 +81,10 @@ def run(module): for opt in type_hints: th = type_hints[opt] - # Show commands require an option to choose between raw JSON and human-readable - # formatted output. - # For interactive use, they default to formatted output. - if _is_show(function_name) and (opt == "raw"): - subparser.add_argument(f"--raw", action='store_true') - elif _is_optional_type(th): + if _is_optional_type(th): subparser.add_argument(f"--{opt}", type=_get_arg_type(th), default=None) + elif _get_arg_type(th) == bool: + subparser.add_argument(f"--{opt}", action='store_true') else: subparser.add_argument(f"--{opt}", type=_get_arg_type(th), required=True) -- cgit v1.2.3 From 1b425cd96b5136c7e45479f34e55e88dd79ed102 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 5 Jul 2022 06:51:32 -0400 Subject: T2719: convert 'show version' to the new op mode style --- src/op_mode/show_version.py | 50 +++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/op_mode/show_version.py b/src/op_mode/show_version.py index b82ab6eca..90645dfbc 100755 --- a/src/op_mode/show_version.py +++ b/src/op_mode/show_version.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2016-2020 VyOS maintainers and contributors +# Copyright (C) 2016-2022 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 @@ -19,11 +19,14 @@ # Used by the "run show version" command. import argparse +import sys +import typing + +import vyos.opmode import vyos.version import vyos.limericks from jinja2 import Template -from sys import exit from vyos.util import call version_output_tmpl = """ @@ -45,32 +48,39 @@ Hardware S/N: {{hardware_serial}} Hardware UUID: {{hardware_uuid}} Copyright: VyOS maintainers and contributors +{%- if limerick %} +{{limerick}} +{% endif -%} """ -def get_raw_data(): +def _get_raw_data(funny=False): version_data = vyos.version.get_full_version_data() + + if funny: + version_data["limerick"] = vyos.limericks.get_random() + return version_data -def get_formatted_output(): - version_data = get_raw_data() +def _get_formatted_output(version_data): tmpl = Template(version_output_tmpl) - return tmpl.render(version_data) + return tmpl.render(version_data).strip() -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("-f", "--funny", action="store_true", help="Add something funny to the output") - parser.add_argument("-j", "--json", action="store_true", help="Produce JSON output") +def show(raw: bool, funny: bool): + """ Display neighbor table contents """ + version_data = _get_raw_data(funny=funny) - args = parser.parse_args() + if raw: + return version_data + else: + return _get_formatted_output(version_data) - version_data = vyos.version.get_full_version_data() - if args.json: - import json - print(json.dumps(version_data)) - exit(0) - else: - print(get_formatted_output()) +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except ValueError as e: + print(e) + sys.exit(1) - if args.funny: - print(vyos.limericks.get_random()) -- cgit v1.2.3 From fbde12af711bdaea63e9ed07de1d200cbd269b07 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 5 Jul 2022 08:47:34 -0400 Subject: T2719: initial version of the route op mode script --- src/op_mode/route.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/op_mode/route.py diff --git a/src/op_mode/route.py b/src/op_mode/route.py new file mode 100644 index 000000000..3bb06adac --- /dev/null +++ b/src/op_mode/route.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 . +# +# Purpose: +# Displays routing table information. +# Used by the "run route *" commands. + +import re +import sys +import typing + +from jinja2 import Template + +import vyos.opmode + +frr_command_template = Template(""" +{% if family == "inet" %} + show ip route +{% else %} + show ipv6 route +{% endif %} + +{% if table %} + table {{table}} +{% endif %} + +{% if vrf %} + vrf {{table}} +{% endif %} + +{% if tag %} + tag {{tag}} +{% elif net %} + {{net}} +{% elif protocol %} + {{protocol}} +{% endif %} + +{% if raw %} + json +{% endif %} +""") + +def show(raw: bool, + family: str, + net: typing.Optional[str], + table: typing.Optional[int], + protocol: typing.Optional[str], + vrf: typing.Optional[str], + tag: typing.Optional[str]): + if net and protocol: + raise ValueError("net and protocol are mutually exclusive") + elif table and vrf: + raise ValueError("table and vrf are mutually exclusive") + elif (family == 'inet6') and (protocol == 'rip'): + raise ValueError("rip is not a valid protocol for family inet6") + elif (family == 'inet') and (protocol == 'ripng'): + raise ValueError("rip is not a valid protocol for family inet6") + else: + if (family == 'inet6') and (protocol == 'ospf'): + protocol = 'ospf6' + + kwargs = dict(locals()) + + frr_command = frr_command_template.render(kwargs) + frr_command = re.sub(r'\s+', ' ', frr_command) + + from vyos.util import cmd + output = cmd(f"vtysh -c '{frr_command}'") + + if raw: + from json import loads + return loads(output) + else: + return output + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except ValueError as e: + print(e) + sys.exit(1) + -- cgit v1.2.3 From d943440080858ea4951dc62c931faeb55f72f601 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 5 Jul 2022 10:08:29 -0400 Subject: T2719: update op mode CLI definition for 'show version' --- op-mode-definitions/show-version.xml.in | 4 +- src/op_mode/show_version.py | 86 --------------------------------- src/op_mode/version.py | 86 +++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 88 deletions(-) delete mode 100755 src/op_mode/show_version.py create mode 100755 src/op_mode/version.py diff --git a/op-mode-definitions/show-version.xml.in b/op-mode-definitions/show-version.xml.in index 8b7cc7e58..d9c4738af 100644 --- a/op-mode-definitions/show-version.xml.in +++ b/op-mode-definitions/show-version.xml.in @@ -6,13 +6,13 @@ Show system version information - sudo ${vyos_op_scripts_dir}/show_version.py + sudo ${vyos_op_scripts_dir}/version.py show Show system version and some fun stuff - sudo ${vyos_op_scripts_dir}/show_version.py --funny + sudo ${vyos_op_scripts_dir}/version.py show --funny diff --git a/src/op_mode/show_version.py b/src/op_mode/show_version.py deleted file mode 100755 index 90645dfbc..000000000 --- a/src/op_mode/show_version.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2016-2022 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 . -# -# Purpose: -# Displays image version and system information. -# Used by the "run show version" command. - -import argparse -import sys -import typing - -import vyos.opmode -import vyos.version -import vyos.limericks - -from jinja2 import Template -from vyos.util import call - -version_output_tmpl = """ -Version: VyOS {{version}} -Release train: {{release_train}} - -Built by: {{built_by}} -Built on: {{built_on}} -Build UUID: {{build_uuid}} -Build commit ID: {{build_git}} - -Architecture: {{system_arch}} -Boot via: {{boot_via}} -System type: {{system_type}} - -Hardware vendor: {{hardware_vendor}} -Hardware model: {{hardware_model}} -Hardware S/N: {{hardware_serial}} -Hardware UUID: {{hardware_uuid}} - -Copyright: VyOS maintainers and contributors -{%- if limerick %} -{{limerick}} -{% endif -%} -""" - -def _get_raw_data(funny=False): - version_data = vyos.version.get_full_version_data() - - if funny: - version_data["limerick"] = vyos.limericks.get_random() - - return version_data - -def _get_formatted_output(version_data): - tmpl = Template(version_output_tmpl) - return tmpl.render(version_data).strip() - -def show(raw: bool, funny: bool): - """ Display neighbor table contents """ - version_data = _get_raw_data(funny=funny) - - if raw: - return version_data - else: - return _get_formatted_output(version_data) - - -if __name__ == '__main__': - try: - res = vyos.opmode.run(sys.modules[__name__]) - if res: - print(res) - except ValueError as e: - print(e) - sys.exit(1) - diff --git a/src/op_mode/version.py b/src/op_mode/version.py new file mode 100755 index 000000000..90645dfbc --- /dev/null +++ b/src/op_mode/version.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2016-2022 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 . +# +# Purpose: +# Displays image version and system information. +# Used by the "run show version" command. + +import argparse +import sys +import typing + +import vyos.opmode +import vyos.version +import vyos.limericks + +from jinja2 import Template +from vyos.util import call + +version_output_tmpl = """ +Version: VyOS {{version}} +Release train: {{release_train}} + +Built by: {{built_by}} +Built on: {{built_on}} +Build UUID: {{build_uuid}} +Build commit ID: {{build_git}} + +Architecture: {{system_arch}} +Boot via: {{boot_via}} +System type: {{system_type}} + +Hardware vendor: {{hardware_vendor}} +Hardware model: {{hardware_model}} +Hardware S/N: {{hardware_serial}} +Hardware UUID: {{hardware_uuid}} + +Copyright: VyOS maintainers and contributors +{%- if limerick %} +{{limerick}} +{% endif -%} +""" + +def _get_raw_data(funny=False): + version_data = vyos.version.get_full_version_data() + + if funny: + version_data["limerick"] = vyos.limericks.get_random() + + return version_data + +def _get_formatted_output(version_data): + tmpl = Template(version_output_tmpl) + return tmpl.render(version_data).strip() + +def show(raw: bool, funny: bool): + """ Display neighbor table contents """ + version_data = _get_raw_data(funny=funny) + + if raw: + return version_data + else: + return _get_formatted_output(version_data) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except ValueError as e: + print(e) + sys.exit(1) + -- cgit v1.2.3 From dc4b80f1aee3c786889d9e34cf6b54a1a5b83b56 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 5 Jul 2022 10:23:57 -0400 Subject: T2719: convert the 'show system memory' script to the new style --- op-mode-definitions/show-system.xml.in | 2 +- src/op_mode/memory.py | 90 ++++++++++++++++++++++++++++++++++ src/op_mode/show_ram.py | 71 --------------------------- 3 files changed, 91 insertions(+), 72 deletions(-) create mode 100755 src/op_mode/memory.py delete mode 100755 src/op_mode/show_ram.py diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in index 68b473bc1..6f05d0c12 100644 --- a/op-mode-definitions/show-system.xml.in +++ b/op-mode-definitions/show-system.xml.in @@ -104,7 +104,7 @@ Show system memory usage - ${vyos_op_scripts_dir}/show_ram.py + ${vyos_op_scripts_dir}/memory.py show diff --git a/src/op_mode/memory.py b/src/op_mode/memory.py new file mode 100755 index 000000000..a3870e498 --- /dev/null +++ b/src/op_mode/memory.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021-2022 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 sys + +import vyos.opmode + + +def _get_system_memory(): + from re import search as re_search + + def find_value(keyword, mem_data): + regex = keyword + ':\s+(\d+)' + res = re_search(regex, mem_data).group(1) + return int(res) + + with open("/proc/meminfo", "r") as f: + mem_data = f.read() + + total = find_value('MemTotal', mem_data) + available = find_value('MemAvailable', mem_data) + buffers = find_value('Buffers', mem_data) + cached = find_value('Cached', mem_data) + + used = total - available + + res = { + "total": total, + "free": available, + "used": used, + "buffers": buffers, + "cached": cached + } + + return res + +def _get_system_memory_human(): + from vyos.util import bytes_to_human + + mem = _get_system_memory() + + for key in mem: + # The Linux kernel exposes memory values in kilobytes, + # so we need to normalize them + mem[key] = bytes_to_human(mem[key], initial_exponent=10) + + return mem + +def _get_raw_data(): + return _get_system_memory_human() + +def _get_formatted_output(mem): + out = "Total: {}\n".format(mem["total"]) + out += "Free: {}\n".format(mem["free"]) + out += "Used: {}".format(mem["used"]) + + return out + +def show(raw: bool): + ram_data = _get_raw_data() + + if raw: + return ram_data + else: + return _get_formatted_output(ram_data) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except ValueError as e: + print(e) + sys.exit(1) + diff --git a/src/op_mode/show_ram.py b/src/op_mode/show_ram.py deleted file mode 100755 index 2b0be3965..000000000 --- a/src/op_mode/show_ram.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2022 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 . -# - -def get_system_memory(): - from re import search as re_search - - def find_value(keyword, mem_data): - regex = keyword + ':\s+(\d+)' - res = re_search(regex, mem_data).group(1) - return int(res) - - with open("/proc/meminfo", "r") as f: - mem_data = f.read() - - total = find_value('MemTotal', mem_data) - available = find_value('MemAvailable', mem_data) - buffers = find_value('Buffers', mem_data) - cached = find_value('Cached', mem_data) - - used = total - available - - res = { - "total": total, - "free": available, - "used": used, - "buffers": buffers, - "cached": cached - } - - return res - -def get_system_memory_human(): - from vyos.util import bytes_to_human - - mem = get_system_memory() - - for key in mem: - # The Linux kernel exposes memory values in kilobytes, - # so we need to normalize them - mem[key] = bytes_to_human(mem[key], initial_exponent=10) - - return mem - -def get_raw_data(): - return get_system_memory_human() - -def get_formatted_output(): - mem = get_raw_data() - - out = "Total: {}\n".format(mem["total"]) - out += "Free: {}\n".format(mem["free"]) - out += "Used: {}".format(mem["used"]) - - return out - -if __name__ == '__main__': - print(get_formatted_output()) -- cgit v1.2.3 From ee5f697065eb1e50ea87bb1a6423b7533c03170a Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Thu, 7 Jul 2022 09:49:34 -0400 Subject: T2719: rework 'show hardware cpu *' commands in the new style --- op-mode-definitions/show-hardware.xml.in | 10 ++-- src/op_mode/cpu.py | 85 ++++++++++++++++++++++++++++++++ src/op_mode/cpu_summary.py | 48 ------------------ src/op_mode/show_cpu.py | 72 --------------------------- 4 files changed, 90 insertions(+), 125 deletions(-) create mode 100755 src/op_mode/cpu.py delete mode 100755 src/op_mode/cpu_summary.py delete mode 100755 src/op_mode/show_cpu.py diff --git a/op-mode-definitions/show-hardware.xml.in b/op-mode-definitions/show-hardware.xml.in index 20fdd753d..ebd806ba5 100644 --- a/op-mode-definitions/show-hardware.xml.in +++ b/op-mode-definitions/show-hardware.xml.in @@ -9,21 +9,21 @@ - Show CPU info + Show CPU informaion - lscpu + ${vyos_op_scripts_dir}/cpu.py show - Show system CPU details + Show system CPU details cat /proc/cpuinfo - Show system CPUs + Show system CPUs summary - ${vyos_op_scripts_dir}/cpu_summary.py + ${vyos_op_scripts_dir}/cpu.py show_summary diff --git a/src/op_mode/cpu.py b/src/op_mode/cpu.py new file mode 100755 index 000000000..cb55ce407 --- /dev/null +++ b/src/op_mode/cpu.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2016-2022 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 sys +import typing + +import vyos.cpu +import vyos.opmode + +from jinja2 import Template +from vyos.util import popen, DEVNULL + +cpu_template = Template(""" +{% for cpu in cpus %} +{% if 'physical id' in cpu %}CPU socket: {{cpu['physical id']}}{% endif %} +{% if 'vendor_id' in cpu %}CPU Vendor: {{cpu['vendor_id']}}{% endif %} +{% if 'model name' in cpu %}Model: {{cpu['model name']}}{% endif %} +{% if 'cpu cores' in cpu %}Cores: {{cpu['cpu cores']}}{% endif %} +{% if 'cpu MHz' in cpu %}Current MHz: {{cpu['cpu MHz']}}{% endif %} +{% endfor %} +""") + +cpu_summary_template = Template(""" +Physical CPU cores: {{count}} +CPU model(s): {{models | join(", ")}} +""") + +def _get_raw_data(): + return vyos.cpu.get_cpus() + +def _format_cpus(cpu_data): + env = {'cpus': cpu_data} + return cpu_template.render(env).strip() + +def _get_summary_data(): + count = vyos.cpu.get_core_count() + cpu_data = vyos.cpu.get_cpus() + models = [c['model name'] for c in cpu_data] + env = {'count': count, "models": models} + + return env + +def _format_cpu_summary(summary_data): + return cpu_summary_template.render(summary_data).strip() + +def show(raw: bool): + cpu_data = _get_raw_data() + + if raw: + return cpu_data + else: + return _format_cpus(cpu_data) + +def show_summary(raw: bool): + cpu_summary_data = _get_summary_data() + + if raw: + return cpu_summary_data + else: + return _format_cpu_summary(cpu_summary_data) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except ValueError as e: + print(e) + sys.exit(1) + diff --git a/src/op_mode/cpu_summary.py b/src/op_mode/cpu_summary.py deleted file mode 100755 index 3bdf5a718..000000000 --- a/src/op_mode/cpu_summary.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018-2022 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 re -from vyos.util import colon_separated_to_dict - -FILE_NAME = '/proc/cpuinfo' - -def get_raw_data(): - with open(FILE_NAME, 'r') as f: - data_raw = f.read() - - data = colon_separated_to_dict(data_raw) - - # Accumulate all data in a dict for future support for machine-readable output - cpu_data = {} - cpu_data['cpu_number'] = len(data['processor']) - cpu_data['models'] = list(set(data['model name'])) - - # Strip extra whitespace from CPU model names, /proc/cpuinfo is prone to that - cpu_data['models'] = list(map(lambda s: re.sub(r'\s+', ' ', s), cpu_data['models'])) - - return cpu_data - -def get_formatted_output(): - cpu_data = get_raw_data() - - out = "CPU(s): {0}\n".format(cpu_data['cpu_number']) - out += "CPU model(s): {0}".format(",".join(cpu_data['models'])) - - return out - -if __name__ == '__main__': - print(get_formatted_output()) - diff --git a/src/op_mode/show_cpu.py b/src/op_mode/show_cpu.py deleted file mode 100755 index 9973d9789..000000000 --- a/src/op_mode/show_cpu.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2016-2020 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 - -from jinja2 import Template -from sys import exit -from vyos.util import popen, DEVNULL - -OUT_TMPL_SRC = """ -{%- if cpu -%} -{% if 'vendor' in cpu %}CPU Vendor: {{cpu.vendor}}{% endif %} -{% if 'model' in cpu %}Model: {{cpu.model}}{% endif %} -{% if 'cpus' in cpu %}Total CPUs: {{cpu.cpus}}{% endif %} -{% if 'sockets' in cpu %}Sockets: {{cpu.sockets}}{% endif %} -{% if 'cores' in cpu %}Cores: {{cpu.cores}}{% endif %} -{% if 'threads' in cpu %}Threads: {{cpu.threads}}{% endif %} -{% if 'mhz' in cpu %}Current MHz: {{cpu.mhz}}{% endif %} -{% if 'mhz_min' in cpu %}Minimum MHz: {{cpu.mhz_min}}{% endif %} -{% if 'mhz_max' in cpu %}Maximum MHz: {{cpu.mhz_max}}{% endif %} -{%- endif -%} -""" - -def get_raw_data(): - cpu = {} - cpu_json, code = popen('lscpu -J', stderr=DEVNULL) - - if code == 0: - cpu_info = json.loads(cpu_json) - if len(cpu_info) > 0 and 'lscpu' in cpu_info: - for prop in cpu_info['lscpu']: - if (prop['field'].find('Thread(s)') > -1): cpu['threads'] = prop['data'] - if (prop['field'].find('Core(s)')) > -1: cpu['cores'] = prop['data'] - if (prop['field'].find('Socket(s)')) > -1: cpu['sockets'] = prop['data'] - if (prop['field'].find('CPU(s):')) > -1: cpu['cpus'] = prop['data'] - if (prop['field'].find('CPU MHz')) > -1: cpu['mhz'] = prop['data'] - if (prop['field'].find('CPU min MHz')) > -1: cpu['mhz_min'] = prop['data'] - if (prop['field'].find('CPU max MHz')) > -1: cpu['mhz_max'] = prop['data'] - if (prop['field'].find('Vendor ID')) > -1: cpu['vendor'] = prop['data'] - if (prop['field'].find('Model name')) > -1: cpu['model'] = prop['data'] - - return cpu - -def get_formatted_output(): - cpu = get_raw_data() - - tmp = {'cpu':cpu} - tmpl = Template(OUT_TMPL_SRC) - return tmpl.render(tmp) - -if __name__ == '__main__': - cpu = get_raw_data() - - if len(cpu) > 0: - print(get_formatted_output()) - else: - print('CPU information could not be determined\n') - exit(1) - -- cgit v1.2.3 From 8d8c14b53408aec3011fbf33c201fda4926248bb Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Mon, 18 Jul 2022 12:34:29 -0500 Subject: T2719: patch for general support for boolean options Signed-off-by: Daniil Baturin --- python/vyos/opmode.py | 11 ++++++----- src/op_mode/version.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index 4e68d6e03..906bd0dcb 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -62,7 +62,7 @@ def _get_arg_type(t): Doesn't work with anything else at the moment! """ if _is_optional_type(t): - t.__args__[0] + return t.__args__[0] else: return t @@ -81,12 +81,13 @@ def run(module): for opt in type_hints: th = type_hints[opt] - if _is_optional_type(th): - subparser.add_argument(f"--{opt}", type=_get_arg_type(th), default=None) - elif _get_arg_type(th) == bool: + if _get_arg_type(th) == bool: subparser.add_argument(f"--{opt}", action='store_true') else: - subparser.add_argument(f"--{opt}", type=_get_arg_type(th), required=True) + if _is_optional_type(th): + subparser.add_argument(f"--{opt}", type=_get_arg_type(th), default=None) + else: + subparser.add_argument(f"--{opt}", type=_get_arg_type(th), required=True) # Get options as a dict rather than a namespace, # so that we can modify it and pack for passing to functions diff --git a/src/op_mode/version.py b/src/op_mode/version.py index 90645dfbc..847bb197e 100755 --- a/src/op_mode/version.py +++ b/src/op_mode/version.py @@ -65,7 +65,7 @@ def _get_formatted_output(version_data): tmpl = Template(version_output_tmpl) return tmpl.render(version_data).strip() -def show(raw: bool, funny: bool): +def show(raw: bool, funny: typing.Optional[bool]): """ Display neighbor table contents """ version_data = _get_raw_data(funny=funny) -- cgit v1.2.3 From e64fcfd6014205184c21124f6570215ed908c233 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 20 Jul 2022 11:30:00 -0400 Subject: T2719: fix a stray empty key in the CPU data dict --- python/vyos/cpu.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/vyos/cpu.py b/python/vyos/cpu.py index a0ef864be..488ae79fb 100644 --- a/python/vyos/cpu.py +++ b/python/vyos/cpu.py @@ -32,7 +32,8 @@ import re def _read_cpuinfo(): with open('/proc/cpuinfo', 'r') as f: - return f.readlines() + lines = f.read().strip() + return re.split(r'\n+', lines) def _split_line(l): l = l.strip() -- cgit v1.2.3 From 39fbe8eecf9f0a115ca0d54bbddc83e3f5fd0c8c Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 20 Jul 2022 12:30:35 -0400 Subject: T2719: fix indentation in vyos.opmode --- python/vyos/opmode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index 906bd0dcb..0af4359c6 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -94,9 +94,9 @@ def run(module): args = vars(parser.parse_args()) if not args["subcommand"]: - print("Subcommand required!") - parser.print_usage() - sys.exit(1) + print("Subcommand required!") + parser.print_usage() + sys.exit(1) function_name = args["subcommand"] func = functions[function_name] -- cgit v1.2.3 From f9e835c41643f8d41f675c0364686fbea5055896 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 20 Jul 2022 12:58:41 -0400 Subject: T2719: fix unused imports --- src/op_mode/cpu.py | 3 --- src/op_mode/version.py | 2 -- 2 files changed, 5 deletions(-) diff --git a/src/op_mode/cpu.py b/src/op_mode/cpu.py index cb55ce407..f9c425826 100755 --- a/src/op_mode/cpu.py +++ b/src/op_mode/cpu.py @@ -14,15 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import json import sys -import typing import vyos.cpu import vyos.opmode from jinja2 import Template -from vyos.util import popen, DEVNULL cpu_template = Template(""" {% for cpu in cpus %} diff --git a/src/op_mode/version.py b/src/op_mode/version.py index 847bb197e..06208c3e5 100755 --- a/src/op_mode/version.py +++ b/src/op_mode/version.py @@ -18,7 +18,6 @@ # Displays image version and system information. # Used by the "run show version" command. -import argparse import sys import typing @@ -27,7 +26,6 @@ import vyos.version import vyos.limericks from jinja2 import Template -from vyos.util import call version_output_tmpl = """ Version: VyOS {{version}} -- cgit v1.2.3