diff options
| author | Daniil Baturin <daniil@vyos.io> | 2025-08-26 13:25:36 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-26 13:25:36 +0100 |
| commit | 2ab41dd61e4a71c10f16489f9e8b215227dd98e0 (patch) | |
| tree | 7f4f45391c3a5ef625026af0f6f93f127b25f685 | |
| parent | 968409218f914d180b2f9781edf3423ddb8ac738 (diff) | |
| parent | 5176bb18f499f614e24a119fa360fcde9240cbac (diff) | |
| download | vyos-1x-2ab41dd61e4a71c10f16489f9e8b215227dd98e0.tar.gz vyos-1x-2ab41dd61e4a71c10f16489f9e8b215227dd98e0.zip | |
Merge pull request #4673 from l0crian1/kernel-updates
op-mode: T7741: Fixes for 'show interfaces kernel'
| -rw-r--r-- | python/vyos/utils/dict.py | 34 | ||||
| -rw-r--r-- | python/vyos/utils/network.py | 5 | ||||
| -rwxr-xr-x | src/op_mode/interfaces.py | 41 |
3 files changed, 68 insertions, 12 deletions
diff --git a/python/vyos/utils/dict.py b/python/vyos/utils/dict.py index ff21f0677..a43430eed 100644 --- a/python/vyos/utils/dict.py +++ b/python/vyos/utils/dict.py @@ -211,6 +211,39 @@ def dict_set(key_path, value, dict_object): dynamic_dict = dynamic_dict[path_list[i]] dynamic_dict[path_list[len(path_list)-1]] = value +def dict_set_nested(key_path, value, dict_object): + """ + Set value to Python dictionary (dict_object) using a path to the key + delimited by dot ('.'). The key will be added if it does not exist. + Missing keys along the path will be created as nested dictionaries. + + Parameters + ---------- + key_path : str + Dot-delimited path to the key (e.g. "this.is.a.path"). + value : any + The value to set at the final key in the path. + dict_object : dict + Dictionary to modify. Will be updated in place. + + Examples + -------- + d = {} + dict_set_nested("this.is.a.path", 42, d) + # {'this': {'is': {'a': {'path': 42}}}} + + d = {"existing": {"branch": {}}} + dict_set_nested("existing.branch.leaf", "value", d) + # {'existing': {'branch': {'leaf': 'value'}}} + """ + path_list = key_path.split(".") + dynamic_dict = dict_object + for i in range(0, len(path_list) - 1): + if path_list[i] not in dynamic_dict or not isinstance(dynamic_dict[path_list[i]], dict): + dynamic_dict[path_list[i]] = {} + dynamic_dict = dynamic_dict[path_list[i]] + dynamic_dict[path_list[-1]] = value + def dict_delete(key_path, dict_object): """ Delete key in Python dictionary (dict_object) using path to key delimited by dot (.). """ @@ -370,4 +403,3 @@ class FixedDict(dict): if k not in self._allowed: raise ConfigError(f'Option "{k}" has no defined default') super().__setitem__(k, v) - diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py index 0e2cc58cf..93f780d8b 100644 --- a/python/vyos/utils/network.py +++ b/python/vyos/utils/network.py @@ -81,7 +81,10 @@ def get_interface_vrf(interface): """ Returns VRF of given interface """ from vyos.utils.dict import dict_search from vyos.utils.network import get_interface_config - tmp = get_interface_config(interface) + if isinstance(interface, str): + tmp = get_interface_config(interface) + elif isinstance(interface, dict): + tmp = interface if dict_search('linkinfo.info_slave_kind', tmp) == 'vrf': return tmp['master'] return 'default' diff --git a/src/op_mode/interfaces.py b/src/op_mode/interfaces.py index 8e3f0ef65..faeb846eb 100755 --- a/src/op_mode/interfaces.py +++ b/src/op_mode/interfaces.py @@ -21,6 +21,7 @@ import sys import glob import json import typing +import textwrap from datetime import datetime from tabulate import tabulate @@ -28,8 +29,10 @@ import vyos.opmode from vyos.ifconfig import Section from vyos.ifconfig import Interface from vyos.ifconfig import VRRP -from vyos.utils.process import cmd +from vyos.utils.dict import dict_set_nested +from vyos.utils.network import get_interface_vrf from vyos.utils.network import interface_exists +from vyos.utils.process import cmd from vyos.utils.process import rc_cmd from vyos.utils.process import call from vyos.configquery import op_mode_config_dict @@ -86,6 +89,10 @@ def filtered_interfaces(ifnames: typing.Union[str, list], yield interface +def is_interface_has_mac(interface_name): + interface_no_mac = ('tun', 'wg') + return not any(interface_name.startswith(prefix) for prefix in interface_no_mac) + def detailed_output(dataset, headers): for data in dataset: adjusted_rule = data + [""] * (len(headers) - len(data)) # account for different header length, like default-action @@ -246,10 +253,6 @@ def _get_summary_data(ifname: typing.Optional[str], iftype = '' ret = [] - def is_interface_has_mac(interface_name): - interface_no_mac = ('tun', 'wg') - return not any(interface_name.startswith(prefix) for prefix in interface_no_mac) - for interface in filtered_interfaces(ifname, iftype, vif, vrrp): res_intf = {} @@ -328,12 +331,22 @@ def _get_kernel_data(raw, ifname = None, detail = False): def _format_kernel_data(data, detail): output_list = [] + podman_vrf = {} tmpInfo = {} # Sort interfaces by name for interface in sorted(data, key=lambda x: x.get('ifname', '')): + interface_name = interface.get('ifname', '') + + # Skip VRF interfaces if interface.get('linkinfo', {}).get('info_kind') == 'vrf': continue + # Skip spawned interfaces + elif interface_name.startswith(('tunl', 'gre', 'erspan', 'pim6reg')): + continue + + master = interface.get('master', 'default') + vrf = get_interface_vrf(interface) # Get the device model; ex. Intel Corporation Ethernet Controller I225-V dev_model = interface.get('parentdev', '') @@ -353,21 +366,28 @@ def _format_kernel_data(data, detail): prefixlen = ip.get('prefixlen', '') ip_list.append(f"{local}/{prefixlen}") - # If no global IP address, add '-'; indicates no IP address on interface if not has_global: ip_list.append('-') + # Generate a mapping of podman interfaces to their VRF + if interface_name.startswith('pod-'): + dict_set_nested(f'{interface_name}.vrf', master, podman_vrf) + + # If the veth interface's master is a podman interface, the VRF is the VRF of the podman interface + if master.startswith('pod-'): + vrf = podman_vrf.get(master).get('vrf', 'default') + sl_status = ('A' if not 'UP' in interface['flags'] else 'u') + '/' + ('D' if interface['operstate'] == 'DOWN' else 'u') # Generate temporary dict to hold data - tmpInfo['ifname'] = interface.get('ifname', '') + tmpInfo['ifname'] = interface_name tmpInfo['ip'] = ip_list - tmpInfo['mac'] = interface.get('address', '') + tmpInfo['mac'] = interface.get('address', 'n/a') if is_interface_has_mac(interface_name) else 'n/a' tmpInfo['mtu'] = interface.get('mtu', '') - tmpInfo['vrf'] = interface.get('master', 'default') + tmpInfo['vrf'] = vrf tmpInfo['status'] = sl_status - tmpInfo['description'] = interface.get('ifalias', '') + tmpInfo['description'] = "\n".join(textwrap.wrap(interface.get('ifalias', ''), width=50)) tmpInfo['device'] = dev_model tmpInfo['alternate_names'] = interface.get('altnames', '') tmpInfo['minimum_mtu'] = interface.get('min_mtu', '') @@ -583,6 +603,7 @@ def show_kernel(raw: bool, intf_name: typing.Optional[str], detail: bool): if detail: detailed_output(data, detail_header) else: + print('Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down') print(tabulate(data, headers)) def _show_raw(data: list, intf_name: str): |
