diff --git a/SOURCES/BZ_1932247-nm-Don-t-touch-unmanaged-interface-unless-desired.patch b/SOURCES/BZ_1932247-nm-Don-t-touch-unmanaged-interface-unless-desired.patch new file mode 100644 index 0000000..ea46af7 --- /dev/null +++ b/SOURCES/BZ_1932247-nm-Don-t-touch-unmanaged-interface-unless-desired.patch @@ -0,0 +1,223 @@ +From ccdcd8f86544a6364109a0c0142d05a5afacf64e Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 2 Mar 2021 15:31:20 +0800 +Subject: [PATCH] nm: Don't touch unmanaged interface unless desired + +We should ignore NetworkManager unmanaged interface when applying and +verifying unless certain interface listed in desired state explicitly. + +Introduced new plugin interface +`NmstatePlugin.get_ignored_kernel_interface_names()` where plugin may +include a list of interface names which should be ignored during +verification stage. + +Integration test case added to simulate CNV usage on partial editing +a linux bridge holding NM unmanaged interface. + +Signed-off-by: Gris Ge +--- + libnmstate/ifaces/base_iface.py | 3 ++ + libnmstate/ifaces/ifaces.py | 26 ++++++++-------- + libnmstate/netapplier.py | 6 ++++ + libnmstate/nispor/plugin.py | 6 +++- + libnmstate/nm/plugin.py | 25 ++++++++++++++++ + libnmstate/plugin.py | 7 +++++ + tests/integration/linux_bridge_test.py | 8 +---- + tests/integration/nm/linux_bridge_test.py | 36 ++++++++++++++++++++++- + 8 files changed, 95 insertions(+), 22 deletions(-) + +diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py +index 227c1d20..e3f2a1ca 100644 +--- a/libnmstate/ifaces/base_iface.py ++++ b/libnmstate/ifaces/base_iface.py +@@ -322,6 +322,9 @@ class BaseIface: + def mark_as_up(self): + self.raw[Interface.STATE] = InterfaceState.UP + ++ def mark_as_ignored(self): ++ self.raw[Interface.STATE] = InterfaceState.IGNORE ++ + @property + def is_controller(self): + return False +diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py +index 6c94a986..efa24aa3 100644 +--- a/libnmstate/ifaces/ifaces.py ++++ b/libnmstate/ifaces/ifaces.py +@@ -95,7 +95,6 @@ class Ifaces: + self._kernel_ifaces = {} + self._user_space_ifaces = _UserSpaceIfaces() + self._cur_user_space_ifaces = _UserSpaceIfaces() +- self._ignored_ifaces = set() + if cur_iface_infos: + for iface_info in cur_iface_infos: + cur_iface = _to_specific_iface_obj(iface_info, save_to_disk) +@@ -143,10 +142,6 @@ class Ifaces: + ): + # Ignore interface with unknown type + continue +- if iface.is_ignore: +- self._ignored_ifaces.add( +- (iface.name, iface.type, iface.is_user_space_only) +- ) + if cur_iface: + iface.merge(cur_iface) + iface.mark_as_desired() +@@ -169,6 +164,10 @@ class Ifaces: + + self._pre_edit_validation_and_cleanup() + ++ @property ++ def _ignored_ifaces(self): ++ return [iface for iface in self.all_ifaces() if iface.is_ignore] ++ + def _apply_copy_mac_from(self): + for iface in self.all_kernel_ifaces.values(): + if iface.type not in ( +@@ -284,7 +283,7 @@ class Ifaces: + if not defiend in desire state + """ + for iface in self.all_ifaces(): +- if iface.is_up and iface.is_controller: ++ if iface.is_desired and iface.is_up and iface.is_controller: + for port_name in iface.port: + port_iface = self._kernel_ifaces[port_name] + if not port_iface.is_desired and not port_iface.is_up: +@@ -550,13 +549,14 @@ class Ifaces: + return None + + def _remove_iface(self, iface_name, iface_type): +- cur_iface = self._cur_kernel_ifaces.get(iface_name, iface_type) ++ cur_iface = self._user_space_ifaces.get(iface_name, iface_type) + if cur_iface: + self._user_space_ifaces.remove(cur_iface) + else: + cur_iface = self._kernel_ifaces.get(iface_name) + if ( +- iface_type ++ cur_iface ++ and iface_type + and iface_type != InterfaceType.UNKNOWN + and iface_type == cur_iface.type + ): +@@ -813,14 +813,14 @@ class Ifaces: + port_controller_map[port_name] = iface.name + + def _remove_ignore_interfaces(self, ignored_ifaces): +- for iface_name, iface_type, _ in ignored_ifaces: +- self._remove_iface(iface_name, iface_type) ++ for iface in ignored_ifaces: ++ self._remove_iface(iface.name, iface.type) + + # Only kernel interface can be used as port + ignored_kernel_iface_names = set( +- iface_name +- for iface_name, _, is_user_space_only in ignored_ifaces +- if not is_user_space_only ++ iface.name ++ for iface in ignored_ifaces ++ if not iface.is_user_space_only + ) + + # Remove ignored port +diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py +index 3c5759b4..a020f003 100644 +--- a/libnmstate/netapplier.py ++++ b/libnmstate/netapplier.py +@@ -107,8 +107,14 @@ def rollback(*, checkpoint=None): + + + def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk): ++ for plugin in plugins: ++ for iface_name in plugin.get_ignored_kernel_interface_names(): ++ iface = net_state.ifaces.all_kernel_ifaces.get(iface_name) ++ if iface and not iface.is_desired: ++ iface.mark_as_ignored() + for plugin in plugins: + plugin.apply_changes(net_state, save_to_disk) ++ + verified = False + if verify_change: + if _net_state_contains_sriov_interface(net_state): +diff --git a/libnmstate/nispor/plugin.py b/libnmstate/nispor/plugin.py +index dc0ea760..19b21d56 100644 +--- a/libnmstate/nispor/plugin.py ++++ b/libnmstate/nispor/plugin.py +@@ -159,7 +159,11 @@ class NisporPlugin(NmstatePlugin): + np_state = NisporNetState.retrieve() + logging.debug(f"Nispor: current network state {np_state}") + for iface in net_state.ifaces.all_ifaces(): +- if iface.is_desired: ++ if iface.is_ignore: ++ logging.debug( ++ f"Nispor: Interface {iface.name} {iface.type} ignored" ++ ) ++ elif iface.is_desired: + logging.debug( + f"Nispor: desired network state {iface.to_dict()}" + ) +diff --git a/libnmstate/nm/plugin.py b/libnmstate/nm/plugin.py +index 302b4cca..335d93c7 100644 +--- a/libnmstate/nm/plugin.py ++++ b/libnmstate/nm/plugin.py +@@ -36,6 +36,7 @@ from .checkpoint import get_checkpoints + from .common import NM + from .context import NmContext + from .device import get_device_common_info ++from .device import get_iface_type + from .device import list_devices + from .dns import get_running as get_dns_running + from .dns import get_running_config as get_dns_running_config +@@ -268,6 +269,21 @@ class NetworkManagerPlugin(NmstatePlugin): + ) + return NmProfiles(None).generate_config_strings(net_state) + ++ def get_ignored_kernel_interface_names(self): ++ """ ++ Return a list of unmanged kernel interface names. ++ """ ++ ignored_ifaces = set() ++ for nm_dev in list_devices(self.client): ++ if ( ++ nm_dev ++ and nm_dev.get_iface() ++ and not nm_dev.get_managed() ++ and _is_kernel_iface(nm_dev) ++ ): ++ ignored_ifaces.add(nm_dev.get_iface()) ++ return list(ignored_ifaces) ++ + + def _remove_ovs_bridge_unsupported_entries(iface_info): + """ +@@ -283,3 +299,12 @@ def _remove_ovs_bridge_unsupported_entries(iface_info): + + def _nm_utils_decode_version(): + return f"{NM.MAJOR_VERSION}.{NM.MINOR_VERSION}.{NM.MICRO_VERSION}" ++ ++ ++def _is_kernel_iface(nm_dev): ++ iface_type = get_iface_type(nm_dev) ++ return iface_type != InterfaceType.UNKNOWN and iface_type not in ( ++ InterfaceType.OVS_BRIDGE, ++ InterfaceType.OVS_INTERFACE, ++ InterfaceType.OVS_PORT, ++ ) +diff --git a/libnmstate/plugin.py b/libnmstate/plugin.py +index ef3874ff..e1d9ad58 100644 +--- a/libnmstate/plugin.py ++++ b/libnmstate/plugin.py +@@ -128,3 +128,10 @@ class NmstatePlugin(metaclass=ABCMeta): + persistently. + """ + return [] ++ ++ def get_ignored_kernel_interface_names(self): ++ """ ++ Return a list of kernel interface names which should be ignored ++ during verification stage. ++ """ ++ return [] +-- +2.29.2 + diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec index 3f992a5..06262d2 100644 --- a/SPECS/nmstate.spec +++ b/SPECS/nmstate.spec @@ -4,7 +4,7 @@ Name: nmstate Version: 1.0.2 -Release: 4%{?dist} +Release: 5%{?dist} Summary: Declarative network manager API License: LGPLv2+ URL: https://github.com/%{srcname}/%{srcname} @@ -13,6 +13,7 @@ Source1: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.g Source2: https://www.nmstate.io/nmstate.gpg Patch1: BZ_1931751-nmstate-fix-return-code.patch Patch2: BZ_1931355-SRIOV-wait-VF-mount-decrease.patch +Patch3: BZ_1932247-nm-Don-t-touch-unmanaged-interface-unless-desired.patch BuildArch: noarch BuildRequires: python3-devel BuildRequires: python3-setuptools @@ -85,6 +86,9 @@ gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0} %{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_ovsdb* %changelog +* Wed Mar 03 2021 Fernando Fernandez Mancera - 1.0.2-5 +- New patch for fixing unmanaged interfaces being managed. RHBZ#1932247 + * Tue Feb 23 2021 Gris Ge - 1.0.2-4 - New patch for SRIOV decrease VF amount. RHBZ#1931355