From 3b5cfe36b1b754369cb8d3cc0d461068bf6bb8aa Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Wed, 25 Mar 2020 12:21:15 +0100 Subject: [PATCH 06/10] nm.bridge: get ports from sysfs Unmanaged ports were not being shown when showing or editting the bridge. In order to fix that, nmstate is retrieving the bridge ports from sysfs instead of NetworkManager on-disk configuration. Ref: https://bugzilla.redhat.com/1806452 Signed-off-by: Fernando Fernandez Mancera --- libnmstate/nm/bridge.py | 55 +++++++++++++++++--------- tests/integration/linux_bridge_test.py | 34 ++++++++++++++++ 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/libnmstate/nm/bridge.py b/libnmstate/nm/bridge.py index e0ab8e4..672214d 100644 --- a/libnmstate/nm/bridge.py +++ b/libnmstate/nm/bridge.py @@ -17,6 +17,9 @@ # along with this program. If not, see . # +import glob +import os + from libnmstate.error import NmstateNotImplementedError from libnmstate.nm import connection from libnmstate.nm import nmclient @@ -25,6 +28,12 @@ from libnmstate.schema import LinuxBridge as LB BRIDGE_TYPE = "bridge" +BRIDGE_PORT_NMSTATE_TO_SYSFS = { + LB.Port.STP_HAIRPIN_MODE: "hairpin_mode", + LB.Port.STP_PATH_COST: "path_cost", + LB.Port.STP_PRIORITY: "priority", +} + def create_setting(options, base_con_profile): bridge_setting = _get_current_bridge_setting(base_con_profile) @@ -106,10 +115,10 @@ def get_info(nmdev): if not bridge_setting: return info - port_profiles = _get_slave_profiles(nmdev) + port_names_sysfs = _get_slaves_names_from_sysfs(nmdev.get_iface()) props = bridge_setting.props info[LB.CONFIG_SUBTREE] = { - LB.PORT_SUBTREE: _get_bridge_ports_info(port_profiles), + LB.PORT_SUBTREE: _get_bridge_ports_info(port_names_sysfs), LB.OPTIONS_SUBTREE: { LB.Options.MAC_AGEING_TIME: props.ageing_time, LB.Options.GROUP_FORWARD_MASK: props.group_forward_mask, @@ -139,25 +148,35 @@ def _get_bridge_setting(nmdev): return bridge_setting -def _get_bridge_ports_info(port_profiles): - ports_info = [] - for p in port_profiles: - port_info = _get_bridge_port_info(p) - if port_info: - ports_info.append(port_info) - return ports_info +def _get_bridge_ports_info(port_names_sysfs): + return [_get_bridge_port_info(name) for name in port_names_sysfs] -def _get_bridge_port_info(port_profile): - """Report port information.""" +def _get_bridge_port_info(port_name): + """Report port runtime information from sysfs.""" + port = {LB.Port.NAME: port_name} + for option, option_sysfs in BRIDGE_PORT_NMSTATE_TO_SYSFS.items(): + sysfs_path = f"/sys/class/net/{port_name}/brport/{option_sysfs}" + with open(sysfs_path) as f: + option_value = int(f.read()) + if option == LB.Port.STP_HAIRPIN_MODE: + option_value = bool(option_value) + port[option] = option_value + return port - port_setting = port_profile.get_setting_bridge_port() - return { - LB.Port.NAME: port_profile.get_interface_name(), - LB.Port.STP_PRIORITY: port_setting.props.priority, - LB.Port.STP_HAIRPIN_MODE: port_setting.props.hairpin_mode, - LB.Port.STP_PATH_COST: port_setting.props.path_cost, - } + +def _get_slaves_names_from_sysfs(master): + """ + We need to use glob in order to get the slaves name due to bug in + NetworkManager. + Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1809547 + """ + slaves = [] + for sysfs_slave in glob.iglob(f"/sys/class/net/{master}/lower_*"): + # The format is lower_, we need to remove the "lower_" prefix + prefix_length = len("lower_") + slaves.append(os.path.basename(sysfs_slave)[prefix_length:]) + return slaves def _get_slave_profiles(master_device): -- 2.25.1