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 --- src/op_mode/neighbor.py | 104 ++++++++++++++++++++++++++++++++++++++++++++++ src/op_mode/show_neigh.py | 102 --------------------------------------------- 2 files changed, 104 insertions(+), 102 deletions(-) create mode 100755 src/op_mode/neighbor.py delete mode 100755 src/op_mode/show_neigh.py (limited to 'src/op_mode') 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 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(-) (limited to 'src/op_mode') 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 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(-) (limited to 'src/op_mode') 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 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(-) (limited to 'src/op_mode') 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 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(+) (limited to 'src/op_mode') 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 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(-) (limited to 'src/op_mode') 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 (limited to 'src/op_mode') 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 (limited to 'src/op_mode') 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 (limited to 'src/op_mode') 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 (limited to 'src/op_mode') 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(-) (limited to 'src/op_mode') 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 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(-) (limited to 'src/op_mode') 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