summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Estabrook <jestabro@vyos.io>2022-07-20 15:32:59 -0500
committerGitHub <noreply@github.com>2022-07-20 15:32:59 -0500
commitf424d84f41791949a2ada417ecdd45a3b842799a (patch)
tree95e26bd89b6829493c0f5cef21616c523a0caa20
parent38d753f830887c35abe4fbcf1bb73b6f26be1fdf (diff)
parentf9e835c41643f8d41f675c0364686fbea5055896 (diff)
downloadvyos-1x-f424d84f41791949a2ada417ecdd45a3b842799a.tar.gz
vyos-1x-f424d84f41791949a2ada417ecdd45a3b842799a.zip
Merge pull request #1351 from dmbaturin/genop
T2719: prototype of an op mode command runner based on type hints and introspection
-rw-r--r--op-mode-definitions/ipv4-route.xml.in10
-rw-r--r--op-mode-definitions/ipv6-route.xml.in6
-rw-r--r--op-mode-definitions/show-arp.xml.in4
-rw-r--r--op-mode-definitions/show-hardware.xml.in10
-rw-r--r--op-mode-definitions/show-ip.xml.in6
-rw-r--r--op-mode-definitions/show-system.xml.in2
-rw-r--r--op-mode-definitions/show-version.xml.in4
-rw-r--r--python/vyos/cpu.py3
-rw-r--r--python/vyos/opmode.py128
-rwxr-xr-xsrc/op_mode/cpu.py82
-rwxr-xr-xsrc/op_mode/cpu_summary.py48
-rwxr-xr-xsrc/op_mode/memory.py (renamed from src/op_mode/show_ram.py)39
-rwxr-xr-xsrc/op_mode/neighbor.py (renamed from src/op_mode/show_neigh.py)60
-rw-r--r--src/op_mode/route.py98
-rwxr-xr-xsrc/op_mode/show_cpu.py72
-rwxr-xr-xsrc/op_mode/version.py (renamed from src/op_mode/show_version.py)52
16 files changed, 433 insertions, 191 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 @@
<list>&lt;x.x.x.x&gt;</list>
</completionHelp>
</properties>
- <command>sudo ip neigh flush to "$5"</command>
+ <command>sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet --address "$5"</command>
</tagNode>
<tagNode name="interface">
<properties>
@@ -48,8 +48,14 @@
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
</properties>
- <command>sudo ip neigh flush dev "$5"</command>
+ <command>sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet --interface "$5"</command>
</tagNode>
+ <node name="table">
+ <properties>
+ <help>Flush the ARP cache completely</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet</command>
+ </node>
</children>
</node>
<node name="route">
diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in
index 4f8792f9f..d75caf308 100644
--- a/op-mode-definitions/ipv6-route.xml.in
+++ b/op-mode-definitions/ipv6-route.xml.in
@@ -20,7 +20,7 @@
<properties>
<help>Show IPv6 neighbor (NDP) table</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet6</command>
<children>
<tagNode name="interface">
<properties>
@@ -29,7 +29,7 @@
<script>${vyos_completion_dir}/list_interfaces.py -b</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6 --interface "$5"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet6 --interface "$5"</command>
</tagNode>
<tagNode name="state">
<properties>
@@ -38,7 +38,7 @@
<list>reachable stale failed permanent</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6 --state "$5"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet6 --state "$5"</command>
</tagNode>
</children>
</node>
diff --git a/op-mode-definitions/show-arp.xml.in b/op-mode-definitions/show-arp.xml.in
index 58cc6e45e..8662549fc 100644
--- a/op-mode-definitions/show-arp.xml.in
+++ b/op-mode-definitions/show-arp.xml.in
@@ -6,7 +6,7 @@
<properties>
<help>Show Address Resolution Protocol (ARP) information</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet</command>
<children>
<tagNode name="interface">
<properties>
@@ -15,7 +15,7 @@
<script>${vyos_completion_dir}/list_interfaces.py -b</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --interface "$4"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet --interface "$4"</command>
</tagNode>
</children>
</node>
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 @@
<children>
<node name="cpu">
<properties>
- <help>Show CPU info</help>
+ <help>Show CPU informaion</help>
</properties>
- <command>lscpu</command>
+ <command>${vyos_op_scripts_dir}/cpu.py show</command>
<children>
<node name="detail">
<properties>
- <help> Show system CPU details</help>
+ <help>Show system CPU details</help>
</properties>
<command>cat /proc/cpuinfo</command>
</node>
<node name="summary">
<properties>
- <help>Show system CPUs</help>
+ <help>Show system CPUs summary</help>
</properties>
- <command>${vyos_op_scripts_dir}/cpu_summary.py</command>
+ <command>${vyos_op_scripts_dir}/cpu.py show_summary</command>
</node>
</children>
</node>
diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in
index d342ac192..0751c50cb 100644
--- a/op-mode-definitions/show-ip.xml.in
+++ b/op-mode-definitions/show-ip.xml.in
@@ -11,7 +11,7 @@
<properties>
<help>Show IPv4 neighbor (ARP) table</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet</command>
<children>
<tagNode name="interface">
<properties>
@@ -20,7 +20,7 @@
<script>${vyos_completion_dir}/list_interfaces.py -b</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --interface "$5"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet --interface "$5"</command>
</tagNode>
<tagNode name="state">
<properties>
@@ -29,7 +29,7 @@
<list>reachable stale failed permanent</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --state "$5"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet --state "$5"</command>
</tagNode>
</children>
</node>
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 @@
<properties>
<help>Show system memory usage</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_ram.py</command>
+ <command>${vyos_op_scripts_dir}/memory.py show</command>
<children>
<leafNode name="cache">
<properties>
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 @@
<properties>
<help>Show system version information</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/show_version.py</command>
+ <command>sudo ${vyos_op_scripts_dir}/version.py show</command>
<children>
<leafNode name="funny">
<properties>
<help>Show system version and some fun stuff</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/show_version.py --funny</command>
+ <command>sudo ${vyos_op_scripts_dir}/version.py show --funny</command>
</leafNode>
<leafNode name="all">
<properties>
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()
diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py
new file mode 100644
index 000000000..0af4359c6
--- /dev/null
+++ b/python/vyos/opmode.py
@@ -0,0 +1,128 @@
+# Copyright 2022 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
+# 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 <http://www.gnu.org/licenses/>.
+
+import re
+import sys
+import typing
+
+
+def _is_op_mode_function_name(name):
+ if re.match(r"^(show|clear|reset|restart)", name):
+ return True
+ else:
+ return False
+
+def _is_show(name):
+ if re.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):
+ return 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]
+
+ if _get_arg_type(th) == bool:
+ subparser.add_argument(f"--{opt}", action='store_true')
+ else:
+ 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
+ args = vars(parser.parse_args())
+
+ if not args["subcommand"]:
+ print("Subcommand required!")
+ parser.print_usage()
+ sys.exit(1)
+
+ 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"]
+
+ # 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 _is_show(function_name):
+ args["raw"] = False
+
+ 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
+ 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/cpu.py b/src/op_mode/cpu.py
new file mode 100755
index 000000000..f9c425826
--- /dev/null
+++ b/src/op_mode/cpu.py
@@ -0,0 +1,82 @@
+#!/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 <http://www.gnu.org/licenses/>.
+
+import sys
+
+import vyos.cpu
+import vyos.opmode
+
+from jinja2 import Template
+
+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 <http://www.gnu.org/licenses/>.
-
-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_ram.py b/src/op_mode/memory.py
index 2b0be3965..a3870e498 100755
--- a/src/op_mode/show_ram.py
+++ b/src/op_mode/memory.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# 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
@@ -15,7 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-def get_system_memory():
+import sys
+
+import vyos.opmode
+
+
+def _get_system_memory():
from re import search as re_search
def find_value(keyword, mem_data):
@@ -43,10 +48,10 @@ def get_system_memory():
return res
-def get_system_memory_human():
+def _get_system_memory_human():
from vyos.util import bytes_to_human
- mem = get_system_memory()
+ mem = _get_system_memory()
for key in mem:
# The Linux kernel exposes memory values in kilobytes,
@@ -55,17 +60,31 @@ def get_system_memory_human():
return mem
-def get_raw_data():
- return get_system_memory_human()
-
-def get_formatted_output():
- mem = get_raw_data()
+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__':
- print(get_formatted_output())
+ 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_neigh.py b/src/op_mode/neighbor.py
index d874bd544..d86a372ac 100755
--- a/src/op_mode/show_neigh.py
+++ b/src/op_mode/neighbor.py
@@ -28,29 +28,37 @@
# ]
import sys
+import typing
+import vyos.opmode
-def get_raw_data(family, device=None, state=None):
+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 device:
- device = f"dev {device}"
+ if interface:
+ if not interface_exists(interface):
+ raise ValueError(f"Interface '{interface}' does not exist in the system")
+ interface = f"dev {interface}"
else:
- device = ""
+ interface = ""
if state:
state = f"nud {state}"
else:
state = ""
- neigh_cmd = f"ip --family {family} --json neighbor list {device} {state}"
+ neigh_cmd = f"ip --family {family} --json neighbor list {interface} {state}"
data = loads(cmd(neigh_cmd))
return data
-def get_formatted_output(family, device=None, state=None):
+def format_neighbors(neighs, interface=None):
from tabulate import tabulate
def entry_to_list(e, intf=None):
@@ -68,35 +76,47 @@ def get_formatted_output(family, device=None, state=None):
# Device field is absent from outputs of `ip neigh list dev ...`
if "dev" in e:
dev = e["dev"]
- elif device:
- dev = device
+ elif interface:
+ dev = interface
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
+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)
- 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")
+ if raw:
+ return data
+ else:
+ return format_neighbors(data, interface)
- args = parser.parse_args()
+def reset(family: str, interface: typing.Optional[str], address: typing.Optional[str]):
+ from vyos.util import run
- 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""")
+ 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__':
try:
- print(get_formatted_output(args.family, device=args.interface, state=args.state))
+ 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/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 <http://www.gnu.org/licenses/>.
+#
+# Purpose:
+# Displays routing table information.
+# Used by the "run <ip|ipv6> 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)
+
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 <http://www.gnu.org/licenses/>.
-
-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)
-
diff --git a/src/op_mode/show_version.py b/src/op_mode/version.py
index b82ab6eca..06208c3e5 100755
--- a/src/op_mode/show_version.py
+++ b/src/op_mode/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
@@ -18,13 +18,14 @@
# 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 sys import exit
-from vyos.util import call
version_output_tmpl = """
Version: VyOS {{version}}
@@ -45,32 +46,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: typing.Optional[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())