import nmstate-1.4.2-4.el8
This commit is contained in:
parent
3250ae7c70
commit
506a948098
4
.gitignore
vendored
4
.gitignore
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,79 +0,0 @@
|
||||
From 316f4fc3333627bcd3aef44c4a469cd6c04360ef Mon Sep 17 00:00:00 2001
|
||||
From: Gris Ge <fge@redhat.com>
|
||||
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 <fge@redhat.com>
|
||||
---
|
||||
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
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 08bf57af942e31a30f7f2c99c6238a3b662cc450 Mon Sep 17 00:00:00 2001
|
||||
From 248cd0bff6e3d030ee72b62a8a8b0e37e9f2ef80 Mon Sep 17 00:00:00 2001
|
||||
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
|
||||
Date: Tue, 29 Nov 2022 22:53:53 +0100
|
||||
Subject: [PATCH] nm: revert IPv6 order before adding them to setting
|
||||
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:
|
||||
@ -14,10 +14,10 @@ Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/libnmstate/nm/ipv6.py b/libnmstate/nm/ipv6.py
|
||||
index 71a80823..27ecf150 100644
|
||||
index 8e01fd70..7eb3196c 100644
|
||||
--- a/libnmstate/nm/ipv6.py
|
||||
+++ b/libnmstate/nm/ipv6.py
|
||||
@@ -174,7 +174,7 @@ def _set_dynamic(setting_ip, is_dhcp, is_autoconf):
|
||||
@@ -157,7 +157,7 @@ def _set_dynamic(setting_ip, is_dhcp, is_autoconf):
|
||||
|
||||
|
||||
def _set_static(setting_ip, ip_addresses):
|
@ -1,54 +0,0 @@
|
||||
From 2786e426173ed4a930dca23e18756123fc9b0e3a Mon Sep 17 00:00:00 2001
|
||||
From: Gris Ge <fge@redhat.com>
|
||||
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 <fge@redhat.com>
|
||||
---
|
||||
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
|
||||
|
@ -1,64 +0,0 @@
|
||||
From 2d0cfd5ad8e049f30cad10d977a5fae8bc4e6b64 Mon Sep 17 00:00:00 2001
|
||||
From: Gris Ge <fge@redhat.com>
|
||||
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 <fge@redhat.com>
|
||||
---
|
||||
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
|
||||
|
@ -1,151 +0,0 @@
|
||||
From 2a98b06c70c93c63298ac0cc5402a74d8015f40b Mon Sep 17 00:00:00 2001
|
||||
From: Gris Ge <fge@redhat.com>
|
||||
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 <fge@redhat.com>
|
||||
---
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
-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 <fge@redhat.com>
|
||||
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 <fge@redhat.com>
|
||||
---
|
||||
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
From 030bd5e38a2913e96ef145f88cb74c619acea6bf Mon Sep 17 00:00:00 2001
|
||||
From d7d732332e486cd8969ff4b5ef95a24cb68b5441 Mon Sep 17 00:00:00 2001
|
||||
From: Gris Ge <fge@redhat.com>
|
||||
Date: Mon, 27 Feb 2023 12:17:05 +0800
|
||||
Subject: [PATCH] nm: Ignore error when creating profile if not desired
|
@ -1,4 +1,4 @@
|
||||
From 12e298f27f1ffa58f6f7e60016ff197719b7a26e Mon Sep 17 00:00:00 2001
|
||||
From d410b928c8f2a22d42d1974b62ab5b3164861184 Mon Sep 17 00:00:00 2001
|
||||
From: Gris Ge <fge@redhat.com>
|
||||
Date: Thu, 23 Feb 2023 13:06:01 +0800
|
||||
Subject: [PATCH] nm: Fix error on SR-IOV
|
File diff suppressed because it is too large
Load Diff
@ -1,208 +0,0 @@
|
||||
From ef23275126898f316cb3e7e2df552c006e867105 Mon Sep 17 00:00:00 2001
|
||||
From: Gris Ge <fge@redhat.com>
|
||||
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 <fge@redhat.com>
|
||||
---
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
-#
|
||||
+# 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
|
||||
|
636
SOURCES/Enable_clib_yml_api.patch
Normal file
636
SOURCES/Enable_clib_yml_api.patch
Normal file
@ -0,0 +1,636 @@
|
||||
From ad2bfa136290e72cdfd4b7877b49b3fc07203f9c Mon Sep 17 00:00:00 2001
|
||||
From: Gris Ge <fge@redhat.com>
|
||||
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 <fge@redhat.com>
|
||||
---
|
||||
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::<serde_json::Value>(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::<serde_json::Value>(policy_str).is_ok()
|
||||
+ } else {
|
||||
+ false
|
||||
+ };
|
||||
+
|
||||
let mut policy = match deserilize_from_c_char::<NetworkPolicy>(
|
||||
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 <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -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 <assert.h>
|
||||
+#include <stddef.h>
|
||||
+#include <stdint.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+#include <nmstate.h>
|
||||
+
|
||||
+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 <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -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 <assert.h>
|
||||
+#include <stddef.h>
|
||||
+#include <stdint.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+#include <nmstate.h>
|
||||
+
|
||||
+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<Self, NmstateError> {
|
||||
+ 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
|
||||
|
@ -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-----
|
16
SOURCES/nmstate-1.4.2.tar.gz.asc
Normal file
16
SOURCES/nmstate-1.4.2.tar.gz.asc
Normal file
@ -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-----
|
@ -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 <fge@redhat.com> - 1.3.3-8
|
||||
- Ignore error in undesired iface. RHBZ#2169642
|
||||
* Mon Feb 27 2023 Gris Ge <fge@redhat.com> - 1.4.2-4
|
||||
- Ignore undesired iface config. RHBZ#2160416
|
||||
|
||||
* Thu Feb 23 2023 Gris Ge <fge@redhat.com> - 1.3.3-7
|
||||
- New patch for fixing SR-IOV. RHBZ#2169642
|
||||
* Thu Feb 23 2023 Gris Ge <fge@redhat.com> - 1.4.2-3
|
||||
- Additional patch for SR-IOV. RHBZ#2160416
|
||||
|
||||
* Thu Feb 16 2023 Gris Ge <fge@redhat.com> - 1.3.3-6
|
||||
- Add Interface wait-ip support. RHBZ#2170078
|
||||
* Wed Feb 22 2023 Gris Ge <fge@redhat.com> - 1.4.2-2
|
||||
- Enable YAML API in rust clib.
|
||||
|
||||
* Wed Feb 15 2023 Gris Ge <fge@redhat.com> - 1.3.3-5
|
||||
- Fix SR-IOV creating VF with IP stack disabled. RHBZ#2169642
|
||||
* Sat Feb 18 2023 Gris Ge <fge@redhat.com> - 1.4.2-1
|
||||
- Upgrade to nmstate 1.4.2
|
||||
|
||||
* Tue Dec 06 2022 Gris Ge <fge@redhat.com> - 1.3.3-4
|
||||
- Fix activation retry. RHBZ#2150705
|
||||
* Mon Jan 09 2023 Gris Ge <fge@redhat.com> - 1.4.1-1
|
||||
- Upgrade to nmstate-1.4.1
|
||||
|
||||
* Tue Nov 29 2022 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.3.3-3
|
||||
- Revert IPv6 addresses order before adding them to IP setting. RHBZ#2149048
|
||||
* Wed Dec 14 2022 Gris Ge <fge@redhat.com> - 1.4.0-1
|
||||
- Upgrade to nmstate-1.4.0
|
||||
|
||||
* Sat Nov 12 2022 Fernando Fernandez Mancera <ferferna@redhat.com> - 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 <ferferna@redhat.com> - 1.4.0.alpha.20221201
|
||||
- Upgrade to nmstate-1.4.0.alpha.20221201
|
||||
|
||||
* Fri Nov 18 2022 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.3.4.alpha.20221118
|
||||
- Upgrade to nmstate-1.3.4.alpha.20221118
|
||||
|
||||
* Mon Oct 24 2022 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.3.4.alpha.20221024
|
||||
- Undo the branching misdone by Fernando.
|
||||
|
||||
* Mon Aug 15 2022 Gris Ge <fge@redhat.com> - 1.3.3-1
|
||||
- Upgrade to nmstate-1.3.3
|
||||
|
Loading…
Reference in New Issue
Block a user