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