Blame SOURCES/BZ_1932247-nm-Don-t-touch-unmanaged-interface-unless-desired.patch

b2ff4f
From ccdcd8f86544a6364109a0c0142d05a5afacf64e Mon Sep 17 00:00:00 2001
b2ff4f
From: Gris Ge <fge@redhat.com>
b2ff4f
Date: Tue, 2 Mar 2021 15:31:20 +0800
b2ff4f
Subject: [PATCH] nm: Don't touch unmanaged interface unless desired
b2ff4f
b2ff4f
We should ignore NetworkManager unmanaged interface when applying and
b2ff4f
verifying unless certain interface listed in desired state explicitly.
b2ff4f
b2ff4f
Introduced new plugin interface
b2ff4f
`NmstatePlugin.get_ignored_kernel_interface_names()` where plugin may
b2ff4f
include a list of interface names which should be ignored during
b2ff4f
verification stage.
b2ff4f
b2ff4f
Integration test case added to simulate CNV usage on partial editing
b2ff4f
a linux bridge holding NM unmanaged interface.
b2ff4f
b2ff4f
Signed-off-by: Gris Ge <fge@redhat.com>
b2ff4f
---
b2ff4f
 libnmstate/ifaces/base_iface.py           |  3 ++
b2ff4f
 libnmstate/ifaces/ifaces.py               | 26 ++++++++--------
b2ff4f
 libnmstate/netapplier.py                  |  6 ++++
b2ff4f
 libnmstate/nispor/plugin.py               |  6 +++-
b2ff4f
 libnmstate/nm/plugin.py                   | 25 ++++++++++++++++
b2ff4f
 libnmstate/plugin.py                      |  7 +++++
b2ff4f
 tests/integration/linux_bridge_test.py    |  8 +----
b2ff4f
 tests/integration/nm/linux_bridge_test.py | 36 ++++++++++++++++++++++-
b2ff4f
 8 files changed, 95 insertions(+), 22 deletions(-)
b2ff4f
b2ff4f
diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py
b2ff4f
index 227c1d20..e3f2a1ca 100644
b2ff4f
--- a/libnmstate/ifaces/base_iface.py
b2ff4f
+++ b/libnmstate/ifaces/base_iface.py
b2ff4f
@@ -322,6 +322,9 @@ class BaseIface:
b2ff4f
     def mark_as_up(self):
b2ff4f
         self.raw[Interface.STATE] = InterfaceState.UP
b2ff4f
 
b2ff4f
+    def mark_as_ignored(self):
b2ff4f
+        self.raw[Interface.STATE] = InterfaceState.IGNORE
b2ff4f
+
b2ff4f
     @property
b2ff4f
     def is_controller(self):
b2ff4f
         return False
b2ff4f
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
b2ff4f
index 6c94a986..efa24aa3 100644
b2ff4f
--- a/libnmstate/ifaces/ifaces.py
b2ff4f
+++ b/libnmstate/ifaces/ifaces.py
b2ff4f
@@ -95,7 +95,6 @@ class Ifaces:
b2ff4f
         self._kernel_ifaces = {}
b2ff4f
         self._user_space_ifaces = _UserSpaceIfaces()
b2ff4f
         self._cur_user_space_ifaces = _UserSpaceIfaces()
b2ff4f
-        self._ignored_ifaces = set()
b2ff4f
         if cur_iface_infos:
b2ff4f
             for iface_info in cur_iface_infos:
b2ff4f
                 cur_iface = _to_specific_iface_obj(iface_info, save_to_disk)
b2ff4f
@@ -143,10 +142,6 @@ class Ifaces:
b2ff4f
                 ):
b2ff4f
                     # Ignore interface with unknown type
b2ff4f
                     continue
b2ff4f
-                if iface.is_ignore:
b2ff4f
-                    self._ignored_ifaces.add(
b2ff4f
-                        (iface.name, iface.type, iface.is_user_space_only)
b2ff4f
-                    )
b2ff4f
                 if cur_iface:
b2ff4f
                     iface.merge(cur_iface)
b2ff4f
                 iface.mark_as_desired()
b2ff4f
@@ -169,6 +164,10 @@ class Ifaces:
b2ff4f
 
b2ff4f
             self._pre_edit_validation_and_cleanup()
b2ff4f
 
b2ff4f
+    @property
b2ff4f
+    def _ignored_ifaces(self):
b2ff4f
+        return [iface for iface in self.all_ifaces() if iface.is_ignore]
b2ff4f
+
b2ff4f
     def _apply_copy_mac_from(self):
b2ff4f
         for iface in self.all_kernel_ifaces.values():
b2ff4f
             if iface.type not in (
b2ff4f
@@ -284,7 +283,7 @@ class Ifaces:
b2ff4f
         if not defiend in desire state
b2ff4f
         """
b2ff4f
         for iface in self.all_ifaces():
b2ff4f
-            if iface.is_up and iface.is_controller:
b2ff4f
+            if iface.is_desired and iface.is_up and iface.is_controller:
b2ff4f
                 for port_name in iface.port:
b2ff4f
                     port_iface = self._kernel_ifaces[port_name]
b2ff4f
                     if not port_iface.is_desired and not port_iface.is_up:
b2ff4f
@@ -550,13 +549,14 @@ class Ifaces:
b2ff4f
         return None
b2ff4f
 
b2ff4f
     def _remove_iface(self, iface_name, iface_type):
b2ff4f
-        cur_iface = self._cur_kernel_ifaces.get(iface_name, iface_type)
b2ff4f
+        cur_iface = self._user_space_ifaces.get(iface_name, iface_type)
b2ff4f
         if cur_iface:
b2ff4f
             self._user_space_ifaces.remove(cur_iface)
b2ff4f
         else:
b2ff4f
             cur_iface = self._kernel_ifaces.get(iface_name)
b2ff4f
             if (
b2ff4f
-                iface_type
b2ff4f
+                cur_iface
b2ff4f
+                and iface_type
b2ff4f
                 and iface_type != InterfaceType.UNKNOWN
b2ff4f
                 and iface_type == cur_iface.type
b2ff4f
             ):
b2ff4f
@@ -813,14 +813,14 @@ class Ifaces:
b2ff4f
                     port_controller_map[port_name] = iface.name
b2ff4f
 
b2ff4f
     def _remove_ignore_interfaces(self, ignored_ifaces):
b2ff4f
-        for iface_name, iface_type, _ in ignored_ifaces:
b2ff4f
-            self._remove_iface(iface_name, iface_type)
b2ff4f
+        for iface in ignored_ifaces:
b2ff4f
+            self._remove_iface(iface.name, iface.type)
b2ff4f
 
b2ff4f
         # Only kernel interface can be used as port
b2ff4f
         ignored_kernel_iface_names = set(
b2ff4f
-            iface_name
b2ff4f
-            for iface_name, _, is_user_space_only in ignored_ifaces
b2ff4f
-            if not is_user_space_only
b2ff4f
+            iface.name
b2ff4f
+            for iface in ignored_ifaces
b2ff4f
+            if not iface.is_user_space_only
b2ff4f
         )
b2ff4f
 
b2ff4f
         # Remove ignored port
b2ff4f
diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py
b2ff4f
index 3c5759b4..a020f003 100644
b2ff4f
--- a/libnmstate/netapplier.py
b2ff4f
+++ b/libnmstate/netapplier.py
b2ff4f
@@ -107,8 +107,14 @@ def rollback(*, checkpoint=None):
b2ff4f
 
b2ff4f
 
b2ff4f
 def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk):
b2ff4f
+    for plugin in plugins:
b2ff4f
+        for iface_name in plugin.get_ignored_kernel_interface_names():
b2ff4f
+            iface = net_state.ifaces.all_kernel_ifaces.get(iface_name)
b2ff4f
+            if iface and not iface.is_desired:
b2ff4f
+                iface.mark_as_ignored()
b2ff4f
     for plugin in plugins:
b2ff4f
         plugin.apply_changes(net_state, save_to_disk)
b2ff4f
+
b2ff4f
     verified = False
b2ff4f
     if verify_change:
b2ff4f
         if _net_state_contains_sriov_interface(net_state):
b2ff4f
diff --git a/libnmstate/nispor/plugin.py b/libnmstate/nispor/plugin.py
b2ff4f
index dc0ea760..19b21d56 100644
b2ff4f
--- a/libnmstate/nispor/plugin.py
b2ff4f
+++ b/libnmstate/nispor/plugin.py
b2ff4f
@@ -159,7 +159,11 @@ class NisporPlugin(NmstatePlugin):
b2ff4f
         np_state = NisporNetState.retrieve()
b2ff4f
         logging.debug(f"Nispor: current network state {np_state}")
b2ff4f
         for iface in net_state.ifaces.all_ifaces():
b2ff4f
-            if iface.is_desired:
b2ff4f
+            if iface.is_ignore:
b2ff4f
+                logging.debug(
b2ff4f
+                    f"Nispor: Interface {iface.name} {iface.type} ignored"
b2ff4f
+                )
b2ff4f
+            elif iface.is_desired:
b2ff4f
                 logging.debug(
b2ff4f
                     f"Nispor: desired network state {iface.to_dict()}"
b2ff4f
                 )
b2ff4f
diff --git a/libnmstate/nm/plugin.py b/libnmstate/nm/plugin.py
b2ff4f
index 302b4cca..335d93c7 100644
b2ff4f
--- a/libnmstate/nm/plugin.py
b2ff4f
+++ b/libnmstate/nm/plugin.py
b2ff4f
@@ -36,6 +36,7 @@ from .checkpoint import get_checkpoints
b2ff4f
 from .common import NM
b2ff4f
 from .context import NmContext
b2ff4f
 from .device import get_device_common_info
b2ff4f
+from .device import get_iface_type
b2ff4f
 from .device import list_devices
b2ff4f
 from .dns import get_running as get_dns_running
b2ff4f
 from .dns import get_running_config as get_dns_running_config
b2ff4f
@@ -268,6 +269,21 @@ class NetworkManagerPlugin(NmstatePlugin):
b2ff4f
             )
b2ff4f
         return NmProfiles(None).generate_config_strings(net_state)
b2ff4f
 
b2ff4f
+    def get_ignored_kernel_interface_names(self):
b2ff4f
+        """
b2ff4f
+        Return a list of unmanged kernel interface names.
b2ff4f
+        """
b2ff4f
+        ignored_ifaces = set()
b2ff4f
+        for nm_dev in list_devices(self.client):
b2ff4f
+            if (
b2ff4f
+                nm_dev
b2ff4f
+                and nm_dev.get_iface()
b2ff4f
+                and not nm_dev.get_managed()
b2ff4f
+                and _is_kernel_iface(nm_dev)
b2ff4f
+            ):
b2ff4f
+                ignored_ifaces.add(nm_dev.get_iface())
b2ff4f
+        return list(ignored_ifaces)
b2ff4f
+
b2ff4f
 
b2ff4f
 def _remove_ovs_bridge_unsupported_entries(iface_info):
b2ff4f
     """
b2ff4f
@@ -283,3 +299,12 @@ def _remove_ovs_bridge_unsupported_entries(iface_info):
b2ff4f
 
b2ff4f
 def _nm_utils_decode_version():
b2ff4f
     return f"{NM.MAJOR_VERSION}.{NM.MINOR_VERSION}.{NM.MICRO_VERSION}"
b2ff4f
+
b2ff4f
+
b2ff4f
+def _is_kernel_iface(nm_dev):
b2ff4f
+    iface_type = get_iface_type(nm_dev)
b2ff4f
+    return iface_type != InterfaceType.UNKNOWN and iface_type not in (
b2ff4f
+        InterfaceType.OVS_BRIDGE,
b2ff4f
+        InterfaceType.OVS_INTERFACE,
b2ff4f
+        InterfaceType.OVS_PORT,
b2ff4f
+    )
b2ff4f
diff --git a/libnmstate/plugin.py b/libnmstate/plugin.py
b2ff4f
index ef3874ff..e1d9ad58 100644
b2ff4f
--- a/libnmstate/plugin.py
b2ff4f
+++ b/libnmstate/plugin.py
b2ff4f
@@ -128,3 +128,10 @@ class NmstatePlugin(metaclass=ABCMeta):
b2ff4f
         persistently.
b2ff4f
         """
b2ff4f
         return []
b2ff4f
+
b2ff4f
+    def get_ignored_kernel_interface_names(self):
b2ff4f
+        """
b2ff4f
+        Return a list of kernel interface names which should be ignored
b2ff4f
+        during verification stage.
b2ff4f
+        """
b2ff4f
+        return []
b2ff4f
-- 
b2ff4f
2.29.2
b2ff4f