diff --git a/.gitignore b/.gitignore index 369f19d..c4e9e56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -SOURCES/nmstate-1.3.3.tar.gz -SOURCES/nmstate-vendor-1.3.3.tar.xz +SOURCES/nmstate-1.4.2.tar.gz +SOURCES/nmstate-vendor-1.4.2.tar.xz SOURCES/nmstate.gpg diff --git a/.nmstate.metadata b/.nmstate.metadata index 4bce02f..4736678 100644 --- a/.nmstate.metadata +++ b/.nmstate.metadata @@ -1,3 +1,3 @@ -b974ce77195e7ca1f2afb64628e666a3c7141e14 SOURCES/nmstate-1.3.3.tar.gz -79b68a9f6ba77aa670fafc6b29851b76df1b4c20 SOURCES/nmstate-vendor-1.3.3.tar.xz +165eba0069da41758442f1d2efa8cae180417882 SOURCES/nmstate-1.4.2.tar.gz +0815a374c7acca14db28b47347e43797d4b3d570 SOURCES/nmstate-vendor-1.4.2.tar.xz 5c1d9d65f9db4fedc9dc96e0fb6cac0a86749c88 SOURCES/nmstate.gpg 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 deleted file mode 100644 index 1707467..0000000 --- a/SOURCES/BZ_2128555-ip-allow-extra-IP-address-found-in-verification-stag.patch +++ /dev/null @@ -1,79 +0,0 @@ -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_2132570-nm-reverse-IPv6-order-before-adding-them-to-setting.patch b/SOURCES/BZ_2132570-nm-reverse-IPv6-order-before-adding-them-to-setting.patch new file mode 100644 index 0000000..9fea02b --- /dev/null +++ b/SOURCES/BZ_2132570-nm-reverse-IPv6-order-before-adding-them-to-setting.patch @@ -0,0 +1,31 @@ +From 248cd0bff6e3d030ee72b62a8a8b0e37e9f2ef80 Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Tue, 29 Nov 2022 23:56:13 +0100 +Subject: [PATCH] nm: reverse 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 8e01fd70..7eb3196c 100644 +--- a/libnmstate/nm/ipv6.py ++++ b/libnmstate/nm/ipv6.py +@@ -157,7 +157,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_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 deleted file mode 100644 index e6a4dae..0000000 --- a/SOURCES/BZ_2139698-nm-sriov-Do-not-touch-SR-IOV-if-not-desired.patch +++ /dev/null @@ -1,54 +0,0 @@ -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 deleted file mode 100644 index e6cf71f..0000000 --- a/SOURCES/BZ_2149048-ip-Preserve-the-IP-address-order-when-applying.patch +++ /dev/null @@ -1,64 +0,0 @@ -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 deleted file mode 100644 index 6b66195..0000000 --- a/SOURCES/BZ_2149048-nm-revert-IPv6-order-before-adding-them-to-setting.patch +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index b400490..0000000 --- a/SOURCES/BZ_2150705-nm-fix-activation-retry.patch +++ /dev/null @@ -1,151 +0,0 @@ -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/SOURCES/BZ_2160416-Ignore-error-when-creating-profile-if-not-desired.patch b/SOURCES/BZ_2160416-Ignore-error-when-creating-profile-if-not-desired.patch new file mode 100644 index 0000000..f7aa1d6 --- /dev/null +++ b/SOURCES/BZ_2160416-Ignore-error-when-creating-profile-if-not-desired.patch @@ -0,0 +1,66 @@ +From d7d732332e486cd8969ff4b5ef95a24cb68b5441 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Mon, 27 Feb 2023 12:17:05 +0800 +Subject: [PATCH] nm: Ignore error when creating profile if not desired + +When a undesired interface holding `autoconf: true` and `dhcp: false` +for IPv6, nmstate will fail with error: + + Autoconf without DHCP is not supported yet + +This is caused by `nm/connection.py` try to create `NM.SimpleConnection` +for every interface even not desired. + +This patch changed to: + * Only create new `NM.SimpleConnection` when desired or changed. + * Use current profile if exists when not desired or changed. + * Ignore error if not desired/changed. + +Integration test case included. + +Signed-off-by: Gris Ge +--- + libnmstate/nm/profile.py | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/libnmstate/nm/profile.py b/libnmstate/nm/profile.py +index ad1ad19f..1119cd1a 100644 +--- a/libnmstate/nm/profile.py ++++ b/libnmstate/nm/profile.py +@@ -24,6 +24,7 @@ from distutils.version import StrictVersion + import logging + import time + ++from libnmstate.error import NmstateError + from libnmstate.error import NmstateInternalError + from libnmstate.error import NmstateLibnmError + from libnmstate.error import NmstateNotSupportedError +@@ -321,9 +322,22 @@ class NmProfile: + # TODO: Use applied config as base profile + # Or even better remove the base profile argument as top level + # of nmstate should provide full/merged configure. +- self._nm_simple_conn = create_new_nm_simple_conn( +- self._iface, self._nm_profile +- ) ++ if self._iface.is_changed or self._iface.is_desired: ++ self._nm_simple_conn = create_new_nm_simple_conn( ++ self._iface, self._nm_profile ++ ) ++ elif self._nm_profile: ++ self._nm_simple_conn = NM.SimpleConnection.new_clone( ++ self._nm_profile ++ ) ++ else: ++ try: ++ self._nm_simple_conn = create_new_nm_simple_conn( ++ self._iface, self._nm_profile ++ ) ++ # No error for undesired interface ++ except NmstateError: ++ pass + + def save_config(self, save_to_disk): + self._check_sriov_support() +-- +2.39.2 + diff --git a/SOURCES/BZ_2160416-fix-SRIOV.patch b/SOURCES/BZ_2160416-fix-SRIOV.patch new file mode 100644 index 0000000..30d63d2 --- /dev/null +++ b/SOURCES/BZ_2160416-fix-SRIOV.patch @@ -0,0 +1,206 @@ +From d410b928c8f2a22d42d1974b62ab5b3164861184 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Thu, 23 Feb 2023 13:06:01 +0800 +Subject: [PATCH] nm: Fix error on SR-IOV + +When SR-IOV VF naming scheme is like `ens1f0v0`, nmstate will delete +the VF NM connection when applying this state: + +```yml +--- +interfaces: +- name: ens1f0 + type: ethernet + state: up + ethernet: + sr-iov: + total-vfs: 1 +- name: ens1f0v0 + type: ethernet + state: up + ipv4: + enabled: false + ipv6: + enabled: false +``` + +This is because `delete_other_profiles()` is checking +`self._nm_profile()` from active NM profile instead of newly created +one. The fix is using newly created profile `self._nm_simple_conn`. + +We also have race problem when activating PF along with VF, PF +activation might delete VF NIC which cause VF activation failed. To +workaround that, we activate PF first via `NmProfile.ACTION_SRIOV_PF` +and wait on it before start VF activation. + +Also problem found during SR-IOV investigations is we do extra +un-required modification to `NM.SettingOvsExternalIDs` even it is not +mentioned in desired. We skip overriding `NM.SettingOvsExternalIDs` when +not desired. + +Existing test case can cover the use cases. + +Signed-off-by: Gris Ge +--- + libnmstate/ifaces/ifaces.py | 18 +++++++++++++++++- + libnmstate/netapplier.py | 20 +++++++++++--------- + libnmstate/nm/connection.py | 2 +- + libnmstate/nm/profile.py | 12 ++++++++++-- + 4 files changed, 39 insertions(+), 13 deletions(-) + +diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py +index 828ff578..470dc0e6 100644 +--- a/libnmstate/ifaces/ifaces.py ++++ b/libnmstate/ifaces/ifaces.py +@@ -157,6 +157,23 @@ class Ifaces: + def has_vf_count_change_and_missing_eth(self): + return self._has_vf_count_change() and self._has_missing_veth() + ++ def has_sriov_iface(self): ++ for iface in self.all_kernel_ifaces.values(): ++ if (iface.is_desired or iface.is_changed) and iface.is_up: ++ cur_iface = self._cur_kernel_ifaces.get(iface.name) ++ if ( ++ cur_iface ++ and cur_iface.raw.get(Ethernet.CONFIG_SUBTREE, {}).get( ++ Ethernet.SRIOV_SUBTREE, {} ++ ) ++ ) or iface.original_desire_dict.get( ++ Ethernet.CONFIG_SUBTREE, {} ++ ).get( ++ Ethernet.SRIOV_SUBTREE, {} ++ ): ++ return True ++ return False ++ + def _has_vf_count_change(self): + for iface in self.all_kernel_ifaces.values(): + cur_iface = self._cur_kernel_ifaces.get(iface.name) +@@ -664,7 +681,6 @@ class Ifaces: + return None + + def get_cur_iface(self, iface_name, iface_type): +- + iface = self._cur_kernel_ifaces.get(iface_name) + if iface and iface_type in (None, InterfaceType.UNKNOWN, iface.type): + return iface +diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py +index ae909126..50a70a9c 100644 +--- a/libnmstate/netapplier.py ++++ b/libnmstate/netapplier.py +@@ -104,7 +104,7 @@ def apply( + pf_net_state, + verify_change, + save_to_disk, +- has_sriov_pf=True, ++ VERIFY_RETRY_COUNT_SRIOV, + ) + # Refresh the current state + current_state = show_with_plugins( +@@ -120,8 +120,16 @@ def apply( + current_state, + save_to_disk, + ) ++ ++ if net_state.ifaces.has_sriov_iface(): ++ # If SR-IOV is present, the verification timeout is being increased ++ # to avoid timeouts due to slow drivers like i40e. ++ verify_retry = VERIFY_RETRY_COUNT_SRIOV ++ else: ++ verify_retry = VERIFY_RETRY_COUNT ++ + _apply_ifaces_state( +- plugins, net_state, verify_change, save_to_disk, has_sriov_pf=False ++ plugins, net_state, verify_change, save_to_disk, verify_retry + ) + if commit: + destroy_checkpoints(plugins, checkpoints) +@@ -154,7 +162,7 @@ def rollback(*, checkpoint=None): + + + def _apply_ifaces_state( +- plugins, net_state, verify_change, save_to_disk, has_sriov_pf=False ++ plugins, net_state, verify_change, save_to_disk, verify_retry + ): + for plugin in plugins: + # Do not allow plugin to modify the net_state for future verification +@@ -163,12 +171,6 @@ def _apply_ifaces_state( + + verified = False + if verify_change: +- if has_sriov_pf: +- # If SR-IOV is present, the verification timeout is being increased +- # to avoid timeouts due to slow drivers like i40e. +- verify_retry = VERIFY_RETRY_COUNT_SRIOV +- else: +- verify_retry = VERIFY_RETRY_COUNT + for _ in range(verify_retry): + try: + _verify_change(plugins, net_state) +diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py +index 1fbb380b..6448e372 100644 +--- a/libnmstate/nm/connection.py ++++ b/libnmstate/nm/connection.py +@@ -240,7 +240,7 @@ def create_new_nm_simple_conn(iface, nm_profile): + InterfaceType.OVS_PORT, + ) + or iface.type == InterfaceType.OVS_BRIDGE +- ): ++ ) and OvsDB.OVS_DB_SUBTREE in iface.original_desire_dict: + nm_setting = create_ovsdb_external_ids_setting( + iface_info.get(OvsDB.OVS_DB_SUBTREE, {}) + ) +diff --git a/libnmstate/nm/profile.py b/libnmstate/nm/profile.py +index 53eaebed..ad1ad19f 100644 +--- a/libnmstate/nm/profile.py ++++ b/libnmstate/nm/profile.py +@@ -56,6 +56,7 @@ ROUTE_REMOVED = "_route_removed" + class NmProfile: + # For unmanged iface and desired to down + ACTION_ACTIVATE_FIRST = "activate_first" ++ ACTION_SRIOV_PF = "activate_sriov_pf" + ACTION_DEACTIVATE = "deactivate" + ACTION_DEACTIVATE_FIRST = "deactivate_first" + ACTION_DELETE_DEVICE = "delete_device" +@@ -77,6 +78,7 @@ class NmProfile: + ACTION_ACTIVATE_FIRST, + ACTION_DEACTIVATE_FIRST, + ACTION_TOP_CONTROLLER, ++ ACTION_SRIOV_PF, + ACTION_NEW_IFACES, + ACTION_OTHER_CONTROLLER, + ACTION_NEW_OVS_PORT, +@@ -181,6 +183,11 @@ class NmProfile: + else: + self._add_action(NmProfile.ACTION_NEW_IFACES) + else: ++ if ( ++ self._nm_dev.props.capabilities ++ & NM.DeviceCapabilities.SRIOV ++ ): ++ self._add_action(NmProfile.ACTION_SRIOV_PF) + if self._iface.type == InterfaceType.OVS_PORT: + self._add_action(NmProfile.ACTION_MODIFIED_OVS_PORT) + if self._iface.type == InterfaceType.OVS_INTERFACE: +@@ -462,6 +469,7 @@ class NmProfile: + + def do_action(self, action): + if action in ( ++ NmProfile.ACTION_SRIOV_PF, + NmProfile.ACTION_MODIFIED, + NmProfile.ACTION_MODIFIED_OVS_PORT, + NmProfile.ACTION_MODIFIED_OVS_IFACE, +@@ -559,8 +567,8 @@ class NmProfile: + or nm_profile.get_connection_type() == self._nm_iface_type + ) + and ( +- self._nm_profile is None +- or nm_profile.get_uuid() != self._nm_profile.get_uuid() ++ self._nm_simple_conn is None ++ or nm_profile.get_uuid() != self._nm_simple_conn.get_uuid() + ) + ): + ProfileDelete( +-- +2.39.2 + diff --git a/SOURCES/BZ_2169642-Fix-SRIOV-2.patch b/SOURCES/BZ_2169642-Fix-SRIOV-2.patch deleted file mode 100644 index 8f33243..0000000 --- a/SOURCES/BZ_2169642-Fix-SRIOV-2.patch +++ /dev/null @@ -1,206 +0,0 @@ -From 12e298f27f1ffa58f6f7e60016ff197719b7a26e Mon Sep 17 00:00:00 2001 -From: Gris Ge -Date: Thu, 23 Feb 2023 13:06:01 +0800 -Subject: [PATCH] nm: Fix error on SR-IOV - -When SR-IOV VF naming scheme is like `ens1f0v0`, nmstate will delete -the VF NM connection when applying this state: - -```yml ---- -interfaces: -- name: ens1f0 - type: ethernet - state: up - ethernet: - sr-iov: - total-vfs: 1 -- name: ens1f0v0 - type: ethernet - state: up - ipv4: - enabled: false - ipv6: - enabled: false -``` - -This is because `delete_other_profiles()` is checking -`self._nm_profile()` from active NM profile instead of newly created -one. The fix is using newly created profile `self._nm_simple_conn`. - -We also have race problem when activating PF along with VF, PF -activation might delete VF NIC which cause VF activation failed. To -workaround that, we activate PF first via `NmProfile.ACTION_SRIOV_PF` -and wait on it before start VF activation. - -Also problem found during SR-IOV investigations is we do extra -un-required modification to `NM.SettingOvsExternalIDs` even it is not -mentioned in desired. We skip overriding `NM.SettingOvsExternalIDs` when -not desired. - -Existing test case can cover the use cases. - -Signed-off-by: Gris Ge ---- - libnmstate/ifaces/ifaces.py | 18 +++++++++++++++++- - libnmstate/netapplier.py | 20 +++++++++++--------- - libnmstate/nm/connection.py | 2 +- - libnmstate/nm/profile.py | 12 ++++++++++-- - 4 files changed, 39 insertions(+), 13 deletions(-) - -diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py -index 828ff578..470dc0e6 100644 ---- a/libnmstate/ifaces/ifaces.py -+++ b/libnmstate/ifaces/ifaces.py -@@ -157,6 +157,23 @@ class Ifaces: - def has_vf_count_change_and_missing_eth(self): - return self._has_vf_count_change() and self._has_missing_veth() - -+ def has_sriov_iface(self): -+ for iface in self.all_kernel_ifaces.values(): -+ if (iface.is_desired or iface.is_changed) and iface.is_up: -+ cur_iface = self._cur_kernel_ifaces.get(iface.name) -+ if ( -+ cur_iface -+ and cur_iface.raw.get(Ethernet.CONFIG_SUBTREE, {}).get( -+ Ethernet.SRIOV_SUBTREE, {} -+ ) -+ ) or iface.original_desire_dict.get( -+ Ethernet.CONFIG_SUBTREE, {} -+ ).get( -+ Ethernet.SRIOV_SUBTREE, {} -+ ): -+ return True -+ return False -+ - def _has_vf_count_change(self): - for iface in self.all_kernel_ifaces.values(): - cur_iface = self._cur_kernel_ifaces.get(iface.name) -@@ -664,7 +681,6 @@ class Ifaces: - return None - - def get_cur_iface(self, iface_name, iface_type): -- - iface = self._cur_kernel_ifaces.get(iface_name) - if iface and iface_type in (None, InterfaceType.UNKNOWN, iface.type): - return iface -diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py -index ae909126..50a70a9c 100644 ---- a/libnmstate/netapplier.py -+++ b/libnmstate/netapplier.py -@@ -104,7 +104,7 @@ def apply( - pf_net_state, - verify_change, - save_to_disk, -- has_sriov_pf=True, -+ VERIFY_RETRY_COUNT_SRIOV, - ) - # Refresh the current state - current_state = show_with_plugins( -@@ -120,8 +120,16 @@ def apply( - current_state, - save_to_disk, - ) -+ -+ if net_state.ifaces.has_sriov_iface(): -+ # If SR-IOV is present, the verification timeout is being increased -+ # to avoid timeouts due to slow drivers like i40e. -+ verify_retry = VERIFY_RETRY_COUNT_SRIOV -+ else: -+ verify_retry = VERIFY_RETRY_COUNT -+ - _apply_ifaces_state( -- plugins, net_state, verify_change, save_to_disk, has_sriov_pf=False -+ plugins, net_state, verify_change, save_to_disk, verify_retry - ) - if commit: - destroy_checkpoints(plugins, checkpoints) -@@ -154,7 +162,7 @@ def rollback(*, checkpoint=None): - - - def _apply_ifaces_state( -- plugins, net_state, verify_change, save_to_disk, has_sriov_pf=False -+ plugins, net_state, verify_change, save_to_disk, verify_retry - ): - for plugin in plugins: - # Do not allow plugin to modify the net_state for future verification -@@ -163,12 +171,6 @@ def _apply_ifaces_state( - - verified = False - if verify_change: -- if has_sriov_pf: -- # If SR-IOV is present, the verification timeout is being increased -- # to avoid timeouts due to slow drivers like i40e. -- verify_retry = VERIFY_RETRY_COUNT_SRIOV -- else: -- verify_retry = VERIFY_RETRY_COUNT - for _ in range(verify_retry): - try: - _verify_change(plugins, net_state) -diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py -index 1fbb380b..6448e372 100644 ---- a/libnmstate/nm/connection.py -+++ b/libnmstate/nm/connection.py -@@ -240,7 +240,7 @@ def create_new_nm_simple_conn(iface, nm_profile): - InterfaceType.OVS_PORT, - ) - or iface.type == InterfaceType.OVS_BRIDGE -- ): -+ ) and OvsDB.OVS_DB_SUBTREE in iface.original_desire_dict: - nm_setting = create_ovsdb_external_ids_setting( - iface_info.get(OvsDB.OVS_DB_SUBTREE, {}) - ) -diff --git a/libnmstate/nm/profile.py b/libnmstate/nm/profile.py -index 53eaebed..ad1ad19f 100644 ---- a/libnmstate/nm/profile.py -+++ b/libnmstate/nm/profile.py -@@ -56,6 +56,7 @@ ROUTE_REMOVED = "_route_removed" - class NmProfile: - # For unmanged iface and desired to down - ACTION_ACTIVATE_FIRST = "activate_first" -+ ACTION_SRIOV_PF = "activate_sriov_pf" - ACTION_DEACTIVATE = "deactivate" - ACTION_DEACTIVATE_FIRST = "deactivate_first" - ACTION_DELETE_DEVICE = "delete_device" -@@ -77,6 +78,7 @@ class NmProfile: - ACTION_ACTIVATE_FIRST, - ACTION_DEACTIVATE_FIRST, - ACTION_TOP_CONTROLLER, -+ ACTION_SRIOV_PF, - ACTION_NEW_IFACES, - ACTION_OTHER_CONTROLLER, - ACTION_NEW_OVS_PORT, -@@ -181,6 +183,11 @@ class NmProfile: - else: - self._add_action(NmProfile.ACTION_NEW_IFACES) - else: -+ if ( -+ self._nm_dev.props.capabilities -+ & NM.DeviceCapabilities.SRIOV -+ ): -+ self._add_action(NmProfile.ACTION_SRIOV_PF) - if self._iface.type == InterfaceType.OVS_PORT: - self._add_action(NmProfile.ACTION_MODIFIED_OVS_PORT) - if self._iface.type == InterfaceType.OVS_INTERFACE: -@@ -462,6 +469,7 @@ class NmProfile: - - def do_action(self, action): - if action in ( -+ NmProfile.ACTION_SRIOV_PF, - NmProfile.ACTION_MODIFIED, - NmProfile.ACTION_MODIFIED_OVS_PORT, - NmProfile.ACTION_MODIFIED_OVS_IFACE, -@@ -559,8 +567,8 @@ class NmProfile: - or nm_profile.get_connection_type() == self._nm_iface_type - ) - and ( -- self._nm_profile is None -- or nm_profile.get_uuid() != self._nm_profile.get_uuid() -+ self._nm_simple_conn is None -+ or nm_profile.get_uuid() != self._nm_simple_conn.get_uuid() - ) - ): - ProfileDelete( --- -2.39.2 - diff --git a/SOURCES/BZ_2169642-Fix-sriov.patch b/SOURCES/BZ_2169642-Fix-sriov.patch deleted file mode 100644 index 990f1bf..0000000 --- a/SOURCES/BZ_2169642-Fix-sriov.patch +++ /dev/null @@ -1,1075 +0,0 @@ -diff -Nur nmstate-1.3.3.old/libnmstate/dns.py nmstate-1.3.3/libnmstate/dns.py ---- nmstate-1.3.3.old/libnmstate/dns.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/dns.py 2023-02-15 15:48:33.668708842 +0800 -@@ -144,6 +144,7 @@ - Find interface to store the DNS configurations in the order of: - * Any interface with static gateway - * Any interface configured as dynamic IP with 'auto-dns:False' -+ The loopback interface is ignored. - Return tuple: (ipv4_iface, ipv6_iface) - """ - ipv4_iface, ipv6_iface = self._find_ifaces_with_static_gateways( -@@ -168,6 +169,8 @@ - ipv4_iface = None - ipv6_iface = None - for iface_name, route_set in route_state.config_iface_routes.items(): -+ if iface_name == "lo": -+ continue - for route in route_set: - if ipv4_iface and ipv6_iface: - return (ipv4_iface, ipv6_iface) -diff -Nur nmstate-1.3.3.old/libnmstate/ifaces/base_iface.py nmstate-1.3.3/libnmstate/ifaces/base_iface.py ---- nmstate-1.3.3.old/libnmstate/ifaces/base_iface.py 2023-02-15 15:48:11.745846015 +0800 -+++ nmstate-1.3.3/libnmstate/ifaces/base_iface.py 2023-02-15 15:48:45.118768209 +0800 -@@ -61,6 +61,7 @@ - InterfaceIP.AUTO_ROUTES, - InterfaceIP.AUTO_GATEWAY, - InterfaceIP.AUTO_DNS, -+ InterfaceIP.AUTO_ROUTE_METRIC, - ): - self._info.pop(dhcp_option, None) - -@@ -123,7 +124,6 @@ - - - class BaseIface: -- CONTROLLER_METADATA = "_controller" - CONTROLLER_TYPE_METADATA = "_controller_type" - DNS_METADATA = "_dns" - ROUTES_METADATA = "_routes" -@@ -253,7 +253,6 @@ - self.ip_state(family).validate( - IPState(family, self._origin_info.get(family, {})) - ) -- self._validate_port_ip() - ip_state = self.ip_state(family) - ip_state.remove_link_local_address() - self._info[family] = ip_state.to_dict() -@@ -302,7 +301,7 @@ - if current_external_ids: - other._info[OvsDB.OVS_DB_SUBTREE].pop(OvsDB.EXTERNAL_IDS) - -- def _validate_port_ip(self): -+ def validate_port_ip(self): - for family in (Interface.IPV4, Interface.IPV6): - ip_state = IPState(family, self._origin_info.get(family, {})) - if ( -@@ -356,10 +355,11 @@ - return False - - def set_controller(self, controller_iface_name, controller_type): -- self._info[BaseIface.CONTROLLER_METADATA] = controller_iface_name -+ self._info[Interface.CONTROLLER] = controller_iface_name - self._info[BaseIface.CONTROLLER_TYPE_METADATA] = controller_type - if ( -- not self.can_have_ip_as_port -+ controller_iface_name -+ and not self.can_have_ip_as_port - and controller_type != InterfaceType.VRF - ): - for family in (Interface.IPV4, Interface.IPV6): -@@ -367,7 +367,7 @@ - - @property - def controller(self): -- return self._info.get(BaseIface.CONTROLLER_METADATA) -+ return self._info.get(Interface.CONTROLLER) - - @property - def controller_type(self): -@@ -378,7 +378,8 @@ - for port_name in self.port: - port_iface = ifaces.all_kernel_ifaces.get(port_name) - if port_iface: -- port_iface.set_controller(self.name, self.type) -+ if port_iface.controller != "": -+ port_iface.set_controller(self.name, self.type) - - def update(self, info): - self._info.update(info) -@@ -444,6 +445,8 @@ - state[Interface.STATE] = InterfaceState.DOWN - _convert_ovs_external_ids_values_to_string(state) - state.pop(BaseIface.PERMANENT_MAC_ADDRESS_METADATA, None) -+ if self.controller == "": -+ state.pop(Interface.CONTROLLER, None) - - return state - -diff -Nur nmstate-1.3.3.old/libnmstate/ifaces/ethtool.py nmstate-1.3.3/libnmstate/ifaces/ethtool.py ---- nmstate-1.3.3.old/libnmstate/ifaces/ethtool.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/ifaces/ethtool.py 2023-02-15 15:48:33.672042194 +0800 -@@ -176,6 +176,7 @@ - "rx-ntuple-filter", - "rx-vlan-hw-parse", - "tx-vlan-hw-insert", -+ "highdma", - } - - def __init__(self, feature_info): -diff -Nur nmstate-1.3.3.old/libnmstate/ifaces/ifaces.py nmstate-1.3.3/libnmstate/ifaces/ifaces.py ---- nmstate-1.3.3.old/libnmstate/ifaces/ifaces.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/ifaces/ifaces.py 2023-02-15 15:48:45.118768209 +0800 -@@ -1,22 +1,6 @@ --# --# Copyright (c) 2020-2021 Red Hat, Inc. --# --# This file is part of nmstate --# --# This program is free software: you can redistribute it and/or modify --# it under the terms of the GNU Lesser General Public License as published by --# the Free Software Foundation, either version 2.1 of the License, or --# (at your option) any later version. --# --# This program is distributed in the hope that it will be useful, --# but WITHOUT ANY WARRANTY; without even the implied warranty of --# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --# GNU Lesser General Public License for more details. --# --# You should have received a copy of the GNU Lesser General Public License --# along with this program. If not, see . --# -+# SPDX-License-Identifier: LGPL-2.1-or-later - -+from copy import deepcopy - import logging - - from libnmstate.error import NmstateKernelIntegerRoundedError -@@ -24,6 +8,7 @@ - from libnmstate.error import NmstateVerificationError - from libnmstate.prettystate import format_desired_current_state_diff - from libnmstate.schema import BondMode -+from libnmstate.schema import Ethernet - from libnmstate.schema import Interface - from libnmstate.schema import InterfaceType - from libnmstate.schema import InterfaceState -@@ -97,18 +82,20 @@ - self._cur_user_space_ifaces = _UserSpaceIfaces() - if cur_iface_infos: - for iface_info in cur_iface_infos: -- cur_iface = _to_specific_iface_obj(iface_info, save_to_disk) -+ cur_iface = _to_specific_iface_obj( -+ deepcopy(iface_info), save_to_disk -+ ) - if cur_iface.is_user_space_only: -- self._user_space_ifaces.set(cur_iface) -+ self._user_space_ifaces.set(deepcopy(cur_iface)) - self._cur_user_space_ifaces.set(cur_iface) - else: -- self._kernel_ifaces[cur_iface.name] = cur_iface -+ self._kernel_ifaces[cur_iface.name] = deepcopy(cur_iface) - self._cur_kernel_ifaces[cur_iface.name] = cur_iface - - if des_iface_infos: - for iface_info in des_iface_infos: - iface = BaseIface(iface_info, save_to_disk) -- if not iface.is_up and self._gen_conf_mode: -+ if not (iface.is_up or iface.is_down) and self._gen_conf_mode: - continue - if iface.type == InterfaceType.UNKNOWN: - cur_ifaces = self._get_cur_ifaces(iface.name) -@@ -157,6 +144,7 @@ - self._validate_infiniband_as_bridge_port() - self._validate_infiniband_as_bond_port() - self._apply_copy_mac_from() -+ self._validate_controller_and_port_list_conflict() - self.gen_metadata() - for iface in self.all_ifaces(): - if iface.is_desired and iface.is_up: -@@ -164,6 +152,67 @@ - - self._pre_edit_validation_and_cleanup() - -+ # Return True when SR-IOV `total-vfs` changed and having interface not -+ # exists in current. -+ def has_vf_count_change_and_missing_eth(self): -+ return self._has_vf_count_change() and self._has_missing_veth() -+ -+ def _has_vf_count_change(self): -+ for iface in self.all_kernel_ifaces.values(): -+ cur_iface = self._cur_kernel_ifaces.get(iface.name) -+ if ( -+ cur_iface -+ and iface.is_desired -+ and iface.is_up -+ and iface.type == InterfaceType.ETHERNET -+ ): -+ des_vf_count = ( -+ iface.original_desire_dict.get(Ethernet.CONFIG_SUBTREE, {}) -+ .get(Ethernet.SRIOV_SUBTREE, {}) -+ .get(Ethernet.SRIOV.TOTAL_VFS, 0) -+ ) -+ cur_vf_count = ( -+ cur_iface.raw.get(Ethernet.CONFIG_SUBTREE, {}) -+ .get(Ethernet.SRIOV_SUBTREE, {}) -+ .get(Ethernet.SRIOV.TOTAL_VFS, 0) -+ ) -+ if des_vf_count != cur_vf_count: -+ return True -+ return False -+ -+ def _has_missing_veth(self): -+ for iface in self.all_kernel_ifaces.values(): -+ cur_iface = self._cur_kernel_ifaces.get(iface.name) -+ if cur_iface is None and iface.type == InterfaceType.ETHERNET: -+ return True -+ return False -+ -+ # Return list of cloned iface_info(dictionary) which SRIOV PF conf only. -+ def get_sriov_pf_ifaces(self): -+ sriov_ifaces = [] -+ for iface in self.all_kernel_ifaces.values(): -+ if ( -+ iface.is_desired -+ and iface.is_up -+ and iface.type == InterfaceType.ETHERNET -+ ): -+ sriov_conf = iface.original_desire_dict.get( -+ Ethernet.CONFIG_SUBTREE, {} -+ ).get(Ethernet.SRIOV_SUBTREE, {}) -+ if sriov_conf: -+ eth_conf = iface.original_desire_dict.get( -+ Ethernet.CONFIG_SUBTREE -+ ) -+ sriov_ifaces.append( -+ { -+ Interface.NAME: iface.name, -+ Interface.TYPE: InterfaceType.ETHERNET, -+ Interface.STATE: InterfaceState.UP, -+ Ethernet.CONFIG_SUBTREE: deepcopy(eth_conf), -+ } -+ ) -+ return sriov_ifaces -+ - @property - def _ignored_ifaces(self): - return [iface for iface in self.all_ifaces() if iface.is_ignore] -@@ -275,6 +324,13 @@ - self._validate_ovs_patch_peers() - self._remove_unknown_type_interfaces() - self._validate_veth_peers() -+ self._resolve_controller_type() -+ self._validate_port_ip() -+ -+ def _validate_port_ip(self): -+ for iface in self.all_ifaces(): -+ if iface.is_desired and iface.is_up: -+ iface.validate_port_ip() - - def _bring_port_up_if_not_in_desire(self): - """ -@@ -400,6 +456,72 @@ - f"{iface.name} is in {iface.bond_mode} mode." - ) - -+ def _validate_controller_and_port_list_conflict(self): -+ """ -+ Validate Check whether user defined both controller property and port -+ list of controller interface, examples of invalid desire state: -+ * eth1 has controller: br1, but br1 has no eth1 in port list -+ * eth2 has controller: br1, but br2 has eth2 in port list -+ * eth1 has controller: Some("") (detach), but br1 has eth1 in port -+ list -+ """ -+ self._validate_controller_not_in_port_list() -+ self._validate_controller_in_other_port_list() -+ -+ def _validate_controller_not_in_port_list(self): -+ for iface_name, iface in self._kernel_ifaces.items(): -+ if ( -+ not iface.is_up -+ or not iface.controller -+ or Interface.CONTROLLER not in iface.original_desire_dict -+ ): -+ continue -+ ctrl_iface = self._user_space_ifaces.get( -+ iface.controller, InterfaceType.OVS_BRIDGE -+ ) -+ if not ctrl_iface: -+ ctrl_iface = self._kernel_ifaces.get(iface.controller) -+ if ctrl_iface: -+ if not ctrl_iface.is_desired: -+ continue -+ if ctrl_iface.port and iface_name not in ctrl_iface.port: -+ raise NmstateValueError( -+ f"Interface {iface_name} desired controller " -+ f"is {iface.controller}, but not listed in port " -+ "list of controller interface" -+ ) -+ -+ def _validate_controller_in_other_port_list(self): -+ port_to_ctrl = {} -+ for iface in self.all_ifaces(): -+ if iface.is_controller and iface.is_desired and iface.is_up: -+ for port in iface.port: -+ port_to_ctrl[port] = iface.name -+ -+ for iface in self._kernel_ifaces.values(): -+ if ( -+ not iface.is_desired -+ or not iface.is_up -+ or iface.controller is None -+ or iface.name not in port_to_ctrl -+ or Interface.CONTROLLER not in iface.original_desire_dict -+ ): -+ continue -+ ctrl_name = port_to_ctrl.get(iface.name) -+ if ctrl_name != iface.controller: -+ if iface.controller: -+ raise NmstateValueError( -+ f"Interface {iface.name} has controller property set " -+ f"to {iface.controller}, but been listed as " -+ f"port of controller {ctrl_name} " -+ ) -+ else: -+ raise NmstateValueError( -+ f"Interface {iface.name} desired to detach controller " -+ "via controller property set to '', but " -+ f"still been listed as port of controller {ctrl_name}" -+ ) -+ - def _handle_controller_port_list_change(self): - """ - * Mark port interface as changed if controller removed. -@@ -419,6 +541,10 @@ - changed_port = (des_port | cur_port) - (des_port & cur_port) - for iface_name in changed_port: - self._kernel_ifaces[iface_name].mark_as_changed() -+ if iface_name not in des_port: -+ self._kernel_ifaces[iface_name].set_controller( -+ None, None -+ ) - if cur_iface: - for port_name in iface.config_changed_port(cur_iface): - if port_name in self._kernel_ifaces: -@@ -823,6 +949,24 @@ - if port_name in ignored_kernel_iface_names: - iface.remove_port(port_name) - -+ def _resolve_controller_type(self): -+ for iface in self._kernel_ifaces.values(): -+ if ( -+ iface.is_up -+ and iface.is_desired -+ and Interface.CONTROLLER in iface.original_desire_dict -+ and iface.controller -+ and iface.controller_type is None -+ ): -+ ctrl_iface = self._cur_user_space_ifaces.get( -+ iface.controller, InterfaceType.OVS_BRIDGE -+ ) -+ if ctrl_iface is None: -+ ctrl_iface = self._cur_kernel_ifaces.get(iface.controller) -+ -+ if ctrl_iface: -+ iface.set_controller(iface.controller, ctrl_iface.type) -+ - - def _to_specific_iface_obj(info, save_to_disk): - iface_type = info.get(Interface.TYPE, InterfaceType.UNKNOWN) -diff -Nur nmstate-1.3.3.old/libnmstate/ifaces/linux_bridge.py nmstate-1.3.3/libnmstate/ifaces/linux_bridge.py ---- nmstate-1.3.3.old/libnmstate/ifaces/linux_bridge.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/ifaces/linux_bridge.py 2023-02-15 15:48:33.672042194 +0800 -@@ -192,9 +192,9 @@ - # There is no good way to detect kernel HZ in user space. Hence - # we check whether certain value is rounded. - if cur_value != value: -- if value >= 8 * (10 ** 6): -+ if value >= 8 * (10**6): - if abs(value - cur_value) <= int( -- value / 8 * (10 ** 6) -+ value / 8 * (10**6) - ): - return key, value, cur_value - else: -diff -Nur nmstate-1.3.3.old/libnmstate/netapplier.py nmstate-1.3.3/libnmstate/netapplier.py ---- nmstate-1.3.3.old/libnmstate/netapplier.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/netapplier.py 2023-02-15 15:48:45.118768209 +0800 -@@ -24,7 +24,7 @@ - - from libnmstate import validator - from libnmstate.error import NmstateVerificationError --from libnmstate.schema import InterfaceType -+from libnmstate.schema import Interface - - from .net_state import NetState - from .nmstate import create_checkpoints -@@ -73,20 +73,56 @@ - - desired_state = copy.deepcopy(desired_state) - remove_the_reserved_secrets(desired_state) -+ - with plugin_context() as plugins: - validator.schema_validate(desired_state) - current_state = show_with_plugins( - plugins, include_status_data=True, include_secrets=True - ) - validator.validate_capabilities( -- desired_state, plugins_capabilities(plugins) -+ copy.deepcopy(desired_state), plugins_capabilities(plugins) - ) - 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) -+ # When we have VF count changes and missing eth, it might be user -+ # referring future VF in the same desire state, we just apply -+ # VF changes state only first. -+ if net_state.ifaces.has_vf_count_change_and_missing_eth(): -+ sriov_ifaces = net_state.ifaces.get_sriov_pf_ifaces() -+ if sriov_ifaces: -+ pf_net_state = NetState( -+ {Interface.KEY: sriov_ifaces}, -+ ignored_ifnames, -+ current_state, -+ save_to_disk, -+ ) -+ _apply_ifaces_state( -+ plugins, -+ pf_net_state, -+ verify_change, -+ save_to_disk, -+ has_sriov_pf=True, -+ ) -+ # Refresh the current state -+ current_state = show_with_plugins( -+ plugins, include_status_data=True, include_secrets=True -+ ) -+ validator.validate_capabilities( -+ desired_state, plugins_capabilities(plugins) -+ ) -+ ignored_ifnames = _get_ignored_interface_names(plugins) -+ net_state = NetState( -+ copy.deepcopy(desired_state), -+ ignored_ifnames, -+ current_state, -+ save_to_disk, -+ ) -+ _apply_ifaces_state( -+ plugins, net_state, verify_change, save_to_disk, has_sriov_pf=False -+ ) - if commit: - destroy_checkpoints(plugins, checkpoints) - else: -@@ -117,13 +153,17 @@ - rollback_checkpoints(plugins, checkpoint) - - --def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk): -+def _apply_ifaces_state( -+ plugins, net_state, verify_change, save_to_disk, has_sriov_pf=False -+): - for plugin in plugins: -- plugin.apply_changes(net_state, save_to_disk) -+ # Do not allow plugin to modify the net_state for future verification -+ tmp_net_state = copy.deepcopy(net_state) -+ plugin.apply_changes(tmp_net_state, save_to_disk) - - verified = False - if verify_change: -- if _net_state_contains_sriov_interface(net_state): -+ if has_sriov_pf: - # If SR-IOV is present, the verification timeout is being increased - # to avoid timeouts due to slow drivers like i40e. - verify_retry = VERIFY_RETRY_COUNT_SRIOV -@@ -140,14 +180,6 @@ - _verify_change(plugins, net_state) - - --def _net_state_contains_sriov_interface(net_state): -- for iface in net_state.ifaces.all_kernel_ifaces.values(): -- if iface.type == InterfaceType.ETHERNET and iface.is_sriov: -- return True -- -- return False -- -- - def _verify_change(plugins, net_state): - current_state = remove_metadata_leftover( - show_with_plugins(plugins, include_secrets=True) -diff -Nur nmstate-1.3.3.old/libnmstate/netinfo.py nmstate-1.3.3/libnmstate/netinfo.py ---- nmstate-1.3.3.old/libnmstate/netinfo.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/netinfo.py 2023-02-15 15:48:33.672042194 +0800 -@@ -39,6 +39,7 @@ - plugins, - include_status_data=include_status_data, - include_secrets=include_secrets, -+ include_controller_prop=False, - ) - ) - -diff -Nur nmstate-1.3.3.old/libnmstate/nispor/base_iface.py nmstate-1.3.3/libnmstate/nispor/base_iface.py ---- nmstate-1.3.3.old/libnmstate/nispor/base_iface.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/nispor/base_iface.py 2023-02-15 15:48:33.672042194 +0800 -@@ -115,6 +115,8 @@ - if ethtool_info_dict: - iface_info[Ethtool.CONFIG_SUBTREE] = ethtool_info_dict - -+ if self.np_iface.controller: -+ iface_info[Interface.CONTROLLER] = self.np_iface.controller - return iface_info - - -@@ -219,6 +221,7 @@ - "rx-ntuple-filter", - "rx-vlan-hw-parse", - "tx-vlan-hw-insert", -+ "highdma", - ] - - def __init__(self, np_ethtool): -diff -Nur nmstate-1.3.3.old/libnmstate/nm/connection.py nmstate-1.3.3/libnmstate/nm/connection.py ---- nmstate-1.3.3.old/libnmstate/nm/connection.py 2023-02-15 15:48:11.739845988 +0800 -+++ nmstate-1.3.3/libnmstate/nm/connection.py 2023-02-15 15:48:33.672042194 +0800 -@@ -94,7 +94,7 @@ - self._setting = new - - def set_controller(self, controller, port_type): -- if controller is not None: -+ if controller: - self._setting.props.master = controller - self._setting.props.slave_type = port_type - -@@ -186,7 +186,7 @@ - settings.extend(create_ovs_interface_setting(patch_state, dpdk_state)) - elif iface.type == InterfaceType.INFINIBAND: - ib_setting = create_infiniband_setting( -- iface_info, -+ iface, - nm_profile, - iface.original_desire_dict, - ) -diff -Nur nmstate-1.3.3.old/libnmstate/nm/device.py nmstate-1.3.3/libnmstate/nm/device.py ---- nmstate-1.3.3.old/libnmstate/nm/device.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/nm/device.py 2023-02-15 15:48:45.122101559 +0800 -@@ -106,39 +106,41 @@ - self._iface_name = iface_name - self._iface_type = iface_type - self._nm_dev = nm_dev -+ self._action = None - - def run(self): -- action = f"Delete device: {self._iface_type} {self._iface_name}" -- user_data = action -- self._ctx.register_async(action) -+ self._action = f"Delete device: {self._iface_type} {self._iface_name}" -+ retried = False -+ self._ctx.register_async(self._action) - self._nm_dev.delete_async( -- self._ctx.cancellable, self._delete_device_callback, user_data -+ self._ctx.cancellable, self._delete_device_callback, retried - ) - -- def _delete_device_callback(self, nm_dev, result, user_data): -- action = user_data -+ def _delete_device_callback(self, nm_dev, result, retried): - if self._ctx.is_cancelled(): - return -- error = None - try: - nm_dev.delete_finish(result) -+ self._ctx.finish_async(self._action) - except Exception as e: -- error = e -- -- if not nm_dev.is_real(): -- logging.debug( -- f"Interface is deleted and not real/exist anymore: " -- f"iface={self._iface_name} type={self._iface_type}" -- ) -- if error: -- logging.debug(f"Ignored error: {error}") -- self._ctx.finish_async(action) -- else: -- self._ctx.fail( -- NmstateLibnmError( -- f"{action} failed: error={error or 'unknown'}" -+ if not nm_dev.is_real(): -+ logging.debug( -+ f"Interface is deleted and not real/exist anymore: " -+ f"iface={self._iface_name} type={self._iface_type}" -+ ) -+ logging.debug(f"Ignored error: {e}") -+ self._ctx.finish_async(self._action) -+ elif retried: -+ self._ctx.fail( -+ NmstateLibnmError(f"{self._action} failed: error={e}") -+ ) -+ else: -+ retried = True -+ self._nm_dev.delete_async( -+ self._ctx.cancellable, -+ self._delete_device_callback, -+ retried, - ) -- ) - - - def list_devices(client): -diff -Nur nmstate-1.3.3.old/libnmstate/nm/infiniband.py nmstate-1.3.3/libnmstate/nm/infiniband.py ---- nmstate-1.3.3.old/libnmstate/nm/infiniband.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/nm/infiniband.py 2023-02-14 18:48:57.083997920 +0800 -@@ -71,7 +71,9 @@ - return None - - --def create_setting(iface_info, base_con_profile, original_iface_info): -+def create_setting(iface, base_con_profile, original_iface_info): -+ iface.pre_edit_validation_and_cleanup() -+ iface_info = iface.to_dict() - ib_config = iface_info.get(InfiniBand.CONFIG_SUBTREE) - if not ib_config: - return None -diff -Nur nmstate-1.3.3.old/libnmstate/nm/ipv4.py nmstate-1.3.3/libnmstate/nm/ipv4.py ---- nmstate-1.3.3.old/libnmstate/nm/ipv4.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/nm/ipv4.py 2023-02-15 15:48:33.672042194 +0800 -@@ -27,7 +27,7 @@ - from ..ifaces import BaseIface - from .common import NM - --INT32_MAX = 2 ** 31 - 1 -+INT32_MAX = 2**31 - 1 - - - def create_setting(config, base_con_profile): -@@ -77,6 +77,9 @@ - # when the DHCP timeout expired, set it to the maximum value to - # make this unlikely. - setting_ipv4.props.dhcp_timeout = INT32_MAX -+ route_metric = config.get(InterfaceIPv4.AUTO_ROUTE_METRIC) -+ if route_metric is not None: -+ setting_ipv4.props.route_metric = route_metric - elif config.get(InterfaceIPv4.ADDRESS): - setting_ipv4.props.method = NM.SETTING_IP4_CONFIG_METHOD_MANUAL - _add_addresses(setting_ipv4, config[InterfaceIPv4.ADDRESS]) -@@ -129,6 +132,8 @@ - info[InterfaceIPv4.AUTO_GATEWAY] = not props.never_default - info[InterfaceIPv4.AUTO_DNS] = not props.ignore_auto_dns - info[InterfaceIPv4.AUTO_ROUTE_TABLE_ID] = props.route_table -+ if props.route_metric >= 0: -+ info[InterfaceIPv4.AUTO_ROUTE_METRIC] = props.route_metric - if props.dhcp_client_id: - info[InterfaceIPv4.DHCP_CLIENT_ID] = props.dhcp_client_id - -diff -Nur nmstate-1.3.3.old/libnmstate/nm/ipv6.py nmstate-1.3.3/libnmstate/nm/ipv6.py ---- nmstate-1.3.3.old/libnmstate/nm/ipv6.py 2023-02-15 15:48:11.737845979 +0800 -+++ nmstate-1.3.3/libnmstate/nm/ipv6.py 2023-02-15 15:50:14.713397220 +0800 -@@ -31,7 +31,7 @@ - from .common import NM - - IPV6_DEFAULT_ROUTE_METRIC = 1024 --INT32_MAX = 2 ** 31 - 1 -+INT32_MAX = 2**31 - 1 - - - def get_info(active_connection, applied_config): -@@ -73,6 +73,8 @@ - info[InterfaceIPv6.AUTO_ROUTE_TABLE_ID] = props.route_table - if props.dhcp_duid: - info[InterfaceIPv6.DHCP_DUID] = props.dhcp_duid -+ if props.route_metric > 0: -+ info[InterfaceIPv6.AUTO_ROUTE_METRIC] = props.route_metric - info[InterfaceIPv6.ADDR_GEN_MODE] = ( - InterfaceIPv6.ADDR_GEN_MODE_STABLE_PRIVACY - if props.addr_gen_mode -@@ -146,6 +148,10 @@ - if route_table: - setting_ip.props.route_table = route_table - -+ route_metric = config.get(InterfaceIPv6.AUTO_ROUTE_METRIC) -+ if route_metric is not None: -+ setting_ip.props.route_metric = route_metric -+ - elif ip_addresses: - _set_static(setting_ip, ip_addresses) - else: -diff -Nur nmstate-1.3.3.old/libnmstate/nm/ovs.py nmstate-1.3.3/libnmstate/nm/ovs.py ---- nmstate-1.3.3.old/libnmstate/nm/ovs.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/nm/ovs.py 2023-02-15 15:48:33.672042194 +0800 -@@ -22,6 +22,7 @@ - - from libnmstate.ifaces import ovs - from libnmstate.ifaces.bridge import BridgeIface -+from libnmstate.ifaces.ovs import OvsBridgeIface - from libnmstate.ifaces.ovs import OvsPortIface - from libnmstate.schema import Interface - from libnmstate.schema import InterfaceType -@@ -33,7 +34,6 @@ - - - CONTROLLER_TYPE_METADATA = "_controller_type" --CONTROLLER_METADATA = "_controller" - SETTING_OVS_EXTERNALIDS = "SettingOvsExternalIDs" - SETTING_OVS_EXTERNAL_IDS_SETTING_NAME = "ovs-external-ids" - -@@ -338,17 +338,24 @@ - iface_name = iface.name - iface_info = iface.to_dict() - port_options = iface_info.get(BridgeIface.BRPORT_OPTIONS_METADATA) -- if ovs.is_ovs_lag_port(port_options): -- port_name = port_options[OB.Port.NAME] -+ if port_options: -+ if ovs.is_ovs_lag_port(port_options): -+ port_name = port_options[OB.Port.NAME] -+ else: -+ port_name = iface_name - else: -+ # User is attaching system port to OVS bridge via `controller` property -+ # with OVS bridge not mentioned in desired state - port_name = iface_name -+ port_options = {} -+ - return OvsPortIface( - { - Interface.NAME: port_name, - Interface.TYPE: InterfaceType.OVS_PORT, - Interface.STATE: iface.state, - OB.OPTIONS_SUBTREE: port_options, -- CONTROLLER_METADATA: iface_info[CONTROLLER_METADATA], -+ Interface.CONTROLLER: iface_info[Interface.CONTROLLER], - CONTROLLER_TYPE_METADATA: iface_info[CONTROLLER_TYPE_METADATA], - } - ) -@@ -356,3 +363,17 @@ - - def _is_nm_support_ovs_external_ids(): - return hasattr(NM, SETTING_OVS_EXTERNALIDS) -+ -+ -+def set_ovs_iface_controller_info(iface_infos): -+ pending_changes = {} -+ for iface_info in iface_infos: -+ if iface_info.get(Interface.TYPE) == InterfaceType.OVS_BRIDGE: -+ iface = OvsBridgeIface(info=iface_info, save_to_disk=True) -+ for port in iface.port: -+ pending_changes[port] = iface.name -+ -+ for iface_info in iface_infos: -+ ctrl_name = pending_changes.get(iface_info[Interface.NAME]) -+ if ctrl_name: -+ iface_info[Interface.CONTROLLER] = ctrl_name -diff -Nur nmstate-1.3.3.old/libnmstate/nm/plugin.py nmstate-1.3.3/libnmstate/nm/plugin.py ---- nmstate-1.3.3.old/libnmstate/nm/plugin.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/nm/plugin.py 2023-02-15 15:48:33.672042194 +0800 -@@ -49,6 +49,7 @@ - from .ovs import get_ovs_bridge_info - from .ovs import get_ovsdb_external_ids - from .ovs import has_ovs_capability -+from .ovs import set_ovs_iface_controller_info - from .profiles import NmProfiles - from .profiles import get_all_applied_configs - from .team import get_info as get_team_info -@@ -192,6 +193,8 @@ - - info.append(iface_info) - -+ set_ovs_iface_controller_info(info) -+ - info.sort(key=itemgetter("name")) - - return info -@@ -293,7 +296,7 @@ - def generate_configurations(self, net_state): - if not hasattr(NM, "keyfile_write"): - raise NmstateNotSupportedError( -- f"Current NetworkManager version does not support generating " -+ "Current NetworkManager version does not support generating " - "configurations, please upgrade to 1.30 or later versoin." - ) - return NmProfiles(None).generate_config_strings(net_state) -diff -Nur nmstate-1.3.3.old/libnmstate/nm/profile.py nmstate-1.3.3/libnmstate/nm/profile.py ---- nmstate-1.3.3.old/libnmstate/nm/profile.py 2023-02-15 15:48:11.748846029 +0800 -+++ nmstate-1.3.3/libnmstate/nm/profile.py 2023-02-15 15:48:45.122101559 +0800 -@@ -139,6 +139,11 @@ - else: - return "" - -+ def disable_autoconnect(self): -+ if self._nm_simple_conn: -+ nm_conn_setting = self._nm_simple_conn.get_setting_connection() -+ nm_conn_setting.props.autoconnect = False -+ - def update_controller(self, controller): - nm_simple_conn_update_controller(self._nm_simple_conn, controller) - -@@ -186,8 +191,10 @@ - elif self._iface.is_down: - if self._nm_ac: - self._add_action(NmProfile.ACTION_DEACTIVATE) -- elif self._iface.is_virtual and self._nm_dev: -+ if self._iface.is_virtual and self._nm_dev: - self._add_action(NmProfile.ACTION_DELETE_DEVICE) -+ if self._nm_dev and not self._nm_dev.get_managed(): -+ self._add_action(NmProfile.ACTION_DEACTIVATE) - - if self._iface.raw.get(ROUTE_REMOVED): - # This is a workaround for NM bug: -@@ -276,7 +283,12 @@ - ) - - def prepare_config(self, save_to_disk, gen_conf_mode=False): -- if self._iface.is_absent or self._iface.is_down: -+ if self._iface.is_absent or ( -+ self._iface.is_down -+ and not gen_conf_mode -+ and self._nm_dev -+ and self._nm_dev.get_managed() -+ ): - return - - # Don't create new profile if original desire does not ask -@@ -312,7 +324,9 @@ - self._gen_actions() - if not self.has_pending_change: - return -- if self._iface.is_absent or self._iface.is_down: -+ if self._iface.is_absent or ( -+ self._iface.is_down and self._nm_dev and self._nm_dev.get_managed() -+ ): - return - # Don't create new profile if original desire does not ask - # anything besides state:up and not been marked as changed. -@@ -411,6 +425,9 @@ - def _deactivate(self): - if self._deactivated: - return -+ self._nm_ac = ( -+ self._nm_dev.get_active_connection() if self._nm_dev else None -+ ) - if self._nm_ac: - ActiveConnectionDeactivate( - self._ctx, self._iface.name, self._iface.type, self._nm_ac -diff -Nur nmstate-1.3.3.old/libnmstate/nm/profiles.py nmstate-1.3.3/libnmstate/nm/profiles.py ---- nmstate-1.3.3.old/libnmstate/nm/profiles.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/nm/profiles.py 2023-02-15 15:48:45.122101559 +0800 -@@ -26,6 +26,7 @@ - from libnmstate.schema import Interface - from libnmstate.schema import InterfaceState - from libnmstate.schema import InterfaceType -+from libnmstate.schema import OvsDB - - from .common import NM - from .device import is_externally_managed -@@ -51,9 +52,11 @@ - _append_nm_ovs_port_iface(net_state) - all_profiles = [] - for iface in net_state.ifaces.all_ifaces(): -- if iface.is_up: -+ if iface.is_up or iface.is_down: - profile = NmProfile(self._ctx, iface) - profile.prepare_config(save_to_disk=False, gen_conf_mode=True) -+ if iface.is_down: -+ profile.disable_autoconnect() - all_profiles.append(profile) - - _use_uuid_as_controller_and_parent(all_profiles) -@@ -123,16 +126,18 @@ - subordinate of NM OVS port profile which is port of the OVS bridge - profile. - We need to create/delete this NM OVS port profile accordingly. -+ We skip this action if ovs interface is not changed. - """ - nm_ovs_port_ifaces = {} - - for iface in net_state.ifaces.all_kernel_ifaces.values(): - if iface.controller_type == InterfaceType.OVS_BRIDGE: -+ has_ovs_change = _has_ovs_changes(iface, net_state) - nm_ovs_port_iface = create_iface_for_nm_ovs_port(iface) - iface.set_controller( - nm_ovs_port_iface.name, InterfaceType.OVS_PORT - ) -- if iface.is_desired or iface.is_changed: -+ if (iface.is_desired or iface.is_changed) and has_ovs_change: - nm_ovs_port_iface.mark_as_changed() - nm_ovs_port_ifaces[nm_ovs_port_iface.name] = nm_ovs_port_iface - -@@ -390,6 +395,10 @@ - iface = nm_profile.iface - if not iface.is_up: - continue -+ # InfiniBand setting does not support UUID as parent -+ if iface.type == InterfaceType.INFINIBAND: -+ continue -+ - if ( - iface.controller - and (iface.is_changed or iface.is_desired) -@@ -434,3 +443,28 @@ - ): - return True - return False -+ -+ -+def _has_ovs_changes(iface, net_state): -+ """ -+ Return False only when below all matches: -+ * Desired interface is up -+ * Desire state did not mentioned its OVS bridge controller -+ * Interface has no changed to controller property -+ * Interface has no ovs-db setting change in desire state -+ """ -+ ctrl_iface = net_state.ifaces.get_iface( -+ iface.controller, InterfaceType.OVS_BRIDGE -+ ) -+ if ( -+ iface.is_desired -+ and iface.is_up -+ and ctrl_iface -+ and not ctrl_iface.is_desired -+ and not ctrl_iface.is_changed -+ and Interface.CONTROLLER not in iface.original_desire_dict -+ and OvsDB.KEY not in iface.original_desire_dict -+ ): -+ return False -+ -+ return True -diff -Nur nmstate-1.3.3.old/libnmstate/nmstate.py nmstate-1.3.3/libnmstate/nmstate.py ---- nmstate-1.3.3.old/libnmstate/nmstate.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/nmstate.py 2023-02-15 15:48:33.672042194 +0800 -@@ -73,6 +73,7 @@ - include_status_data=None, - info_type=_INFO_TYPE_RUNNING, - include_secrets=False, -+ include_controller_prop=True, - ): - for plugin in plugins: - plugin.refresh_content() -@@ -81,7 +82,7 @@ - report["capabilities"] = plugins_capabilities(plugins) - - report[Interface.KEY] = _get_interface_info_from_plugins( -- plugins, info_type -+ plugins, info_type, include_controller_prop=include_controller_prop - ) - - report[Route.KEY] = _get_routes_from_plugins(plugins, info_type) -@@ -103,6 +104,7 @@ - - if not include_secrets: - hide_the_secrets(report) -+ - return report - - -@@ -185,7 +187,9 @@ - return chose_plugin - - --def _get_interface_info_from_plugins(plugins, info_type): -+def _get_interface_info_from_plugins( -+ plugins, info_type, include_controller_prop=True -+): - all_ifaces = {} - IFACE_PRIORITY_METADATA = "_plugin_priority" - IFACE_PLUGIN_SRC_METADATA = "_plugin_source" -@@ -287,6 +291,8 @@ - for iface in all_ifaces.values(): - iface.pop(IFACE_PRIORITY_METADATA) - iface.pop(IFACE_PLUGIN_SRC_METADATA) -+ if not include_controller_prop: -+ iface.pop(Interface.CONTROLLER, None) - - return sorted(all_ifaces.values(), key=itemgetter(Interface.NAME)) - -@@ -404,6 +410,7 @@ - plugins, - info_type=_INFO_TYPE_RUNNING_CONFIG, - include_secrets=include_secrets, -+ include_controller_prop=False, - ) - - -diff -Nur nmstate-1.3.3.old/libnmstate/plugin.py nmstate-1.3.3/libnmstate/plugin.py ---- nmstate-1.3.3.old/libnmstate/plugin.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/plugin.py 2023-02-14 18:35:05.960135769 +0800 -@@ -32,6 +32,7 @@ - PLUGIN_CAPABILITY_ROUTE = "route" - PLUGIN_CAPABILITY_ROUTE_RULE = "route_rule" - PLUGIN_CAPABILITY_DNS = "dns" -+ PLUGIN_CAPABILITY_OVSDB_GLOBAL = "ovsdb_global" - - DEFAULT_PRIORITY = 10 - -diff -Nur nmstate-1.3.3.old/libnmstate/plugins/nmstate_plugin_ovsdb.py nmstate-1.3.3/libnmstate/plugins/nmstate_plugin_ovsdb.py ---- nmstate-1.3.3.old/libnmstate/plugins/nmstate_plugin_ovsdb.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/plugins/nmstate_plugin_ovsdb.py 2023-02-14 18:35:05.960135769 +0800 -@@ -156,8 +156,16 @@ - return NmstatePlugin.DEFAULT_PRIORITY + 1 - - @property -+ def capabilities(self): -+ return [ -+ NmstatePlugin.PLUGIN_CAPABILITY_OVSDB_GLOBAL, -+ ] -+ -+ @property - def plugin_capabilities(self): -- return NmstatePlugin.PLUGIN_CAPABILITY_IFACE -+ return [ -+ NmstatePlugin.PLUGIN_CAPABILITY_IFACE, -+ ] - - def get_interfaces(self): - ifaces = [] -diff -Nur nmstate-1.3.3.old/libnmstate/schema.py nmstate-1.3.3/libnmstate/schema.py ---- nmstate-1.3.3.old/libnmstate/schema.py 2023-02-15 15:48:11.742846002 +0800 -+++ nmstate-1.3.3/libnmstate/schema.py 2023-02-15 15:48:33.672042194 +0800 -@@ -48,6 +48,7 @@ - MTU = "mtu" - COPY_MAC_FROM = "copy-mac-from" - ACCEPT_ALL_MAC_ADDRESSES = "accept-all-mac-addresses" -+ CONTROLLER = "controller" - - - class Route: -@@ -148,6 +149,7 @@ - AUTO_GATEWAY = "auto-gateway" - AUTO_ROUTES = "auto-routes" - AUTO_ROUTE_TABLE_ID = "auto-route-table-id" -+ AUTO_ROUTE_METRIC = "auto-route-metric" - - - class InterfaceIPv4(InterfaceIP): -diff -Nur nmstate-1.3.3.old/libnmstate/validator.py nmstate-1.3.3/libnmstate/validator.py ---- nmstate-1.3.3.old/libnmstate/validator.py 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/validator.py 2023-02-14 18:35:05.960135769 +0800 -@@ -23,6 +23,7 @@ - import jsonschema as js - - from libnmstate.schema import Interface -+from libnmstate.schema import OvsDB - from libnmstate.schema import InterfaceType - from libnmstate.error import NmstateDependencyError - -@@ -43,6 +44,7 @@ - - def validate_capabilities(state, capabilities): - validate_interface_capabilities(state.get(Interface.KEY, []), capabilities) -+ validate_ovsdb_global_cap(state.get(OvsDB.KEY, {}), capabilities) - - - def validate_interface_capabilities(ifaces_state, capabilities): -@@ -78,3 +80,15 @@ - "Interfaces count exceeds the limit %s in desired state", - MAX_SUPPORTED_INTERFACES, - ) -+ -+ -+def validate_ovsdb_global_cap(ovsdb_global_conf, capabilities): -+ if ( -+ ovsdb_global_conf -+ and NmstatePlugin.PLUGIN_CAPABILITY_OVSDB_GLOBAL not in capabilities -+ ): -+ raise NmstateDependencyError( -+ "Missing plugin for ovs-db global configuration, " -+ "please try to install 'nmstate-plugin-ovsdb' or other plugin " -+ "provides NmstatePlugin.PLUGIN_CAPABILITY_OVSDB_GLOBAL" -+ ) -diff -Nur nmstate-1.3.3.old/libnmstate/VERSION nmstate-1.3.3/libnmstate/VERSION ---- nmstate-1.3.3.old/libnmstate/VERSION 2022-08-11 23:22:22.000000000 +0800 -+++ nmstate-1.3.3/libnmstate/VERSION 2023-02-15 15:48:33.668708842 +0800 -@@ -1 +1 @@ --1.3.3 -+1.3.4 diff --git a/SOURCES/BZ_2169642-Ignore-error-when-creating-profile-if-not-desired.patch b/SOURCES/BZ_2169642-Ignore-error-when-creating-profile-if-not-desired.patch deleted file mode 100644 index 68e34d4..0000000 --- a/SOURCES/BZ_2169642-Ignore-error-when-creating-profile-if-not-desired.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 030bd5e38a2913e96ef145f88cb74c619acea6bf Mon Sep 17 00:00:00 2001 -From: Gris Ge -Date: Mon, 27 Feb 2023 12:17:05 +0800 -Subject: [PATCH] nm: Ignore error when creating profile if not desired - -When a undesired interface holding `autoconf: true` and `dhcp: false` -for IPv6, nmstate will fail with error: - - Autoconf without DHCP is not supported yet - -This is caused by `nm/connection.py` try to create `NM.SimpleConnection` -for every interface even not desired. - -This patch changed to: - * Only create new `NM.SimpleConnection` when desired or changed. - * Use current profile if exists when not desired or changed. - * Ignore error if not desired/changed. - -Integration test case included. - -Signed-off-by: Gris Ge ---- - libnmstate/nm/profile.py | 20 +++++++++++++++++--- - 1 file changed, 17 insertions(+), 3 deletions(-) - -diff --git a/libnmstate/nm/profile.py b/libnmstate/nm/profile.py -index ad1ad19f..1119cd1a 100644 ---- a/libnmstate/nm/profile.py -+++ b/libnmstate/nm/profile.py -@@ -24,6 +24,7 @@ from distutils.version import StrictVersion - import logging - import time - -+from libnmstate.error import NmstateError - from libnmstate.error import NmstateInternalError - from libnmstate.error import NmstateLibnmError - from libnmstate.error import NmstateNotSupportedError -@@ -321,9 +322,22 @@ class NmProfile: - # TODO: Use applied config as base profile - # Or even better remove the base profile argument as top level - # of nmstate should provide full/merged configure. -- self._nm_simple_conn = create_new_nm_simple_conn( -- self._iface, self._nm_profile -- ) -+ if self._iface.is_changed or self._iface.is_desired: -+ self._nm_simple_conn = create_new_nm_simple_conn( -+ self._iface, self._nm_profile -+ ) -+ elif self._nm_profile: -+ self._nm_simple_conn = NM.SimpleConnection.new_clone( -+ self._nm_profile -+ ) -+ else: -+ try: -+ self._nm_simple_conn = create_new_nm_simple_conn( -+ self._iface, self._nm_profile -+ ) -+ # No error for undesired interface -+ except NmstateError: -+ pass - - def save_config(self, save_to_disk): - self._check_sriov_support() --- -2.39.2 - diff --git a/SOURCES/BZ_2170078-Introduce-wait-ip.patch b/SOURCES/BZ_2170078-Introduce-wait-ip.patch deleted file mode 100644 index b8ebc60..0000000 --- a/SOURCES/BZ_2170078-Introduce-wait-ip.patch +++ /dev/null @@ -1,208 +0,0 @@ -From ef23275126898f316cb3e7e2df552c006e867105 Mon Sep 17 00:00:00 2001 -From: Gris Ge -Date: Tue, 14 Feb 2023 15:20:21 +0800 -Subject: [PATCH] ip: Introduce `Interface.WAIT_IP` - -This patch is introducing `Interface.WAIT_IP` property with these -possible values: - * `Interface.WAIT_IP_ANY`("any"): - System will consider interface activated when any IP stack is - configured(neither static or automatic). - * `Interface.WAIT_IP_IPV4`("ipv4"): - System will wait IPv4 been configured. - * `Interface.WAIT_IP_IPV6`("ipv6"): - System will wait IPv6 been configured. - * `Introduce.WAIT_IP_IPV4_AND_IPV6`("ipv4+ipv6"): - System will wait both IPv4 and IPv6 been configured. - -Considering this old branch, there is no validation on invalid use cases -like setting wait-ip on disabled IP stack. - -Example YAML on waiting both IP stacks: - -```yml ---- -interfaces: - - name: eth1 - type: ethernet - state: up - mtu: 1500 - wait-ip: ipv4+ipv6 - ipv4: - enabled: true - dhcp: true - ipv6: - enabled: true - dhcp: true - autoconf: true -``` - -Integration test case included. - -Signed-off-by: Gris Ge ---- - libnmstate/nm/connection.py | 9 +++---- - libnmstate/nm/ip.py | 48 +++++++++++++++++++++++++++++++++++++ - libnmstate/nm/plugin.py | 29 +++++++++------------- - libnmstate/schema.py | 5 ++++ - 4 files changed, 69 insertions(+), 22 deletions(-) - create mode 100644 libnmstate/nm/ip.py - -diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py -index 1974cfd4..1fbb380b 100644 ---- a/libnmstate/nm/connection.py -+++ b/libnmstate/nm/connection.py -@@ -39,6 +39,7 @@ from .common import NM - from .ethtool import create_ethtool_setting - from .ieee_802_1x import create_802_1x_setting - from .infiniband import create_setting as create_infiniband_setting -+from .ip import set_wait_ip - from .ipv4 import create_setting as create_ipv4_setting - from .ipv6 import create_setting as create_ipv6_setting - from .lldp import apply_lldp_setting -@@ -106,10 +107,10 @@ class _ConnectionSetting: - def create_new_nm_simple_conn(iface, nm_profile): - nm_iface_type = Api2Nm.get_iface_type(iface.type) - iface_info = iface.to_dict() -- settings = [ -- create_ipv4_setting(iface_info.get(Interface.IPV4), nm_profile), -- create_ipv6_setting(iface_info.get(Interface.IPV6), nm_profile), -- ] -+ ipv4_set = create_ipv4_setting(iface_info.get(Interface.IPV4), nm_profile) -+ ipv6_set = create_ipv6_setting(iface_info.get(Interface.IPV6), nm_profile) -+ set_wait_ip(ipv4_set, ipv6_set, iface_info.get(Interface.WAIT_IP)) -+ settings = [ipv4_set, ipv6_set] - con_setting = _ConnectionSetting() - if nm_profile and not is_multiconnect_profile(nm_profile): - con_setting.import_by_profile(nm_profile, iface.is_controller) -diff --git a/libnmstate/nm/ip.py b/libnmstate/nm/ip.py -new file mode 100644 -index 00000000..d0fc1e3b ---- /dev/null -+++ b/libnmstate/nm/ip.py -@@ -0,0 +1,48 @@ -+# SPDX-License-Identifier: LGPL-2.1-or-later -+ -+from libnmstate.schema import Interface -+ -+ -+def get_wait_ip(applied_config): -+ if applied_config: -+ nm_ipv4_may_fail = _get_may_fail(applied_config, False) -+ nm_ipv6_may_fail = _get_may_fail(applied_config, True) -+ if nm_ipv4_may_fail and not nm_ipv6_may_fail: -+ return Interface.WAIT_IP_IPV6 -+ elif not nm_ipv4_may_fail and nm_ipv6_may_fail: -+ return Interface.WAIT_IP_IPV4 -+ elif not nm_ipv4_may_fail and not nm_ipv6_may_fail: -+ return Interface.WAIT_IP_IPV4_AND_IPV6 -+ return Interface.WAIT_IP_ANY -+ -+ -+def set_wait_ip(nm_ipv4_set, nm_ipv6_set, wait_ip): -+ if nm_ipv4_set: -+ if wait_ip == Interface.WAIT_IP_ANY: -+ nm_ipv4_set.props.may_fail = True -+ elif wait_ip in ( -+ Interface.WAIT_IP_IPV4, -+ Interface.WAIT_IP_IPV4_AND_IPV6, -+ ): -+ nm_ipv4_set.props.may_fail = False -+ if nm_ipv6_set: -+ if wait_ip == Interface.WAIT_IP_ANY: -+ nm_ipv6_set.props.may_fail = True -+ elif wait_ip in ( -+ Interface.WAIT_IP_IPV6, -+ Interface.WAIT_IP_IPV4_AND_IPV6, -+ ): -+ nm_ipv6_set.props.may_fail = False -+ -+ -+def _get_may_fail(nm_profile, is_ipv6): -+ if is_ipv6: -+ nm_set = nm_profile.get_setting_ip6_config() -+ else: -+ nm_set = nm_profile.get_setting_ip4_config() -+ -+ if nm_set: -+ return nm_set.props.may_fail -+ else: -+ # NM is defaulting `may-fail` as True -+ return True -diff --git a/libnmstate/nm/plugin.py b/libnmstate/nm/plugin.py -index bca1aedd..9bbbbb98 100644 ---- a/libnmstate/nm/plugin.py -+++ b/libnmstate/nm/plugin.py -@@ -1,21 +1,5 @@ --# --# Copyright (c) 2020-2021 Red Hat, Inc. --# --# This file is part of nmstate --# --# This program is free software: you can redistribute it and/or modify --# it under the terms of the GNU Lesser General Public License as published by --# the Free Software Foundation, either version 2.1 of the License, or --# (at your option) any later version. --# --# This program is distributed in the hope that it will be useful, --# but WITHOUT ANY WARRANTY; without even the implied warranty of --# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --# GNU Lesser General Public License for more details. --# --# You should have received a copy of the GNU Lesser General Public License --# along with this program. If not, see . --# -+# SPDX-License-Identifier: LGPL-2.1-or-later -+ - from distutils.version import StrictVersion - import logging - from operator import itemgetter -@@ -25,6 +9,8 @@ from libnmstate.error import NmstateNotSupportedError - from libnmstate.error import NmstateValueError - from libnmstate.schema import DNS - from libnmstate.schema import Interface -+from libnmstate.schema import InterfaceIPv4 -+from libnmstate.schema import InterfaceIPv6 - from libnmstate.schema import InterfaceType - from libnmstate.schema import LLDP - from libnmstate.plugin import NmstatePlugin -@@ -41,6 +27,7 @@ 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 - from .infiniband import get_info as get_infiniband_info -+from .ip import get_wait_ip - from .ipv4 import get_info as get_ipv4_info - from .ipv6 import get_info as get_ipv6_info - from .lldp import get_info as get_lldp_info -@@ -190,6 +177,12 @@ class NetworkManagerPlugin(NmstatePlugin): - - if applied_config: - iface_info.update(get_ovsdb_external_ids(applied_config)) -+ if iface_info.get(Interface.IPV4, {}).get( -+ InterfaceIPv4.ENABLED -+ ) or iface_info.get(Interface.IPV6, {}).get( -+ InterfaceIPv6.ENABLED -+ ): -+ iface_info[Interface.WAIT_IP] = get_wait_ip(applied_config) - - info.append(iface_info) - -diff --git a/libnmstate/schema.py b/libnmstate/schema.py -index e740abff..c3d3fcfc 100644 ---- a/libnmstate/schema.py -+++ b/libnmstate/schema.py -@@ -49,6 +49,11 @@ class Interface: - COPY_MAC_FROM = "copy-mac-from" - ACCEPT_ALL_MAC_ADDRESSES = "accept-all-mac-addresses" - CONTROLLER = "controller" -+ WAIT_IP = "wait-ip" -+ WAIT_IP_ANY = "any" -+ WAIT_IP_IPV4 = "ipv4" -+ WAIT_IP_IPV6 = "ipv6" -+ WAIT_IP_IPV4_AND_IPV6 = "ipv4+ipv6" - - - class Route: --- -2.39.2 - diff --git a/SOURCES/Enable_clib_yml_api.patch b/SOURCES/Enable_clib_yml_api.patch new file mode 100644 index 0000000..ed8cf9f --- /dev/null +++ b/SOURCES/Enable_clib_yml_api.patch @@ -0,0 +1,636 @@ +From ad2bfa136290e72cdfd4b7877b49b3fc07203f9c Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 21 Feb 2023 16:26:22 +0800 +Subject: [PATCH] clib: Introduce YAML support + +Allowing both YAML and JSON input, the output format will matching input +format. + +For `nmstate_net_state_retrieve()`, user can use +`NMSTATE_FLAG_YAML_OUTPUT` flag to instruct the output to be YAML +format. + +Signed-off-by: Gris Ge +--- + rust/src/clib/Cargo.toml | 1 + + rust/src/clib/apply.rs | 2 +- + rust/src/clib/gen_conf.rs | 52 +++++++----- + rust/src/clib/nmstate.h.in | 55 +++++++------ + rust/src/clib/policy.rs | 57 ++++++++----- + rust/src/clib/query.rs | 49 ++++++++---- + .../{nmpolicy_test.c => nmpolicy_json_test.c} | 5 ++ + rust/src/clib/test/nmpolicy_yaml_test.c | 80 +++++++++++++++++++ + .../{nmstate_test.c => nmstate_json_test.c} | 5 ++ + rust/src/clib/test/nmstate_yaml_test.c | 34 ++++++++ + rust/src/lib/Cargo.toml | 3 + + rust/src/lib/net_state.rs | 14 +++- + 12 files changed, 274 insertions(+), 83 deletions(-) + rename rust/src/clib/test/{nmpolicy_test.c => nmpolicy_json_test.c} (96%) + create mode 100644 rust/src/clib/test/nmpolicy_yaml_test.c + rename rust/src/clib/test/{nmstate_test.c => nmstate_json_test.c} (87%) + create mode 100644 rust/src/clib/test/nmstate_yaml_test.c + +diff --git a/rust/src/clib/Cargo.toml b/rust/src/clib/Cargo.toml +index 97e4128c..ed391b3a 100644 +--- a/rust/src/clib/Cargo.toml ++++ b/rust/src/clib/Cargo.toml +@@ -16,6 +16,7 @@ crate-type = ["cdylib", "staticlib"] + nmstate = { path = "../lib", default-features = false } + libc = "0.2.74" + serde_json = "1.0" ++serde_yaml = "0.9" + log = "0.4.17" + serde = { version = "1.0.137", features = ["derive"] } + once_cell = "1.12.0" +diff --git a/rust/src/clib/apply.rs b/rust/src/clib/apply.rs +index 9a0d6fbc..67d39730 100644 +--- a/rust/src/clib/apply.rs ++++ b/rust/src/clib/apply.rs +@@ -74,7 +74,7 @@ pub extern "C" fn nmstate_net_state_apply( + }; + + let mut net_state = +- match nmstate::NetworkState::new_from_json(net_state_str) { ++ match nmstate::NetworkState::new_from_yaml(net_state_str) { + Ok(n) => n, + Err(e) => { + unsafe { +diff --git a/rust/src/clib/gen_conf.rs b/rust/src/clib/gen_conf.rs +index f63fb7b0..1ad7156b 100644 +--- a/rust/src/clib/gen_conf.rs ++++ b/rust/src/clib/gen_conf.rs +@@ -68,7 +68,7 @@ pub extern "C" fn nmstate_generate_configurations( + } + }; + +- let net_state = match nmstate::NetworkState::new_from_json(net_state_str) { ++ let net_state = match nmstate::NetworkState::new_from_yaml(net_state_str) { + Ok(n) => n, + Err(e) => { + unsafe { +@@ -80,28 +80,44 @@ pub extern "C" fn nmstate_generate_configurations( + } + }; + ++ let input_is_json = ++ serde_json::from_str::(net_state_str).is_ok(); + let result = net_state.gen_conf(); + unsafe { + *log = CString::new(logger.drain(now)).unwrap().into_raw(); + } + match result { +- Ok(s) => match serde_json::to_string(&s) { +- Ok(cfgs) => unsafe { +- *configs = CString::new(cfgs).unwrap().into_raw(); +- NMSTATE_PASS +- }, +- Err(e) => unsafe { +- *err_msg = +- CString::new(format!("serde_json::to_string failure: {e}")) +- .unwrap() +- .into_raw(); +- *err_kind = +- CString::new(format!("{}", nmstate::ErrorKind::Bug)) +- .unwrap() +- .into_raw(); +- NMSTATE_FAIL +- }, +- }, ++ Ok(s) => { ++ let serialize = if input_is_json { ++ serde_json::to_string(&s).map_err(|e| { ++ nmstate::NmstateError::new( ++ nmstate::ErrorKind::Bug, ++ format!("Failed to convert state {s:?} to JSON: {e}"), ++ ) ++ }) ++ } else { ++ serde_yaml::to_string(&s).map_err(|e| { ++ nmstate::NmstateError::new( ++ nmstate::ErrorKind::Bug, ++ format!("Failed to convert state {s:?} to YAML: {e}"), ++ ) ++ }) ++ }; ++ ++ match serialize { ++ Ok(cfgs) => unsafe { ++ *configs = CString::new(cfgs).unwrap().into_raw(); ++ NMSTATE_PASS ++ }, ++ Err(e) => unsafe { ++ *err_msg = ++ CString::new(e.msg().to_string()).unwrap().into_raw(); ++ *err_kind = ++ CString::new(e.kind().to_string()).unwrap().into_raw(); ++ NMSTATE_FAIL ++ }, ++ } ++ } + Err(e) => { + unsafe { + *err_msg = CString::new(e.msg()).unwrap().into_raw(); +diff --git a/rust/src/clib/nmstate.h.in b/rust/src/clib/nmstate.h.in +index 0879d47e..391477fd 100644 +--- a/rust/src/clib/nmstate.h.in ++++ b/rust/src/clib/nmstate.h.in +@@ -1,19 +1,4 @@ +-/* +- * Copyright 2021 Red Hat +- * +- * Licensed under the Apache License, Version 2.0 (the "License"); +- * you may not use this file except in compliance with the License. +- * You may obtain a copy of the License at +- * +- * http://www.apache.org/licenses/LICENSE-2.0 +- * +- * Unless required by applicable law or agreed to in writing, software +- * distributed under the License is distributed on an "AS IS" BASIS, +- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +- * See the License for the specific language governing permissions and +- * limitations under the License. +- */ +- ++// SPDX-License-Identifier: Apache-2.0 + + #ifndef _LIBNMSTATE_H_ + #define _LIBNMSTATE_H_ +@@ -44,6 +29,7 @@ extern "C" { + #define NMSTATE_FLAG_NO_COMMIT 1 << 5 + #define NMSTATE_FLAG_MEMORY_ONLY 1 << 6 + #define NMSTATE_FLAG_RUNNING_CONFIG_ONLY 1 << 7 ++#define NMSTATE_FLAG_YAML_OUTPUT 1 << 8 + + /** + * nmstate_net_state_retrieve - Retrieve network state +@@ -52,7 +38,7 @@ extern "C" { + * 0.1 + * + * Description: +- * Retrieve network state in the format of JSON. ++ * Retrieve network state in the format of JSON or YAML. + * + * @flags: + * Flags for special use cases: +@@ -60,6 +46,13 @@ extern "C" { + * No flag + * * NMSTATE_FLAG_KERNEL_ONLY + * Do not use external plugins, show kernel status only. ++ * * NMSTATE_FLAG_INCLUDE_SECRETS ++ * No not hide sercerts like password. ++ * * NMSTATE_FLAG_RUNNING_CONFIG_ONLY ++ * Only include running config excluding running status like auto ++ * IP addresses and routes, LLDP neighbors. ++ * * NMSTATE_FLAG_YAML_OUTPUT ++ * Show the state in YAML format + * @state: + * Output pointer of char array for network state in json format. + * The memory should be freed by nmstate_net_state_free(). +@@ -90,7 +83,7 @@ int nmstate_net_state_retrieve(uint32_t flags, char **state, char **log, + * 0.1 + * + * Description: +- * Apply network state in the format of JSON. ++ * Apply network state in the format of JSON or YAML. + * + * @flags: + * Flags for special use cases: +@@ -98,8 +91,12 @@ int nmstate_net_state_retrieve(uint32_t flags, char **state, char **log, + * No flag + * * NMSTATE_FLAG_KERNEL_ONLY + * Do not use external plugins, apply to kernel only. ++ * * NMSTATE_FLAG_NO_VERIFY ++ * Do not verify state after applied + * * NMSTATE_FLAG_NO_COMMIT + * Do not commit new state after verification ++ * * NMSTATE_FLAG_MEMORY_ONLY ++ * No not store network state to persistent. + * @state: + * Pointer of char array for network state in json format. + * @log: +@@ -119,7 +116,8 @@ int nmstate_net_state_retrieve(uint32_t flags, char **state, char **log, + * * NMSTATE_FAIL + * On failure. + */ +-int nmstate_net_state_apply(uint32_t flags, const char *state, uint32_t rollback_timeout, char **log, ++int nmstate_net_state_apply(uint32_t flags, const char *state, ++ uint32_t rollback_timeout, char **log, + char **err_kind, char **err_msg); + + /** +@@ -151,8 +149,8 @@ int nmstate_net_state_apply(uint32_t flags, const char *state, uint32_t rollback + * * NMSTATE_FAIL + * On failure. + */ +-int nmstate_checkpoint_commit(const char *checkpoint, char **log, char **err_kind, +- char **err_msg); ++int nmstate_checkpoint_commit(const char *checkpoint, char **log, ++ char **err_kind, char **err_msg); + + /** + * nmstate_checkpoint_rollback - Rollback the checkpoint +@@ -183,8 +181,8 @@ int nmstate_checkpoint_commit(const char *checkpoint, char **log, char **err_kin + * * NMSTATE_FAIL + * On failure. + */ +-int nmstate_checkpoint_rollback(const char *checkpoint, char **log, char **err_kind, +- char **err_msg); ++int nmstate_checkpoint_rollback(const char *checkpoint, char **log, ++ char **err_kind, char **err_msg); + + /** + * nmstate_generate_configurations - Generate network configurations +@@ -199,9 +197,10 @@ int nmstate_checkpoint_rollback(const char *checkpoint, char **log, char **err_k + * as value. + * + * @state: +- * Pointer of char array for network state in json format. ++ * Pointer of char array for network state in JSON or YAML format. + * @configs: +- * Output pointer of char array for network configures in json format. ++ * Output pointer of char array for network configures in JSON or ++ * YAML(depend on which format you use in @state) format. + * The memory should be freed by nmstate_net_state_free(). + * @log: + * Output pointer of char array for logging. +@@ -231,14 +230,14 @@ int nmstate_generate_configurations(const char *state, char **configs, + * 2.2 + * + * Description: +- * Generate new network state from policy again specifed state ++ * Generate new network state from policy again specified state + * + * @policy: +- * Pointer of char array for network policy in json format. ++ * Pointer of char array for network policy in JSON/YAML format. + * @current_state: + * Pointer of char array for current network state. + * @state: +- * Output pointer of char array for network state in json format. ++ * Output pointer of char array for network state in JSON/YAML format. + * The memory should be freed by nmstate_net_state_free(). + * @log: + * Output pointer of char array for logging. +diff --git a/rust/src/clib/policy.rs b/rust/src/clib/policy.rs +index ec8c46c1..ea7dd036 100644 +--- a/rust/src/clib/policy.rs ++++ b/rust/src/clib/policy.rs +@@ -67,6 +67,13 @@ pub extern "C" fn nmstate_net_state_from_policy( + } + }; + ++ let input_is_json = ++ if let Ok(policy_str) = unsafe { CStr::from_ptr(policy) }.to_str() { ++ serde_json::from_str::(policy_str).is_ok() ++ } else { ++ false ++ }; ++ + let mut policy = match deserilize_from_c_char::( + policy, err_kind, err_msg, + ) { +@@ -86,23 +93,37 @@ pub extern "C" fn nmstate_net_state_from_policy( + } + + match result { +- Ok(s) => match serde_json::to_string(&s) { +- Ok(state_str) => unsafe { +- *state = CString::new(state_str).unwrap().into_raw(); +- NMSTATE_PASS +- }, +- Err(e) => unsafe { +- *err_msg = +- CString::new(format!("serde_json::to_string failure: {e}")) +- .unwrap() +- .into_raw(); +- *err_kind = +- CString::new(format!("{}", nmstate::ErrorKind::Bug)) +- .unwrap() +- .into_raw(); +- NMSTATE_FAIL +- }, +- }, ++ Ok(s) => { ++ let serialize = if input_is_json { ++ serde_json::to_string(&s).map_err(|e| { ++ nmstate::NmstateError::new( ++ nmstate::ErrorKind::Bug, ++ format!("Failed to convert state {s:?} to JSON: {e}"), ++ ) ++ }) ++ } else { ++ serde_yaml::to_string(&s).map_err(|e| { ++ nmstate::NmstateError::new( ++ nmstate::ErrorKind::Bug, ++ format!("Failed to convert state {s:?} to YAML: {e}"), ++ ) ++ }) ++ }; ++ ++ match serialize { ++ Ok(state_str) => unsafe { ++ *state = CString::new(state_str).unwrap().into_raw(); ++ NMSTATE_PASS ++ }, ++ Err(e) => unsafe { ++ *err_msg = ++ CString::new(e.msg().to_string()).unwrap().into_raw(); ++ *err_kind = ++ CString::new(e.kind().to_string()).unwrap().into_raw(); ++ NMSTATE_FAIL ++ }, ++ } ++ } + Err(e) => { + unsafe { + *err_msg = CString::new(e.msg()).unwrap().into_raw(); +@@ -144,7 +165,7 @@ where + } + }; + +- match serde_json::from_str(content_str) { ++ match serde_yaml::from_str(content_str) { + Ok(n) => Some(n), + Err(e) => { + unsafe { +diff --git a/rust/src/clib/query.rs b/rust/src/clib/query.rs +index a24b9c83..12e44d05 100644 +--- a/rust/src/clib/query.rs ++++ b/rust/src/clib/query.rs +@@ -14,6 +14,7 @@ pub(crate) const NMSTATE_FLAG_INCLUDE_SECRETS: u32 = 1 << 4; + pub(crate) const NMSTATE_FLAG_NO_COMMIT: u32 = 1 << 5; + pub(crate) const NMSTATE_FLAG_MEMORY_ONLY: u32 = 1 << 6; + pub(crate) const NMSTATE_FLAG_RUNNING_CONFIG_ONLY: u32 = 1 << 7; ++pub(crate) const NMSTATE_FLAG_YAML_OUTPUT: u32 = 1 << 8; + + #[allow(clippy::not_unsafe_ptr_arg_deref)] + #[no_mangle] +@@ -72,23 +73,37 @@ pub extern "C" fn nmstate_net_state_retrieve( + } + + match result { +- Ok(s) => match serde_json::to_string(&s) { +- Ok(state_str) => unsafe { +- *state = CString::new(state_str).unwrap().into_raw(); +- NMSTATE_PASS +- }, +- Err(e) => unsafe { +- *err_msg = +- CString::new(format!("serde_json::to_string failure: {e}")) +- .unwrap() +- .into_raw(); +- *err_kind = +- CString::new(format!("{}", nmstate::ErrorKind::Bug)) +- .unwrap() +- .into_raw(); +- NMSTATE_FAIL +- }, +- }, ++ Ok(s) => { ++ let serialize = if (flags & NMSTATE_FLAG_YAML_OUTPUT) > 0 { ++ serde_yaml::to_string(&s).map_err(|e| { ++ nmstate::NmstateError::new( ++ nmstate::ErrorKind::Bug, ++ format!("Failed to convert state {s:?} to YAML: {e}"), ++ ) ++ }) ++ } else { ++ serde_json::to_string(&s).map_err(|e| { ++ nmstate::NmstateError::new( ++ nmstate::ErrorKind::Bug, ++ format!("Failed to convert state {s:?} to JSON: {e}"), ++ ) ++ }) ++ }; ++ ++ match serialize { ++ Ok(state_str) => unsafe { ++ *state = CString::new(state_str).unwrap().into_raw(); ++ NMSTATE_PASS ++ }, ++ Err(e) => unsafe { ++ *err_msg = ++ CString::new(e.msg().to_string()).unwrap().into_raw(); ++ *err_kind = ++ CString::new(e.kind().to_string()).unwrap().into_raw(); ++ NMSTATE_FAIL ++ }, ++ } ++ } + Err(e) => { + unsafe { + *err_msg = CString::new(e.msg()).unwrap().into_raw(); +diff --git a/rust/src/clib/test/nmpolicy_test.c b/rust/src/clib/test/nmpolicy_json_test.c +similarity index 96% +rename from rust/src/clib/test/nmpolicy_test.c +rename to rust/src/clib/test/nmpolicy_json_test.c +index 7a71a5f5..8a0444d4 100644 +--- a/rust/src/clib/test/nmpolicy_test.c ++++ b/rust/src/clib/test/nmpolicy_json_test.c +@@ -1,3 +1,6 @@ ++// SPDX-License-Identifier: Apache-2.0 ++ ++#include + #include + #include + #include +@@ -91,6 +94,8 @@ int main(void) { + rc = EXIT_FAILURE; + } + ++ assert(state[0] == '{'); ++ + nmstate_cstring_free(state); + nmstate_cstring_free(err_kind); + nmstate_cstring_free(err_msg); +diff --git a/rust/src/clib/test/nmpolicy_yaml_test.c b/rust/src/clib/test/nmpolicy_yaml_test.c +new file mode 100644 +index 00000000..7984f509 +--- /dev/null ++++ b/rust/src/clib/test/nmpolicy_yaml_test.c +@@ -0,0 +1,80 @@ ++// SPDX-License-Identifier: Apache-2.0 ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(void) { ++ int rc = EXIT_SUCCESS; ++ const char *policy = "\ ++capture:\n\ ++ default-gw: override me with the cache\n\ ++ base-iface: >\n\ ++ interfaces.name == capture.default-gw.routes.running.0.next-hop-interface\n\ ++ base-iface-routes: >\n\ ++ routes.running.next-hop-interface ==\n\ ++ capture.default-gw.routes.running.0.next-hop-interface\n\ ++ bridge-routes: >\n\ ++ capture.base-iface-routes | routes.running.next-hop-interface:=\"br1\"\n\ ++desired:\n\ ++ interfaces:\n\ ++ - name: br1\n\ ++ description: Linux bridge with base interface as a port\n\ ++ type: linux-bridge\n\ ++ state: up\n\ ++ bridge:\n\ ++ options:\n\ ++ stp:\n\ ++ enabled: false\n\ ++ port:\n\ ++ - name: '{{ capture.base-iface.interfaces.0.name }}'\n\ ++ ipv4: '{{ capture.base-iface.interfaces.0.ipv4 }}'\n\ ++ routes:\n\ ++ config: '{{ capture.bridge-routes.routes.running }}'"; ++ const char *current_state = "\ ++interfaces:\n\ ++- name: eth1\n\ ++ type: ethernet\n\ ++ state: up\n\ ++ mac-address: 1c:c1:0c:32:3b:ff\n\ ++ ipv4:\n\ ++ address:\n\ ++ - ip: 192.0.2.251\n\ ++ prefix-length: 24\n\ ++ dhcp: false\n\ ++ enabled: true\n\ ++routes:\n\ ++ config:\n\ ++ - destination: 0.0.0.0/0\n\ ++ next-hop-address: 192.0.2.1\n\ ++ next-hop-interface: eth1\n\ ++ running:\n\ ++ - destination: 0.0.0.0/0\n\ ++ next-hop-address: 192.0.2.1\n\ ++ next-hop-interface: eth1"; ++ char *state = NULL; ++ char *err_kind = NULL; ++ char *err_msg = NULL; ++ char *log = NULL; ++ ++ if (nmstate_net_state_from_policy(policy, current_state, &state, &log, ++ &err_kind, &err_msg) == NMSTATE_PASS) ++ { ++ printf("%s\n", state); ++ } else { ++ printf("%s: %s\n", err_kind, err_msg); ++ rc = EXIT_FAILURE; ++ } ++ ++ assert(state[0] != '{'); ++ ++ nmstate_cstring_free(state); ++ nmstate_cstring_free(err_kind); ++ nmstate_cstring_free(err_msg); ++ nmstate_cstring_free(log); ++ exit(rc); ++} +diff --git a/rust/src/clib/test/nmstate_test.c b/rust/src/clib/test/nmstate_json_test.c +similarity index 87% +rename from rust/src/clib/test/nmstate_test.c +rename to rust/src/clib/test/nmstate_json_test.c +index 0e79cb15..1bfbcda7 100644 +--- a/rust/src/clib/test/nmstate_test.c ++++ b/rust/src/clib/test/nmstate_json_test.c +@@ -1,3 +1,6 @@ ++// SPDX-License-Identifier: Apache-2.0 ++ ++#include + #include + #include + #include +@@ -21,6 +24,8 @@ int main(void) { + rc = EXIT_FAILURE; + } + ++ assert(state[0] == '{'); ++ + nmstate_cstring_free(state); + nmstate_cstring_free(err_kind); + nmstate_cstring_free(err_msg); +diff --git a/rust/src/clib/test/nmstate_yaml_test.c b/rust/src/clib/test/nmstate_yaml_test.c +new file mode 100644 +index 00000000..de0f2486 +--- /dev/null ++++ b/rust/src/clib/test/nmstate_yaml_test.c +@@ -0,0 +1,34 @@ ++// SPDX-License-Identifier: Apache-2.0 ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(void) { ++ int rc = EXIT_SUCCESS; ++ char *state = NULL; ++ char *err_kind = NULL; ++ char *err_msg = NULL; ++ char *log = NULL; ++ uint32_t flag = NMSTATE_FLAG_KERNEL_ONLY | NMSTATE_FLAG_YAML_OUTPUT; ++ ++ if (nmstate_net_state_retrieve(flag, &state, &log, &err_kind, &err_msg) ++ == NMSTATE_PASS) { ++ printf("%s\n", state); ++ } else { ++ printf("%s: %s\n", err_kind, err_msg); ++ rc = EXIT_FAILURE; ++ } ++ ++ assert(state[0] != '{'); ++ ++ nmstate_cstring_free(state); ++ nmstate_cstring_free(err_kind); ++ nmstate_cstring_free(err_msg); ++ nmstate_cstring_free(log); ++ exit(rc); ++} +diff --git a/rust/src/lib/Cargo.toml b/rust/src/lib/Cargo.toml +index a27d0e1a..0142026d 100644 +--- a/rust/src/lib/Cargo.toml ++++ b/rust/src/lib/Cargo.toml +@@ -15,6 +15,9 @@ edition = "2018" + [lib] + path = "lib.rs" + ++[dependencies] ++serde_yaml = "0.9" ++ + [dependencies.nispor] + version = "1.2.9" + optional = true +diff --git a/rust/src/lib/net_state.rs b/rust/src/lib/net_state.rs +index 8ab79642..fe5fea78 100644 +--- a/rust/src/lib/net_state.rs ++++ b/rust/src/lib/net_state.rs +@@ -274,7 +274,19 @@ impl NetworkState { + Ok(s) => Ok(s), + Err(e) => Err(NmstateError::new( + ErrorKind::InvalidArgument, +- format!("Invalid json string: {e}"), ++ format!("Invalid JSON string: {e}"), ++ )), ++ } ++ } ++ ++ /// Wrapping function of [serde_yaml::from_str()] with error mapped to ++ /// [NmstateError]. ++ pub fn new_from_yaml(net_state_yaml: &str) -> Result { ++ match serde_yaml::from_str(net_state_yaml) { ++ Ok(s) => Ok(s), ++ Err(e) => Err(NmstateError::new( ++ ErrorKind::InvalidArgument, ++ format!("Invalid YAML string: {e}"), + )), + } + } +-- +2.39.2 + diff --git a/SOURCES/nmstate-1.3.3.tar.gz.asc b/SOURCES/nmstate-1.3.3.tar.gz.asc deleted file mode 100644 index 4ad5f54..0000000 --- a/SOURCES/nmstate-1.3.3.tar.gz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEfUQ+BAINyWGvqJXerIciWuEsPqMFAmL1I2QACgkQrIciWuEs -PqMfiw//dfPsrRLLyMwEde0SWMEnaHvTwks38OXZN6p8cKk+sWF8DXwmQy5/PAwn -LRj7CrQ7zBsjFBtLmiV8HtT4zpAyt40N1x2IhnPHw8GzGOc6stQZ+SpNf3nDRolU -ambnnfSVN/D2DQN0RNbKDqFVa3+1sLSaygC7SxI0it+pZHRQmz98Q1CTJMQawRby -qG8Rqn93YN9PRxjqihcUN4P+L4rfN3b+hGzzKs1Hfhp6Jcj783JCFGmLNiK+AVjo -MrM17T6ZRtJJAzNLjaPNXKIvOH7hb4smEX5GAb9EDYA4OlH2hwy6J+7zg9eG5ovu -Rs3gpB/iYgEt0UBLpmKWax3ScplU5sIX483wgAq9S5ldciWFbojNYDG1uygqS4WT -KHeBhNP3DmX8T92RTp8Zla44HKedmsgSblTG++xT38SQxT/12N7X+NSp0LqSKCFq -lAplMql7RZDji7BgYOhwTEU9FOEZ6GZnEZAjkWfraw98t2yQnCHhBXNtkhlQPfiK -oZ+vLv7N2peP1c4g+y7yQBCNlehDNp0LFURYoJUVBcHBWkgqJNz7+gv6BBZNVOAh -RdHi/AGzgCKHW1RrNInZmdA1FXc1djeBQbsv72UtgMiDniPMCZbm4Ok8XEwvNNyE -69aTgdst+xhQlklLSyKNergvQ5UYLNZo8GKVdVck2T1w6es3tKo= -=dK0O ------END PGP SIGNATURE----- diff --git a/SOURCES/nmstate-1.4.2.tar.gz.asc b/SOURCES/nmstate-1.4.2.tar.gz.asc new file mode 100644 index 0000000..7f3131a --- /dev/null +++ b/SOURCES/nmstate-1.4.2.tar.gz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEE8f1XsqXpyNthgIbGbM3lj+QeKP8FAmPwSo0ACgkQbM3lj+Qe +KP/P7A/8DLoHZkeuCsDjCYQ5yk0pWSuEqDMZT4Qqk6khBJOtvx5L14oImlrpmOVr +iw3pclF/fl8wxcpPYwcVn2odJWryaZxKw3vNPfth47vEtk3RVZpIqHLsgcCOEcX1 +/czFbA4EJLZV67Qu4gEfrfjbTKEmcakXLI5qUbMDw0nwqx5BI/66iF5kZD4IGJui +MDVbe61wAatcOl5RU8Y0rFI6fz4a9PXVJEijHJC8ZMg/Vq4Q/aBErUZAQ9RKJmi7 +Uu2TLYv4JTMiWX/xspH+CJX7bbaJ9N7P/Z3NzEOKVlDGMmG7TN9QtpnuSF46Jv5K +LTLdr7mBSK3dnQ9vIwcmM8m/MWU2w9dxf7Oh8YCkjwlLk1ANnANHz+xBMx+qIM5e +uNB2iPL2nHTDLFVbOMp1dcZzvndgm3a5oS9L4nglVojvNqGTmkjeJaM5bk80HvxJ +i/onHNmNRnQeHn8xUT7EcBQXUBUCORExygTyJ1dsw+BwbncwV2lOSan7OJY53Os2 +Lm7kHRBtOPeJNK7NbF55eo+N2+1+n9XP7oiCaj//FwWXN6vh97afi7fp5men2MKr +xbBPtYP5BV5LT7/DxKW+AunL2UUtlYoeCI0wm16kDzL8VW1u1Arv5NtdQsSnONk9 +PChrlhPnITjIZ0o2+GrYYOROZPbMSl4oQtO0hTiUub2Vrf3sPIc= +=zl4m +-----END PGP SIGNATURE----- diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec index a658ac7..3eb6f65 100644 --- a/SPECS/nmstate.spec +++ b/SPECS/nmstate.spec @@ -3,8 +3,8 @@ %define libname libnmstate Name: nmstate -Version: 1.3.3 -Release: 8%{?dist} +Version: 1.4.2 +Release: 4%{?dist} Summary: Declarative network manager API License: LGPLv2+ URL: https://github.com/%{srcname}/%{srcname} @@ -12,16 +12,11 @@ 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 -Patch14: BZ_2169642-Fix-sriov.patch -Patch15: BZ_2170078-Introduce-wait-ip.patch -Patch16: BZ_2169642-Fix-SRIOV-2.patch -Patch17: BZ_2169642-Ignore-error-when-creating-profile-if-not-desired.patch +# Patches 0X are reserved to downstream only +Patch0: BZ_2132570-nm-reverse-IPv6-order-before-adding-them-to-setting.patch +Patch11: Enable_clib_yml_api.patch +Patch12: BZ_2160416-fix-SRIOV.patch +Patch13: BZ_2160416-Ignore-error-when-creating-profile-if-not-desired.patch BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: gnupg2 @@ -156,27 +151,32 @@ popd /sbin/ldconfig %changelog -* Mon Feb 27 2023 Gris Ge - 1.3.3-8 -- Ignore error in undesired iface. RHBZ#2169642 +* Mon Feb 27 2023 Gris Ge - 1.4.2-4 +- Ignore undesired iface config. RHBZ#2160416 -* Thu Feb 23 2023 Gris Ge - 1.3.3-7 -- New patch for fixing SR-IOV. RHBZ#2169642 +* Thu Feb 23 2023 Gris Ge - 1.4.2-3 +- Additional patch for SR-IOV. RHBZ#2160416 -* Thu Feb 16 2023 Gris Ge - 1.3.3-6 -- Add Interface wait-ip support. RHBZ#2170078 +* Wed Feb 22 2023 Gris Ge - 1.4.2-2 +- Enable YAML API in rust clib. -* Wed Feb 15 2023 Gris Ge - 1.3.3-5 -- Fix SR-IOV creating VF with IP stack disabled. RHBZ#2169642 +* Sat Feb 18 2023 Gris Ge - 1.4.2-1 +- Upgrade to nmstate 1.4.2 -* Tue Dec 06 2022 Gris Ge - 1.3.3-4 -- Fix activation retry. RHBZ#2150705 +* Mon Jan 09 2023 Gris Ge - 1.4.1-1 +- Upgrade to nmstate-1.4.1 -* Tue Nov 29 2022 Fernando Fernandez Mancera - 1.3.3-3 -- Revert IPv6 addresses order before adding them to IP setting. RHBZ#2149048 +* Wed Dec 14 2022 Gris Ge - 1.4.0-1 +- Upgrade to nmstate-1.4.0 -* 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 +* Thu Dec 01 2022 Fernando Fernandez Mancera - 1.4.0.alpha.20221201 +- Upgrade to nmstate-1.4.0.alpha.20221201 + +* Fri Nov 18 2022 Fernando Fernandez Mancera - 1.3.4.alpha.20221118 +- Upgrade to nmstate-1.3.4.alpha.20221118 + +* Mon Oct 24 2022 Fernando Fernandez Mancera - 1.3.4.alpha.20221024 +- Undo the branching misdone by Fernando. * Mon Aug 15 2022 Gris Ge - 1.3.3-1 - Upgrade to nmstate-1.3.3