diff --git a/SOURCES/BZ_1961912-fix-ovs-interface-activation.patch b/SOURCES/BZ_1961912-fix-ovs-interface-activation.patch new file mode 100644 index 0000000..81109e5 --- /dev/null +++ b/SOURCES/BZ_1961912-fix-ovs-interface-activation.patch @@ -0,0 +1,137 @@ +From 95d77329b30c9a9a435a881941e27f9a1bed074e Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Wed, 5 May 2021 10:14:40 +0200 +Subject: [PATCH 1/2] nm.profile: do not activate new interfaces twice + +The current code is always adding the action MODIFIED if the interface +is marked as up on the desired state. When a new interface is being +added, Nmstate is adding two actions MODIFIED and NEW_*, that is +incorrect. + +This patch is improving the performance when creating new interfaces. + +Signed-off-by: Fernando Fernandez Mancera +Signed-off-by: Gris Ge +--- + libnmstate/nm/profile.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/libnmstate/nm/profile.py b/libnmstate/nm/profile.py +index b4814d9..e117dfe 100644 +--- a/libnmstate/nm/profile.py ++++ b/libnmstate/nm/profile.py +@@ -164,7 +164,6 @@ class NmProfile: + if self._iface.is_virtual and self._nm_dev: + self._add_action(NmProfile.ACTION_DELETE_DEVICE) + elif self._iface.is_up and not self._needs_veth_activation(): +- self._add_action(NmProfile.ACTION_MODIFIED) + if not self._nm_dev: + if self._iface.type == InterfaceType.OVS_PORT: + self._add_action(NmProfile.ACTION_NEW_OVS_PORT) +@@ -176,6 +175,8 @@ class NmProfile: + self._add_action(NmProfile.ACTION_NEW_VXLAN) + else: + self._add_action(NmProfile.ACTION_NEW_IFACES) ++ else: ++ self._add_action(NmProfile.ACTION_MODIFIED) + + elif self._iface.is_down: + if self._nm_ac: +-- +2.31.1 + + +From 9ea925a9a978671881e428abf82aac39c01376e8 Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Wed, 5 May 2021 10:52:32 +0200 +Subject: [PATCH 2/2] nm.profile: activate modified ovs-port first + +When removing an ovs-br with an ovs-iface attached and creating a new +ovs-br with the ovs-iface attached in the same transaction the order of +the activations is important. + +The ovs-port must be activated before the ovs-iface. If not, NM will +throw a dependency error. This error is correct because the ovs-iface +depends on the ovs-port, so it must be updated first. + +This fixes: + +``` +Traceback (most recent call last): + File "/usr/lib/python3.6/site-packages/libnmstate/nm/checkpoint.py", line 93, in _refresh_checkpoint_timeout + self._dbuspath, self._timeout, cancellable, cb, cb_data +TypeError: Argument 1 does not allow None as a value +^CTraceback (most recent call last): + File "/usr/lib/python3.6/site-packages/libnmstate/nmstate.py", line 53, in plugin_context + yield plugins + File "/usr/lib/python3.6/site-packages/libnmstate/netapplier.py", line 78, in apply + _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk) + File "/usr/lib/python3.6/site-packages/libnmstate/netapplier.py", line 116, in _apply_ifaces_state + plugin.apply_changes(net_state, save_to_disk) + File "/usr/lib/python3.6/site-packages/libnmstate/nm/plugin.py", line 204, in apply_changes + NmProfiles(self.context).apply_config(net_state, save_to_disk) + File "/usr/lib/python3.6/site-packages/libnmstate/nm/profiles.py", line 89, in apply_config + self._ctx.wait_all_finish() + File "/usr/lib/python3.6/site-packages/libnmstate/nm/context.py", line 213, in wait_all_finish + raise tmp_error +libnmstate.error.NmstateLibnmError: Activate profile uuid:3a359cd0-d68a-4c7a-ae50-f97b47390142 iface:net type: ovs-interface failed: reason= +``` + +Integration test added + +Ref: https://bugzilla.redhat.com/1947287 + +Signed-off-by: Fernando Fernandez Mancera +Signed-off-by: Gris Ge +--- + libnmstate/nm/profile.py | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/libnmstate/nm/profile.py b/libnmstate/nm/profile.py +index e117dfe..b655885 100644 +--- a/libnmstate/nm/profile.py ++++ b/libnmstate/nm/profile.py +@@ -69,6 +69,8 @@ class NmProfile: + ACTION_OTHER_MASTER = "other_master" + ACTION_DELETE_PROFILE = "delete_profile" + ACTION_TOP_MASTER = "top_master" ++ ACTION_MODIFIED_OVS_PORT = "modified_ovs_port" ++ ACTION_MODIFIED_OVS_IFACE = "modified_ovs_iface" + + # This is order on group for activation/deactivation + ACTIONS = ( +@@ -81,6 +83,8 @@ class NmProfile: + ACTION_NEW_OVS_IFACE, + ACTION_NEW_VETH, + ACTION_NEW_VETH_PEER, ++ ACTION_MODIFIED_OVS_PORT, ++ ACTION_MODIFIED_OVS_IFACE, + ACTION_MODIFIED, + ACTION_NEW_VLAN, + ACTION_NEW_VXLAN, +@@ -176,7 +180,12 @@ class NmProfile: + else: + self._add_action(NmProfile.ACTION_NEW_IFACES) + else: +- self._add_action(NmProfile.ACTION_MODIFIED) ++ if self._iface.type == InterfaceType.OVS_PORT: ++ self._add_action(NmProfile.ACTION_MODIFIED_OVS_PORT) ++ if self._iface.type == InterfaceType.OVS_INTERFACE: ++ self._add_action(NmProfile.ACTION_MODIFIED_OVS_IFACE) ++ else: ++ self._add_action(NmProfile.ACTION_MODIFIED) + + elif self._iface.is_down: + if self._nm_ac: +@@ -420,6 +429,8 @@ class NmProfile: + def do_action(self, action): + if action in ( + NmProfile.ACTION_MODIFIED, ++ NmProfile.ACTION_MODIFIED_OVS_PORT, ++ NmProfile.ACTION_MODIFIED_OVS_IFACE, + NmProfile.ACTION_ACTIVATE_FIRST, + NmProfile.ACTION_TOP_MASTER, + NmProfile.ACTION_NEW_IFACES, +-- +2.31.1 + diff --git a/SOURCES/BZ_1961914-do-not-use-unmanaged-interface-for-dns.patch b/SOURCES/BZ_1961914-do-not-use-unmanaged-interface-for-dns.patch new file mode 100644 index 0000000..c6b7af3 --- /dev/null +++ b/SOURCES/BZ_1961914-do-not-use-unmanaged-interface-for-dns.patch @@ -0,0 +1,163 @@ +From 72edab395316ba1ae69ea4d788b0572c935759ac Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Tue, 27 Apr 2021 10:19:02 +0200 +Subject: [PATCH] net_state: mark ifaces as ignored earlier + +Nmstate is not touching unmanaged interfaces if they are not being +included in the desired state. If an unmanaged interface has a default +gateway configured and we try to add a dns server, Nmstate will pick the +unmanaged interface because at that point it is not marked as ignored. + +As the interface is being marked as ignored later, the changes are not +being applied and is failing on verification. In order to avoid this, +the unmanaged interfaces should be marked as ignored earlier so they are +not considered for DNS/Routes changes. + +Ref: https://bugzilla.redhat.com/1944582 + +Signed-off-by: Fernando Fernandez Mancera +Signed-off-by: Gris Ge +--- + libnmstate/dns.py | 11 +++++++++-- + libnmstate/net_state.py | 13 +++++++++++-- + libnmstate/netapplier.py | 19 +++++++++++++------ + 3 files changed, 33 insertions(+), 10 deletions(-) + +diff --git a/libnmstate/dns.py b/libnmstate/dns.py +index 1fb2cc8..d44a869 100644 +--- a/libnmstate/dns.py ++++ b/libnmstate/dns.py +@@ -133,7 +133,7 @@ class DnsState: + Return tuple: (ipv4_iface, ipv6_iface) + """ + ipv4_iface, ipv6_iface = self._find_ifaces_with_static_gateways( +- route_state ++ ifaces, route_state + ) + if not (ipv4_iface and ipv6_iface): + ( +@@ -147,7 +147,7 @@ class DnsState: + + return ipv4_iface, ipv6_iface + +- def _find_ifaces_with_static_gateways(self, route_state): ++ def _find_ifaces_with_static_gateways(self, ifaces, route_state): + """ + Return tuple of interfaces with IPv4 and IPv6 static gateways. + """ +@@ -158,6 +158,11 @@ class DnsState: + if ipv4_iface and ipv6_iface: + return (ipv4_iface, ipv6_iface) + if route.is_gateway: ++ # Only interfaces not ignored can be considered as valid ++ # static gateway for nameservers. ++ iface = ifaces.all_kernel_ifaces.get(iface_name) ++ if iface and iface.is_ignore: ++ continue + if route.is_ipv6: + ipv6_iface = iface_name + else: +@@ -168,6 +173,8 @@ class DnsState: + ipv4_iface = None + ipv6_iface = None + for iface in ifaces.all_kernel_ifaces.values(): ++ if iface.is_ignore: ++ continue + if ipv4_iface and ipv6_iface: + return (ipv4_iface, ipv6_iface) + for family in (Interface.IPV4, Interface.IPV6): +diff --git a/libnmstate/net_state.py b/libnmstate/net_state.py +index 5dc7b43..713b7dc 100644 +--- a/libnmstate/net_state.py ++++ b/libnmstate/net_state.py +@@ -1,5 +1,5 @@ + # +-# Copyright (c) 2020 Red Hat, Inc. ++# Copyright (c) 2020-2021 Red Hat, Inc. + # + # This file is part of nmstate + # +@@ -26,8 +26,8 @@ from libnmstate.schema import Interface + from libnmstate.schema import Route + from libnmstate.schema import RouteRule + +-from .ifaces import Ifaces + from .dns import DnsState ++from .ifaces import Ifaces + from .route import RouteState + from .route_rule import RouteRuleState + from .state import state_match +@@ -37,6 +37,7 @@ class NetState: + def __init__( + self, + desire_state, ++ ignored_ifnames=[], + current_state=None, + save_to_disk=True, + gen_conf_mode=False, +@@ -49,6 +50,8 @@ class NetState: + save_to_disk, + gen_conf_mode, + ) ++ if not gen_conf_mode: ++ self._mark_ignored_kernel_ifaces(ignored_ifnames) + self._route = RouteState( + self._ifaces, + desire_state.get(Route.KEY), +@@ -70,6 +73,12 @@ class NetState: + self._ifaces.gen_route_metadata(self._route) + self._ifaces.gen_route_rule_metadata(self._route_rule, self._route) + ++ def _mark_ignored_kernel_ifaces(self, ignored_ifnames): ++ for iface_name in ignored_ifnames: ++ iface = self._ifaces.all_kernel_ifaces.get(iface_name) ++ if iface and not iface.is_desired: ++ iface.mark_as_ignored() ++ + def verify(self, current_state): + self._ifaces.verify(current_state.get(Interface.KEY)) + self._dns.verify(current_state.get(DNS.KEY)) +diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py +index a020f00..202494d 100644 +--- a/libnmstate/netapplier.py ++++ b/libnmstate/netapplier.py +@@ -73,7 +73,10 @@ def apply( + validator.validate_capabilities( + desired_state, plugins_capabilities(plugins) + ) +- net_state = NetState(desired_state, current_state, save_to_disk) ++ ignored_ifnames = _get_ignored_interface_names(plugins) ++ net_state = NetState( ++ desired_state, ignored_ifnames, current_state, save_to_disk ++ ) + checkpoints = create_checkpoints(plugins, rollback_timeout) + _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk) + if commit: +@@ -107,11 +110,6 @@ 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) + +@@ -145,3 +143,12 @@ def _net_state_contains_sriov_interface(net_state): + def _verify_change(plugins, net_state): + current_state = show_with_plugins(plugins) + net_state.verify(current_state) ++ ++ ++def _get_ignored_interface_names(plugins): ++ ifaces = set() ++ for plugin in plugins: ++ for iface_name in plugin.get_ignored_kernel_interface_names(): ++ ifaces.add(iface_name) ++ ++ return ifaces +-- +2.31.1 + diff --git a/SOURCES/BZ_1964439-ovs-Fix-is_ovs_running-in-container-environment.patch b/SOURCES/BZ_1964439-ovs-Fix-is_ovs_running-in-container-environment.patch new file mode 100644 index 0000000..5856e10 --- /dev/null +++ b/SOURCES/BZ_1964439-ovs-Fix-is_ovs_running-in-container-environment.patch @@ -0,0 +1,222 @@ +From 48c7645ce8849ac31298e6c2b1d5661d0f581279 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Mon, 17 May 2021 16:09:52 +0800 +Subject: [PATCH 1/2] ovs: Fix `is_ovs_running()` in container environment. + +In k8s container environment, the OVS database socket +/var/run/openvswitch/db.sock is mounted from host, so NM can managed it +without the ovs daemon running in container. + +To support that, this patch removed the top level checking on +`is_ovs_running()` and trust plugin raise the proper error on failure. + +Patched the NM plugin to check the error +`NM.DeviceStateReason.OVSDB_FAILED` on activation failure, raise +`NmstateDependencyError` if OVS DB failed to connected. + +NM will not raise any error when creating OVS internal interface with +OVSDB mounted to /dev/null, NM will keep showing the OVS interface as +ACTIVATING, changed the fallback checker to give only 30 seconds for OVS +interface to exit `NM.DeviceState.PREPARE`, if not treat it as OVS +daemon malfunctioning. + +Updated integration test case to mask(mount /dev/null) the OVS DB socket +file for simulating the stopped OVS daemon. + +Signed-off-by: Gris Ge +Signed-off-by: Fernando Fernandez Mancera +--- + libnmstate/ifaces/ovs.py | 15 ---------- + libnmstate/nm/active_connection.py | 47 ++++++++++++++++++++++++++---- + libnmstate/nm/plugin.py | 3 +- + libnmstate/validator.py | 16 +++------- + tests/integration/ovs_test.py | 41 +++++++++++--------------- + 5 files changed, 64 insertions(+), 58 deletions(-) + +diff --git a/libnmstate/ifaces/ovs.py b/libnmstate/ifaces/ovs.py +index 24d4aba..28892ad 100644 +--- a/libnmstate/ifaces/ovs.py ++++ b/libnmstate/ifaces/ovs.py +@@ -19,7 +19,6 @@ + + from copy import deepcopy + from operator import itemgetter +-import subprocess + import warnings + + from libnmstate.error import NmstateValueError +@@ -252,20 +251,6 @@ class OvsInternalIface(BaseIface): + self._info.pop(Interface.MAC, None) + + +-def is_ovs_running(): +- try: +- subprocess.run( +- ("systemctl", "status", "openvswitch"), +- stdout=subprocess.DEVNULL, +- stderr=subprocess.DEVNULL, +- check=True, +- timeout=SYSTEMCTL_TIMEOUT_SECONDS, +- ) +- return True +- except Exception: +- return False +- +- + def is_ovs_lag_port(port_state): + return port_state.get(OVSBridge.Port.LINK_AGGREGATION_SUBTREE) is not None + +diff --git a/libnmstate/nm/active_connection.py b/libnmstate/nm/active_connection.py +index ddf93a7..150256f 100644 +--- a/libnmstate/nm/active_connection.py ++++ b/libnmstate/nm/active_connection.py +@@ -20,6 +20,7 @@ + import logging + + from libnmstate.error import NmstateLibnmError ++from libnmstate.error import NmstateDependencyError + from libnmstate.error import NmstateInternalError + + from .common import GLib +@@ -33,6 +34,7 @@ from .ipv6 import is_dynamic as is_ipv6_dynamic + + NM_AC_STATE_CHANGED_SIGNAL = "state-changed" + FALLBACK_CHECKER_INTERNAL = 15 ++MAX_OVS_IFACE_PREPARE_TIME = FALLBACK_CHECKER_INTERNAL * 2 + GIO_ERROR_DOMAIN = "g-io-error-quark" + + +@@ -92,6 +94,7 @@ class ProfileActivation: + self._dev_handlers = set() + self._action = None + self._fallback_checker = None ++ self._fallback_checker_counter = 0 + + def run(self): + specific_object = None +@@ -336,19 +339,53 @@ class ProfileActivation: + self._activation_clean_up() + self._ctx.finish_async(self._action) + elif not is_activating(self._nm_ac, self._nm_dev): +- reason = f"{self._nm_ac.get_state_reason()}" ++ nm_ac_reason = f"{self._nm_ac.get_state_reason()}" ++ nm_dev_reason = None + if self._nm_dev: +- reason += f" {self._nm_dev.get_state_reason()}" ++ nm_dev_reason = self._nm_dev.get_state_reason() ++ ++ if nm_dev_reason == NM.DeviceStateReason.OVSDB_FAILED: ++ error = NmstateDependencyError( ++ f"{self._action} failed: failed to communicating with " ++ f"Open vSwitch database, {nm_dev_reason}" ++ ) ++ else: ++ reason = nm_ac_reason + ( ++ str(nm_dev_reason) if nm_dev_reason else "" ++ ) ++ error = NmstateLibnmError( ++ f"{self._action} failed: reason={reason}" ++ ) + self._activation_clean_up() +- self._ctx.fail( +- NmstateLibnmError(f"{self._action} failed: reason={reason}") +- ) ++ self._ctx.fail(error) + + def _fallback_checker_callback(self, _user_data): ++ self._fallback_checker_counter += 1 + nm_dev = get_nm_dev(self._ctx, self._iface_name, self._iface_type) + if nm_dev: + self._nm_dev = nm_dev + self._activation_progress_check() ++ # When OVSDB connection is invalid(such as been mounted as ++ # /dev/null), NM will hang on the activation of ovs internal ++ # interface with state ACITVATING with reason UNKNOWN forever with ++ # no state change signal. The fallback check only found it ++ # as activating which lead us hang till killed by idle timeout. ++ # To prevent that, when we found OVS interface interface in ++ # `NM.DeviceState.PREPARE` on in second call of fallbacker, ++ # we fail the action as NmstateDependencyError. ++ if ( ++ self._fallback_checker_counter ++ >= MAX_OVS_IFACE_PREPARE_TIME / FALLBACK_CHECKER_INTERNAL ++ and nm_dev.get_device_type() == NM.DeviceType.OVS_INTERFACE ++ and nm_dev.get_state() == NM.DeviceState.PREPARE ++ ): ++ self._ctx.fail( ++ NmstateDependencyError( ++ f"{self._action} failed: timeout on creating OVS " ++ "interface, please check Open vSwitch daemon" ++ ) ++ ) ++ + return GLib.SOURCE_CONTINUE + + +diff --git a/libnmstate/nm/plugin.py b/libnmstate/nm/plugin.py +index 335d93c..da933b3 100644 +--- a/libnmstate/nm/plugin.py ++++ b/libnmstate/nm/plugin.py +@@ -23,7 +23,6 @@ from operator import itemgetter + from libnmstate.error import NmstateDependencyError + from libnmstate.error import NmstateNotSupportedError + from libnmstate.error import NmstateValueError +-from libnmstate.ifaces.ovs import is_ovs_running + from libnmstate.schema import DNS + from libnmstate.schema import Interface + from libnmstate.schema import InterfaceType +@@ -103,7 +102,7 @@ class NetworkManagerPlugin(NmstatePlugin): + @property + def capabilities(self): + capabilities = [] +- if has_ovs_capability(self.client) and is_ovs_running(): ++ if has_ovs_capability(self.client): + capabilities.append(NmstatePlugin.OVS_CAPABILITY) + if has_team_capability(self.client): + capabilities.append(NmstatePlugin.TEAM_CAPABILITY) +diff --git a/libnmstate/validator.py b/libnmstate/validator.py +index 02890b4..cd3b540 100644 +--- a/libnmstate/validator.py ++++ b/libnmstate/validator.py +@@ -22,7 +22,6 @@ import logging + + import jsonschema as js + +-from libnmstate.ifaces.ovs import is_ovs_running + from libnmstate.schema import Interface + from libnmstate.schema import InterfaceType + from libnmstate.error import NmstateDependencyError +@@ -50,7 +49,6 @@ def validate_interface_capabilities(ifaces_state, capabilities): + ifaces_types = {iface_state.get("type") for iface_state in ifaces_state} + has_ovs_capability = NmstatePlugin.OVS_CAPABILITY in capabilities + has_team_capability = NmstatePlugin.TEAM_CAPABILITY in capabilities +- ovs_is_running = is_ovs_running() + for iface_type in ifaces_types: + is_ovs_type = iface_type in ( + InterfaceType.OVS_BRIDGE, +@@ -58,18 +56,12 @@ def validate_interface_capabilities(ifaces_state, capabilities): + InterfaceType.OVS_PORT, + ) + if is_ovs_type and not has_ovs_capability: +- if not ovs_is_running: +- raise NmstateDependencyError( +- "openvswitch service is not started." +- ) +- else: +- raise NmstateDependencyError( +- "Open vSwitch NetworkManager support not installed " +- "and started" +- ) ++ raise NmstateDependencyError( ++ "Open vSwitch support not properly installed or started" ++ ) + elif iface_type == InterfaceType.TEAM and not has_team_capability: + raise NmstateDependencyError( +- "NetworkManager-team plugin not installed and started" ++ "Team support not properly installed or started" + ) + + +-- +2.31.1 + diff --git a/SOURCES/BZ_1964440-nm-ipv4-Deactivate-profile-when-route-removed.patch b/SOURCES/BZ_1964440-nm-ipv4-Deactivate-profile-when-route-removed.patch new file mode 100644 index 0000000..1ffe314 --- /dev/null +++ b/SOURCES/BZ_1964440-nm-ipv4-Deactivate-profile-when-route-removed.patch @@ -0,0 +1,86 @@ +From af8199135907d300014b5052571ca3e445455af7 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 25 May 2021 16:50:06 +0800 +Subject: [PATCH 2/2] nm ipv4: Deactivate profile when route removed. + +The bug https://bugzilla.redhat.com/show_bug.cgi?id=1962551 has shown +IPv4 route also share the same problem. + +Whenever we got a route removal, we deactivate that profile via +NetworkManager. + +Integration test case included. + +Signed-off-by: Gris Ge +Signed-off-by: Fernando Fernandez Mancera +--- + libnmstate/nm/profile.py | 5 +++-- + libnmstate/route.py | 19 +++++++---------- + tests/integration/route_test.py | 37 ++++++++++++++++++++++++++++++--- + 3 files changed, 45 insertions(+), 16 deletions(-) + +diff --git a/libnmstate/nm/profile.py b/libnmstate/nm/profile.py +index b655885..6b20b21 100644 +--- a/libnmstate/nm/profile.py ++++ b/libnmstate/nm/profile.py +@@ -49,7 +49,7 @@ from .translator import Api2Nm + IMPORT_NM_DEV_TIMEOUT = 5 + IMPORT_NM_DEV_RETRY_INTERNAL = 0.5 + FALLBACK_CHECKER_INTERNAL = 15 +-IPV6_ROUTE_REMOVED = "_ipv6_route_removed" ++ROUTE_REMOVED = "_route_removed" + + + class NmProfile: +@@ -193,9 +193,10 @@ class NmProfile: + elif self._iface.is_virtual and self._nm_dev: + self._add_action(NmProfile.ACTION_DELETE_DEVICE) + +- if self._iface.raw.get(IPV6_ROUTE_REMOVED): ++ if self._iface.raw.get(ROUTE_REMOVED): + # This is a workaround for NM bug: + # https://bugzilla.redhat.com/1837254 ++ # https://bugzilla.redhat.com/1962551 + self._add_action(NmProfile.ACTION_DEACTIVATE_FIRST) + + if self._iface.is_controller and self._iface.is_up: +diff --git a/libnmstate/route.py b/libnmstate/route.py +index cdea198..d373427 100644 +--- a/libnmstate/route.py ++++ b/libnmstate/route.py +@@ -36,7 +36,7 @@ from .state import StateEntry + DEFAULT_ROUTE_TABLE = 254 + + +-IPV6_ROUTE_REMOVED = "_ipv6_route_removed" ++ROUTE_REMOVED = "_route_removed" + + + class RouteEntry(StateEntry): +@@ -235,16 +235,13 @@ class RouteState: + for route in route_set: + if not rt.match(route): + new_routes.add(route) +- if route.is_ipv6: +- # The routes match and therefore it is being removed. +- # Nmstate will check if it is an IPv6 route and if so, +- # marking the interface as deactivate first. +- # +- # This is a workaround for NM bug: +- # https://bugzilla.redhat.com/1837254 +- ifaces.all_kernel_ifaces[iface_name].raw[ +- IPV6_ROUTE_REMOVED +- ] = True ++ # The routes match and therefore it is being removed. ++ # marking the interface as deactivate first. ++ # ++ # This is a workaround for NM bug: ++ # https://bugzilla.redhat.com/1837254 ++ # https://bugzilla.redhat.com/1962551 ++ ifaces.all_kernel_ifaces[iface_name].raw[ROUTE_REMOVED] = True + if new_routes != route_set: + self._routes[iface_name] = new_routes + +-- +2.31.1 + diff --git a/SOURCES/BZ_1966379_fix_bond_opt_tlb_dynamic_lb.patch b/SOURCES/BZ_1966379_fix_bond_opt_tlb_dynamic_lb.patch new file mode 100644 index 0000000..1d70d68 --- /dev/null +++ b/SOURCES/BZ_1966379_fix_bond_opt_tlb_dynamic_lb.patch @@ -0,0 +1,74 @@ +From e503eb8241dda600ef16741c29cab83443ae0528 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 1 Jun 2021 10:13:33 +0800 +Subject: [PATCH 1/2] nm bond: Fixing `tlb_dynamic_lb` option + +NM only takes 1 or 0 for True/False, currently only "use_carrier" +and "tlb_dynamic_lb" are boolean. + +Integration test case included. NM 1.31 is required for bug +https://bugzilla.redhat.com/show_bug.cgi?id=1959934 + +Signed-off-by: Gris Ge +--- + libnmstate/nm/bond.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libnmstate/nm/bond.py b/libnmstate/nm/bond.py +index cebac4d..067d87f 100644 +--- a/libnmstate/nm/bond.py ++++ b/libnmstate/nm/bond.py +@@ -92,7 +92,7 @@ def _nm_fix_bond_options(option_name, option_value): + option_name, option_value + ) + ) +- elif option_name == "use_carrier": ++ elif option_name in ("use_carrier", "tlb_dynamic_lb"): + option_value = 1 if option_value else 0 + + return str(option_value) +-- +2.31.1 + + +From 1d6c7715dfbcb5021016bcd7b4f1f9fa357b2f20 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 1 Jun 2021 10:41:57 +0800 +Subject: [PATCH 2/2] nm bond: Fix preserving `all_slaves_active` option + +When `all_slaves_active` was previously set via nmstate or NM, follow up +bond modification will fail with: + + NmstateNotImplementedError: Unsupported bond option: 'all_slaves_active'='0' + +This is because the option returned by `_get_bond_options_from_profiles()` is +not canonicalized. + +Expand the check to cover `1` and `0`. + +Integration test case included. + +Signed-off-by: Gris Ge +--- + libnmstate/nm/bond.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libnmstate/nm/bond.py b/libnmstate/nm/bond.py +index 067d87f..1642319 100644 +--- a/libnmstate/nm/bond.py ++++ b/libnmstate/nm/bond.py +@@ -82,9 +82,9 @@ def create_setting(iface, wired_setting, base_con_profile): + + def _nm_fix_bond_options(option_name, option_value): + if option_name == "all_slaves_active": +- if option_value == "delivered": ++ if option_value in ("delivered", "1"): + option_value = 1 +- elif option_value == "dropped": ++ elif option_value in ("dropped", "0"): + option_value = 0 + else: + raise NmstateNotImplementedError( +-- +2.31.1 + diff --git a/SOURCES/BZ_1966457_Fix_bond_fail_over_mac.patch b/SOURCES/BZ_1966457_Fix_bond_fail_over_mac.patch new file mode 100644 index 0000000..c72cc8f --- /dev/null +++ b/SOURCES/BZ_1966457_Fix_bond_fail_over_mac.patch @@ -0,0 +1,35 @@ +From b1e94d1aa04f51b2d15711c8e7ab37198b173065 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 1 Jun 2021 16:33:15 +0800 +Subject: [PATCH] bond: Fix bond fail_over_mac=active + +With bond interface in fail_over_mac=active and active-backup mode, +any future change via nmstate will fail as nmstate is validating +on current state instead of desire state for this bond mac restriction. + +Fixed the code to only validate bond mac restriction on desired or +changed bond interface. + +Integration test case include. + +Signed-off-by: Gris Ge +--- + libnmstate/ifaces/bond.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libnmstate/ifaces/bond.py b/libnmstate/ifaces/bond.py +index 138386e..01859b0 100644 +--- a/libnmstate/ifaces/bond.py ++++ b/libnmstate/ifaces/bond.py +@@ -88,7 +88,7 @@ class BondIface(BaseIface): + + def pre_edit_validation_and_cleanup(self): + super().pre_edit_validation_and_cleanup() +- if self.is_up: ++ if self.is_up and (self.is_desired or self.is_changed): + self._discard_bond_option_when_mode_change() + self._validate_bond_mode() + self._fix_mac_restriced_mode() +-- +2.31.1 + diff --git a/SOURCES/BZ_1966457_only_validate_desired_bond.patch b/SOURCES/BZ_1966457_only_validate_desired_bond.patch new file mode 100644 index 0000000..fd4a581 --- /dev/null +++ b/SOURCES/BZ_1966457_only_validate_desired_bond.patch @@ -0,0 +1,32 @@ +From 355bc8f052e35084405343a0b94ccf06007c31f3 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Wed, 2 Jun 2021 19:53:25 +0800 +Subject: [PATCH] bond: Don't validate current bond status + +If a bond interface is only marked as changed due to other +interface(like bridge port list change), its original desire information +is fully read from current status, there is no need to validate it. + +The fix is only validate on desired bond interface. + +Signed-off-by: Gris Ge +--- + libnmstate/ifaces/bond.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libnmstate/ifaces/bond.py b/libnmstate/ifaces/bond.py +index 01859b0..6311f5f 100644 +--- a/libnmstate/ifaces/bond.py ++++ b/libnmstate/ifaces/bond.py +@@ -88,7 +88,7 @@ class BondIface(BaseIface): + + def pre_edit_validation_and_cleanup(self): + super().pre_edit_validation_and_cleanup() +- if self.is_up and (self.is_desired or self.is_changed): ++ if self.is_up and self.is_desired: + self._discard_bond_option_when_mode_change() + self._validate_bond_mode() + self._fix_mac_restriced_mode() +-- +2.31.1 + diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec index 06262d2..65ff75a 100644 --- a/SPECS/nmstate.spec +++ b/SPECS/nmstate.spec @@ -4,7 +4,7 @@ Name: nmstate Version: 1.0.2 -Release: 5%{?dist} +Release: 11%{?dist} Summary: Declarative network manager API License: LGPLv2+ URL: https://github.com/%{srcname}/%{srcname} @@ -14,6 +14,13 @@ 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 +Patch4: BZ_1961914-do-not-use-unmanaged-interface-for-dns.patch +Patch5: BZ_1961912-fix-ovs-interface-activation.patch +Patch6: BZ_1964439-ovs-Fix-is_ovs_running-in-container-environment.patch +Patch7: BZ_1964440-nm-ipv4-Deactivate-profile-when-route-removed.patch +Patch8: BZ_1966379_fix_bond_opt_tlb_dynamic_lb.patch +Patch9: BZ_1966457_Fix_bond_fail_over_mac.patch +Patch10: BZ_1966457_only_validate_desired_bond.patch BuildArch: noarch BuildRequires: python3-devel BuildRequires: python3-setuptools @@ -86,6 +93,24 @@ gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0} %{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_ovsdb* %changelog +* Thu Jun 03 2021 Gris Ge - 1.0.2-11 +- Only validate desired bone interface. RHBZ#1966457 + +* Tue Jun 01 2021 Gris Ge - 1.0.2-10 +- Fix bond option fail_over_mac=active. RHBZ#1966457 + +* Tue Jun 01 2021 Gris Ge - 1.0.2-9 +- Fix bond option tlb_dynamic_lb. RHBZ#1966379 + +* Mon May 31 2021 Fernando Fernandez Mancera - 1.0.2-8 +- Remove test changes from patches. + +* Mon May 31 2021 Fernando Fernandez Mancera - 1.0.2-7 +- Fix OVS bugs and route removal bug: RHBZ#1964440 RHBZ#1964439 + +* Wed May 19 2021 Gris Ge - 1.0.2-6 +- Fix OVS bugs: RHBZ#1961912 RHBZ#1961913 RHBZ#1961914 + * Wed Mar 03 2021 Fernando Fernandez Mancera - 1.0.2-5 - New patch for fixing unmanaged interfaces being managed. RHBZ#1932247