Blob Blame History Raw
From 3b5cfe36b1b754369cb8d3cc0d461068bf6bb8aa Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
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 <ffmancera@riseup.net>
---
 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 <https://www.gnu.org/licenses/>.
 #
 
+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_<iface>, 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