From ccdcd8f86544a6364109a0c0142d05a5afacf64e Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
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 <fge@redhat.com>
---
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