diff --git a/SOURCES/BZ_2128555-ip-allow-extra-IP-address-found-in-verification-stag.patch b/SOURCES/BZ_2128555-ip-allow-extra-IP-address-found-in-verification-stag.patch new file mode 100644 index 0000000..1707467 --- /dev/null +++ b/SOURCES/BZ_2128555-ip-allow-extra-IP-address-found-in-verification-stag.patch @@ -0,0 +1,79 @@ +From 316f4fc3333627bcd3aef44c4a469cd6c04360ef Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 20 Sep 2022 21:35:47 +0800 +Subject: [PATCH 2/2] ip: allow extra IP address found in verification stage + +When nmstate applying the network state, there might be another tool +changing the IP addresses of interface, which lead to verification error +as extra IP address found. This is valid use case in kubernetes-nmstate +where keepalived is trying to add VIP(192.168.111.4) to certain interface. + +To support that, we introduce `InterfaceIP.ALLOW_EXTRA_ADDRESS` with +default set to true, nmstate verification will ignore extra IP address +after applied. + +Considering this is a very corner case, and could make the life of of +OpenshiftSDN engineer easier, I would suggest we accept this breaker of +API behavior. + +It is hard to reproduce it in integration test case, hence only added +unit test cases. + +Signed-off-by: Gris Ge +--- + libnmstate/ifaces/base_iface.py | 20 ++++++ + libnmstate/schema.py | 1 + + tests/lib/ifaces/base_iface_test.py | 98 +++++++++++++++++++++++++++++ + tests/lib/testlib/constants.py | 2 + + 4 files changed, 121 insertions(+) + +diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py +index e1b45617..fb2b7bb6 100644 +--- a/libnmstate/ifaces/base_iface.py ++++ b/libnmstate/ifaces/base_iface.py +@@ -403,6 +403,10 @@ class BaseIface: + def match(self, other): + self_state = self.state_for_verify() + other_state = other.state_for_verify() ++ for family in (Interface.IPV4, Interface.IPV6): ++ apply_allow_extra_address( ++ self_state.get(family, {}), other_state.get(family, {}) ++ ) + return state_match(self_state, other_state) + + def state_for_verify(self): +@@ -537,3 +541,19 @@ def _convert_ovs_external_ids_values_to_string(iface_info): + ) + for key, value in external_ids.items(): + external_ids[key] = str(value) ++ ++ ++# When `ALLOW_EXTRA_ADDRESS:True`, we should remove extra IP address in ++# current. ++def apply_allow_extra_address(desire_ip_state, current_ip_state): ++ # By default, we allow extra IP found during verification stage in order ++ # to make the life of OpenshiftSDN easier for this corner case. ++ if desire_ip_state.get(InterfaceIP.ALLOW_EXTRA_ADDRESS, True): ++ desire_addresses = desire_ip_state.get(InterfaceIP.ADDRESS, []) ++ new_cur_addresses = [ ++ addr ++ for addr in current_ip_state.get(InterfaceIP.ADDRESS, []) ++ if addr in desire_addresses ++ ] ++ current_ip_state[InterfaceIP.ADDRESS] = new_cur_addresses ++ desire_ip_state.pop(InterfaceIP.ALLOW_EXTRA_ADDRESS, None) +diff --git a/libnmstate/schema.py b/libnmstate/schema.py +index 17daf8f1..76418bf0 100644 +--- a/libnmstate/schema.py ++++ b/libnmstate/schema.py +@@ -142,6 +142,7 @@ class InterfaceIP: + ADDRESS = "address" + ADDRESS_IP = "ip" + ADDRESS_PREFIX_LENGTH = "prefix-length" ++ ALLOW_EXTRA_ADDRESS = "allow-extra-address" + DHCP = "dhcp" + AUTO_DNS = "auto-dns" + AUTO_GATEWAY = "auto-gateway" +-- +2.37.3 + diff --git a/SOURCES/BZ_2139698-nm-sriov-Do-not-touch-SR-IOV-if-not-desired.patch b/SOURCES/BZ_2139698-nm-sriov-Do-not-touch-SR-IOV-if-not-desired.patch new file mode 100644 index 0000000..e6a4dae --- /dev/null +++ b/SOURCES/BZ_2139698-nm-sriov-Do-not-touch-SR-IOV-if-not-desired.patch @@ -0,0 +1,54 @@ +From 2786e426173ed4a930dca23e18756123fc9b0e3a Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Mon, 26 Sep 2022 14:42:28 +0800 +Subject: [PATCH 1/2] nm sriov: Do not touch SR-IOV if not desired + +We should not create SRIOV settings in NetworkManager if that is not +desired. + +Integration test case included. +Manual test been done on Mellanox MT27710(mlx5). + +Signed-off-by: Gris Ge +--- + libnmstate/nm/connection.py | 2 +- + libnmstate/nm/sriov.py | 8 ++-- + tests/integration/nm/sriov_test.py | 62 ++++++++++++++++++++++++++++++ + 3 files changed, 67 insertions(+), 5 deletions(-) + +diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py +index 9beb7d18..535179ef 100644 +--- a/libnmstate/nm/connection.py ++++ b/libnmstate/nm/connection.py +@@ -210,7 +210,7 @@ def create_new_nm_simple_conn(iface, nm_profile): + if vxlan_setting: + settings.append(vxlan_setting) + +- sriov_setting = create_sriov_setting(iface_info, nm_profile) ++ sriov_setting = create_sriov_setting(iface, nm_profile) + if sriov_setting: + settings.append(sriov_setting) + +diff --git a/libnmstate/nm/sriov.py b/libnmstate/nm/sriov.py +index 4aa73e86..74513cb7 100644 +--- a/libnmstate/nm/sriov.py ++++ b/libnmstate/nm/sriov.py +@@ -47,11 +47,11 @@ SRIOV_NMSTATE_TO_NM_MAP = { + } + + +-def create_setting(iface_state, base_con_profile): ++def create_setting(iface, base_con_profile): + sriov_setting = None +- sriov_config = iface_state.get(Ethernet.CONFIG_SUBTREE, {}).get( +- Ethernet.SRIOV_SUBTREE +- ) ++ sriov_config = iface.original_desire_dict.get( ++ Ethernet.CONFIG_SUBTREE, {} ++ ).get(Ethernet.SRIOV_SUBTREE) + + if base_con_profile: + sriov_setting = base_con_profile.get_setting_by_name( +-- +2.37.3 + diff --git a/SOURCES/BZ_2149048-ip-Preserve-the-IP-address-order-when-applying.patch b/SOURCES/BZ_2149048-ip-Preserve-the-IP-address-order-when-applying.patch new file mode 100644 index 0000000..e6cf71f --- /dev/null +++ b/SOURCES/BZ_2149048-ip-Preserve-the-IP-address-order-when-applying.patch @@ -0,0 +1,64 @@ +From 2d0cfd5ad8e049f30cad10d977a5fae8bc4e6b64 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Thu, 10 Nov 2022 15:51:25 +0800 +Subject: [PATCH] ip: Preserve the IP address order when applying + +When applying the IP address, we should preserve the order for use case +whether user is expecting non-first ones been set with `secondary` flag. + +In RHEL/CentOS 8, NetworkManager is using reverted IPv6 address +according to +https://bugzilla.redhat.com/show_bug.cgi?id=2139443 + +Hence downstream nmstate will ship additional patch to fix it. +The upstream nmstate will not revert the IPv6 address list before +sending to NM. + +The downstream build of RHEL 8 has different behaviour than copr build +from git main branch. It is hard to tell whether we are using downstream +build or git build at runtime, hence we ship the +`test_preserve_ipv6_addresses_order` test in RHEL 8. + +Integration test case included. + +Signed-off-by: Gris Ge +--- + libnmstate/ifaces/base_iface.py | 4 +- + tests/integration/static_ip_address_test.py | 67 ++++++++++++++++++++- + tests/integration/testlib/env.py | 5 ++ + tests/integration/testlib/iproutelib.py | 14 +++++ + tests/lib/ifaces/ip_state_test.py | 2 + + 5 files changed, 89 insertions(+), 3 deletions(-) + +diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py +index fb2b7bb6..f29f9ac9 100644 +--- a/libnmstate/ifaces/base_iface.py ++++ b/libnmstate/ifaces/base_iface.py +@@ -47,7 +47,6 @@ class IPState: + self._family = family + self._info = info + self._remove_stack_if_disabled() +- self._sort_addresses() + self._canonicalize_ip_addr() + self._canonicalize_dynamic() + +@@ -71,7 +70,7 @@ class IPState: + addr[InterfaceIP.ADDRESS_IP] + ) + +- def _sort_addresses(self): ++ def sort_addresses(self): + self.addresses.sort(key=itemgetter(InterfaceIP.ADDRESS_IP)) + + def _remove_stack_if_disabled(self): +@@ -431,6 +430,7 @@ class BaseIface: + self.sort_port() + for family in (Interface.IPV4, Interface.IPV6): + ip_state = self.ip_state(family) ++ ip_state.sort_addresses() + ip_state.remove_link_local_address() + self._info[family] = ip_state.to_dict() + state = self.to_dict() +-- +2.38.1 + diff --git a/SOURCES/BZ_2149048-nm-revert-IPv6-order-before-adding-them-to-setting.patch b/SOURCES/BZ_2149048-nm-revert-IPv6-order-before-adding-them-to-setting.patch new file mode 100644 index 0000000..6b66195 --- /dev/null +++ b/SOURCES/BZ_2149048-nm-revert-IPv6-order-before-adding-them-to-setting.patch @@ -0,0 +1,31 @@ +From 08bf57af942e31a30f7f2c99c6238a3b662cc450 Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Tue, 29 Nov 2022 22:53:53 +0100 +Subject: [PATCH] nm: revert IPv6 order before adding them to setting + +This is a downstream patch that needs to be applied before any other +patch. Please check: + +https://github.com/nmstate/nmstate/commit/2d0cfd5ad8e049f30cad10d977a5fae8bc4e6b64 + +Signed-off-by: Fernando Fernandez Mancera +--- + libnmstate/nm/ipv6.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libnmstate/nm/ipv6.py b/libnmstate/nm/ipv6.py +index 71a80823..27ecf150 100644 +--- a/libnmstate/nm/ipv6.py ++++ b/libnmstate/nm/ipv6.py +@@ -174,7 +174,7 @@ def _set_dynamic(setting_ip, is_dhcp, is_autoconf): + + + def _set_static(setting_ip, ip_addresses): +- for address in ip_addresses: ++ for address in reversed(ip_addresses): + if iplib.is_ipv6_link_local_addr( + address[InterfaceIPv6.ADDRESS_IP], + address[InterfaceIPv6.ADDRESS_PREFIX_LENGTH], +-- +2.38.1 + diff --git a/SOURCES/BZ_2150705-nm-fix-activation-retry.patch b/SOURCES/BZ_2150705-nm-fix-activation-retry.patch new file mode 100644 index 0000000..b400490 --- /dev/null +++ b/SOURCES/BZ_2150705-nm-fix-activation-retry.patch @@ -0,0 +1,151 @@ +From 2a98b06c70c93c63298ac0cc5402a74d8015f40b Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 29 Nov 2022 12:57:19 +0800 +Subject: [PATCH 1/2] nm: Fix activation retry + +Using `time.sleep(5)` will not process the MainLoop of NM library which +will cause checkpoint expire during `time.sleep()`. + +Use Glib timer will fix this problem. + +Integration test case created to create 32 veth in single transaction, +the test case is marked as slow as it takes 10 seconds to finish. + +Signed-off-by: Gris Ge +--- + libnmstate/nm/active_connection.py | 38 +++++++++++++++++------------- + 1 file changed, 22 insertions(+), 16 deletions(-) + +diff --git a/libnmstate/nm/active_connection.py b/libnmstate/nm/active_connection.py +index 66e82aec..4c0ef9d5 100644 +--- a/libnmstate/nm/active_connection.py ++++ b/libnmstate/nm/active_connection.py +@@ -1,5 +1,5 @@ + # +-# Copyright (c) 2019-2020 Red Hat, Inc. ++# Copyright (c) 2019-2022 Red Hat, Inc. + # + # This file is part of nmstate + # +@@ -17,7 +17,6 @@ + # along with this program. If not, see . + # + +-import time + import logging + + from libnmstate.error import NmstateLibnmError +@@ -105,6 +104,19 @@ class ProfileActivation: + self._fallback_checker = None + self._fallback_checker_counter = 0 + ++ def _retry_activate(self, _user_data): ++ specific_object = None ++ retried = True ++ self._ctx.client.activate_connection_async( ++ self._nm_profile, ++ self._nm_dev, ++ specific_object, ++ self._ctx.cancellable, ++ self._activate_profile_callback, ++ retried, ++ ) ++ return GLib.SOURCE_REMOVE ++ + def run(self): + specific_object = None + self._action = ( +@@ -112,7 +124,7 @@ class ProfileActivation: + f"iface:{self._iface_name} type: {self._iface_type}" + ) + +- retry = True ++ retried = False + self._ctx.register_async(self._action) + self._ctx.client.activate_connection_async( + self._nm_profile, +@@ -120,7 +132,7 @@ class ProfileActivation: + specific_object, + self._ctx.cancellable, + self._activate_profile_callback, +- retry, ++ retried, + ) + self._fallback_checker = GLib.timeout_source_new( + FALLBACK_CHECKER_INTERNAL * 1000 +@@ -154,7 +166,7 @@ class ProfileActivation: + activation._fallback_checker.attach(ctx.context) + activation._wait_profile_activation() + +- def _activate_profile_callback(self, nm_client, result, retry): ++ def _activate_profile_callback(self, nm_client, result, retried): + nm_ac = None + if self._ctx.is_cancelled(): + self._activation_clean_up() +@@ -162,22 +174,16 @@ class ProfileActivation: + try: + nm_ac = nm_client.activate_connection_finish(result) + except GLib.Error as e: +- if retry: +- retry = False +- specific_object = None ++ if not retried: + logging.debug( + f"Action {self._action} failed, trying again in " + f"{ACTIVATION_RETRY_SLEEP} seconds." + ) +- time.sleep(ACTIVATION_RETRY_SLEEP) +- self._ctx.client.activate_connection_async( +- self._nm_profile, +- self._nm_dev, +- specific_object, +- self._ctx.cancellable, +- self._activate_profile_callback, +- retry, ++ activation_retry_timer = GLib.timeout_source_new( ++ ACTIVATION_RETRY_SLEEP * 1000 + ) ++ activation_retry_timer.set_callback(self._retry_activate, None) ++ activation_retry_timer.attach(self._ctx.context) + return + elif e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT): + logging.debug( +-- +2.38.1 + + +From 70c4a665aa6341c8bf22e2a91749bd8ae551b2b7 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 6 Dec 2022 16:12:21 +0800 +Subject: [PATCH 2/2] nm: Fix `time.sleep()` in `_import_current_device()` + +The `time.sleep()` in `_import_current_device()` will cause checkpoint +timeout as the `time.sleep()` does not iterate the NM Mainloop which +cause the checkpoint refresh not function as expected. + +Invoking a `NmContext.refresh()` after every small `time.sleep()` fixed +this problem. + +Extra test case not required, this problem only found on slow server(my +VM compiling rust project at the same time). + +Signed-off-by: Gris Ge +--- + libnmstate/nm/profile.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libnmstate/nm/profile.py b/libnmstate/nm/profile.py +index 82643d73..85c0e623 100644 +--- a/libnmstate/nm/profile.py ++++ b/libnmstate/nm/profile.py +@@ -501,6 +501,7 @@ class NmProfile: + break + else: + time.sleep(IMPORT_NM_DEV_RETRY_INTERNAL) ++ self._ctx.refresh() + + def import_current(self): + self._nm_dev = get_nm_dev( +-- +2.38.1 + diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec index 64e4c0c..a29ece7 100644 --- a/SPECS/nmstate.spec +++ b/SPECS/nmstate.spec @@ -4,7 +4,7 @@ Name: nmstate Version: 1.3.3 -Release: 1%{?dist} +Release: 4%{?dist} Summary: Declarative network manager API License: LGPLv2+ URL: https://github.com/%{srcname}/%{srcname} @@ -12,6 +12,12 @@ Source0: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.g Source1: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz.asc Source2: https://www.nmstate.io/nmstate.gpg Source3: %{url}/releases/download/v%{version}/%{srcname}-vendor-%{version}.tar.xz +Patch0: BZ_2149048-nm-revert-IPv6-order-before-adding-them-to-setting.patch +# Patches 0X are reserved for downstream only patches +Patch10: BZ_2139698-nm-sriov-Do-not-touch-SR-IOV-if-not-desired.patch +Patch11: BZ_2128555-ip-allow-extra-IP-address-found-in-verification-stag.patch +Patch12: BZ_2149048-ip-Preserve-the-IP-address-order-when-applying.patch +Patch13: BZ_2150705-nm-fix-activation-retry.patch BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: gnupg2 @@ -146,6 +152,16 @@ popd /sbin/ldconfig %changelog +* Tue Dec 06 2022 Gris Ge - 1.3.3-4 +- Fix activation retry. RHBZ#2150705 + +* Tue Nov 29 2022 Fernando Fernandez Mancera - 1.3.3-3 +- Revert IPv6 addresses order before adding them to IP setting. RHBZ#2149048 + +* Sat Nov 12 2022 Fernando Fernandez Mancera - 1.3.3-2 +- Do not remove SR-IOV that is not in desired state. RHBZ#2139698 +- Allow extra IP addresses found in verification stage. RHBZ#2128555 + * Mon Aug 15 2022 Gris Ge - 1.3.3-1 - Upgrade to nmstate-1.3.3