import nmstate-1.0.2-5.el8

This commit is contained in:
CentOS Sources 2021-05-18 02:55:08 -04:00 committed by Andrew Lukoshko
parent a764ed7069
commit 8c0f309116
33 changed files with 481 additions and 2717 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
SOURCES/nmstate-0.3.4.tar.gz SOURCES/nmstate-1.0.2.tar.gz
SOURCES/nmstate.gpg SOURCES/nmstate.gpg

View File

@ -1,2 +1,2 @@
d732a1ccb1dfc54741a9d602179c809c3223af3a SOURCES/nmstate-0.3.4.tar.gz eeda8a0238732e5dc37e2217ed6e316f76c93145 SOURCES/nmstate-1.0.2.tar.gz
b5f872551d434e2c62b30d70471efaeede83ab44 SOURCES/nmstate.gpg b5f872551d434e2c62b30d70471efaeede83ab44 SOURCES/nmstate.gpg

View File

@ -1,40 +0,0 @@
From 862e669fcfe02b49c0e24af210d6466197962b97 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Mon, 3 Aug 2020 11:34:44 +0800
Subject: [PATCH] ovs: Fix bug when adding bond to existing bridge
When adding OVS bond/link aggregation interface to existing OVS bridge,
nmstate will fail with error:
> self._ifaces[slave_name].mark_as_changed()
E KeyError: 'bond1'
This is because ovs bond interface does not require a interface entry in
desire state.
Fixed by check before adding dict.
Integration test case added.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/ifaces/ifaces.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index a400712..1c2ffd5 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -217,7 +217,8 @@ class Ifaces:
self._ifaces[iface_name].mark_as_changed()
if cur_iface:
for slave_name in iface.config_changed_slaves(cur_iface):
- self._ifaces[slave_name].mark_as_changed()
+ if slave_name in self._ifaces:
+ self._ifaces[slave_name].mark_as_changed()
def _match_child_iface_state_with_parent(self):
"""
--
2.28.0

View File

@ -1,105 +0,0 @@
From bc2f8445d493f8a5a4ff1ceead13d2b3ac5325cc Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Sun, 26 Jul 2020 00:46:16 +0200
Subject: [PATCH 1/2] nm.wired: do not report MTU if it is 0
If an interface contains an MTU with value 0, Nmstate should not report
it because it is an special interface like OVS patch port interfaces.
Added a test case for this.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/nm/wired.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/libnmstate/nm/wired.py b/libnmstate/nm/wired.py
index 27d4318..64662ac 100644
--- a/libnmstate/nm/wired.py
+++ b/libnmstate/nm/wired.py
@@ -124,7 +124,9 @@ def get_info(device):
iface = device.get_iface()
try:
- info[Interface.MTU] = int(device.get_mtu())
+ mtu = int(device.get_mtu())
+ if mtu:
+ info[Interface.MTU] = mtu
except AttributeError:
pass
--
2.27.0
From 03aea7d7debfca0f01b60e9f406c9acdf3de3775 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Mon, 27 Jul 2020 20:51:53 +0800
Subject: [PATCH 2/2] nm ovs: Raise NmstateNotSupportedError for
save_to_disk=False
Due to limitation of NetworkManager 1.26, nmstate cannot support
`save_to_disk=False`(ask, memory only) state for OVS interfaces.
Raise NmstateNotSupportedError if NetworkManager version is older than
1.28 and has OVS interface in desire state with `save_to_disk=False`.
Integration test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/nm/applier.py | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index 4e20af5..a91cee5 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -17,9 +17,11 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
+from distutils.version import StrictVersion
import logging
import itertools
+from libnmstate.error import NmstateNotSupportedError
from libnmstate.error import NmstateValueError
from libnmstate.schema import Interface
from libnmstate.schema import InterfaceState
@@ -65,6 +67,17 @@ MASTER_IFACE_TYPES = (
def apply_changes(context, net_state, save_to_disk):
con_profiles = []
+ if (
+ not save_to_disk
+ and _has_ovs_interface_desired_or_changed(net_state)
+ and StrictVersion(context.client.get_version())
+ < StrictVersion("1.28.0")
+ ):
+ raise NmstateNotSupportedError(
+ f"NetworkManager version {context.client.get_version()} does not "
+ f"support 'save_to_disk=False' against OpenvSwitch interface"
+ )
+
_preapply_dns_fix(context, net_state)
ifaces_desired_state = net_state.ifaces.state_to_edit
@@ -602,3 +615,13 @@ def _preapply_dns_fix(context, net_state):
for iface in net_state.ifaces.values():
if iface.is_changed or iface.is_desired:
iface.remove_dns_metadata()
+
+
+def _has_ovs_interface_desired_or_changed(net_state):
+ for iface in net_state.ifaces.values():
+ if iface.type in (
+ InterfaceType.OVS_BRIDGE,
+ InterfaceType.OVS_INTERFACE,
+ InterfaceType.OVS_PORT,
+ ) and (iface.is_desired or iface.is_changed):
+ return True
--
2.27.0

View File

@ -1,59 +0,0 @@
From fc7e6b2329409b95ab1726b7b65f14c284bf67ab Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Mon, 3 Aug 2020 12:07:56 +0800
Subject: [PATCH] nm: Fix converting memory-only profile to persistent
When converting memory-only profile to persistent using simple desire
state with `state: up` only, the memory only profile will not be
converted to persistent.
This is caused by `nm/applier.py` skip profile creation if desire state
is only `state: up`.
The fix is checking whether current profile's persistent state is equal
to desired.
Integration test case added.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/applier.py | 1 +
libnmstate/nm/connection.py | 10 ++++++++++
2 files changed, 11 insertions(+)
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index 4d40862..26a057f 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -125,6 +125,7 @@ def apply_changes(context, net_state, save_to_disk):
and cur_con_profile
and cur_con_profile.profile
and not net_state.ifaces[ifname].is_changed
+ and cur_con_profile.is_memory_only != save_to_disk
):
# Don't create new profile if original desire does not ask
# anything besides state:up and not been marked as changed.
diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
index 5804f13..1f6c734 100644
--- a/libnmstate/nm/connection.py
+++ b/libnmstate/nm/connection.py
@@ -162,6 +162,16 @@ class ConnectionProfile:
assert self._con_profile is None
self._con_profile = con_profile
+ @property
+ def is_memory_only(self):
+ if self._con_profile:
+ profile_flags = self._con_profile.get_flags()
+ return (
+ NM.SettingsConnectionFlags.UNSAVED & profile_flags
+ or NM.SettingsConnectionFlags.VOLATILE & profile_flags
+ )
+ return False
+
@property
def devname(self):
if self._con_profile:
--
2.28.0

View File

@ -1,65 +0,0 @@
From ea7f304cc1ad32c3f2c25b49bf6b2663f348496a Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Tue, 28 Jul 2020 15:59:11 +0800
Subject: [PATCH] nm: Mark external subordinate as changed
When user create bond with subordinate interfaces using non-NM
tools(iproute), the NetworkManager will mark the subordinates as
managed externally.
When the desire state only contains the main interface, nmstate
noticing the slave list is unchanged, so only activate the main
interface, then NM remove the subordinate from their main interface.
To workaround that, mark subordinate interfaces as changed when they are
managed by NM as externally.
Integration test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/applier.py | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index a91cee5..68d11dc 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -79,6 +79,7 @@ def apply_changes(context, net_state, save_to_disk):
)
_preapply_dns_fix(context, net_state)
+ _mark_nm_external_subordinate_changed(context, net_state)
ifaces_desired_state = net_state.ifaces.state_to_edit
ifaces_desired_state.extend(
@@ -625,3 +626,26 @@ def _has_ovs_interface_desired_or_changed(net_state):
InterfaceType.OVS_PORT,
) and (iface.is_desired or iface.is_changed):
return True
+
+
+def _mark_nm_external_subordinate_changed(context, net_state):
+ """
+ When certain main interface contains subordinates is marked as
+ connected(externally), it means its profile is memory only and will lost
+ on next deactivation.
+ For this case, we should mark the subordinate as changed.
+ that subordinate should be marked as changed for NM to take over.
+ """
+ for iface in net_state.ifaces.values():
+ if iface.type in MASTER_IFACE_TYPES:
+ for subordinate in iface.slaves:
+ nmdev = context.get_nm_dev(subordinate)
+ if nmdev:
+ nm_ac = nmdev.get_active_connection()
+ if (
+ nm_ac
+ and NM.ActivationStateFlags.EXTERNAL
+ & nm_ac.get_state_flags()
+ ):
+ subordinate_iface = net_state.ifaces[subordinate]
+ subordinate_iface.mark_as_changed()
--
2.27.0

View File

@ -1,200 +0,0 @@
From a8590744fbdd4bce9ab340ac49a7add31727b990 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Wed, 29 Jul 2020 17:51:27 +0800
Subject: [PATCH 1/3] nm applier: Fix external managed interface been marked as
changed
The net_state passing to `_mark_nm_external_subordinate_changed()`
does not contain unknown type interface, so the
`net_state.ifaces[subordinate]` could trigger KeyError as subordinate
not found.
Changed it to use `get()` and only perform this changes to
desired/changed interface.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/applier.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index 68d11dc..d0fb5f3 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -637,7 +637,7 @@ def _mark_nm_external_subordinate_changed(context, net_state):
that subordinate should be marked as changed for NM to take over.
"""
for iface in net_state.ifaces.values():
- if iface.type in MASTER_IFACE_TYPES:
+ if iface.is_desired or iface.is_changed and iface.is_master:
for subordinate in iface.slaves:
nmdev = context.get_nm_dev(subordinate)
if nmdev:
@@ -647,5 +647,6 @@ def _mark_nm_external_subordinate_changed(context, net_state):
and NM.ActivationStateFlags.EXTERNAL
& nm_ac.get_state_flags()
):
- subordinate_iface = net_state.ifaces[subordinate]
- subordinate_iface.mark_as_changed()
+ subordinate_iface = net_state.ifaces.get(subordinate)
+ if subordinate_iface:
+ subordinate_iface.mark_as_changed()
--
2.28.0
From 77a05cfe726efc4a4207d57958a71e1730b6d62a Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Wed, 29 Jul 2020 18:02:52 +0800
Subject: [PATCH 2/3] nm: Ignore externally managed interface for down/absent
main interface
When main interface been marked as down or absent, its subordinate
should be also marked as so. NetworkManager plugin should ignore
externally managed subordinate in this case.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/applier.py | 26 +++++++++++---------------
libnmstate/nm/device.py | 8 ++++++++
2 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index d0fb5f3..9cd8f9a 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -50,6 +50,7 @@ from . import vxlan
from . import wired
from .common import NM
from .dns import get_dns_config_iface_names
+from .device import is_externally_managed
MAXIMUM_INTERFACE_LENGTH = 15
@@ -265,13 +266,14 @@ def _set_ifaces_admin_state(context, ifaces_desired_state, con_profiles):
== InterfaceState.ABSENT
)
for affected_nmdev in nmdevs:
- devs_to_deactivate[
- affected_nmdev.get_iface()
- ] = affected_nmdev
- if is_absent:
- devs_to_delete_profile[
+ if not is_externally_managed(affected_nmdev):
+ devs_to_deactivate[
affected_nmdev.get_iface()
] = affected_nmdev
+ if is_absent:
+ devs_to_delete_profile[
+ affected_nmdev.get_iface()
+ ] = affected_nmdev
if (
is_absent
and nmdev.is_software()
@@ -640,13 +642,7 @@ def _mark_nm_external_subordinate_changed(context, net_state):
if iface.is_desired or iface.is_changed and iface.is_master:
for subordinate in iface.slaves:
nmdev = context.get_nm_dev(subordinate)
- if nmdev:
- nm_ac = nmdev.get_active_connection()
- if (
- nm_ac
- and NM.ActivationStateFlags.EXTERNAL
- & nm_ac.get_state_flags()
- ):
- subordinate_iface = net_state.ifaces.get(subordinate)
- if subordinate_iface:
- subordinate_iface.mark_as_changed()
+ if nmdev and is_externally_managed(nmdev):
+ subordinate_iface = net_state.ifaces.get(subordinate)
+ if subordinate_iface:
+ subordinate_iface.mark_as_changed()
diff --git a/libnmstate/nm/device.py b/libnmstate/nm/device.py
index 528f57d..a175b71 100644
--- a/libnmstate/nm/device.py
+++ b/libnmstate/nm/device.py
@@ -23,6 +23,7 @@ from libnmstate.error import NmstateLibnmError
from . import active_connection as ac
from . import connection
+from .common import NM
def activate(context, dev=None, connection_id=None):
@@ -161,3 +162,10 @@ def get_device_common_info(dev):
"type_name": dev.get_type_description(),
"state": dev.get_state(),
}
+
+
+def is_externally_managed(nmdev):
+ nm_ac = nmdev.get_active_connection()
+ return (
+ nm_ac and NM.ActivationStateFlags.EXTERNAL & nm_ac.get_state_flags()
+ )
--
2.28.0
From afb51e8421b8749962dd9ee2e31b61548de09a78 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Wed, 29 Jul 2020 18:22:32 +0800
Subject: [PATCH 3/3] state: Remove unmanaged interface before verifying
Since we remove unknown type interface before sending to apply,
we should also remove unknown type interface before verifying.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/ifaces/ifaces.py | 9 +++++----
libnmstate/nm/device.py | 4 +---
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 1ff4198..a400712 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -286,16 +286,16 @@ class Ifaces:
def cur_ifaces(self):
return self._cur_ifaces
- def _remove_unmanaged_slaves(self):
+ def _remove_unknown_interface_type_slaves(self):
"""
- When master containing unmanaged slaves, they should be removed from
- master slave list.
+ When master containing slaves with unknown interface type, they should
+ be removed from master slave list before verifying.
"""
for iface in self._ifaces.values():
if iface.is_up and iface.is_master and iface.slaves:
for slave_name in iface.slaves:
slave_iface = self._ifaces[slave_name]
- if not slave_iface.is_up:
+ if slave_iface.type == InterfaceType.UNKNOWN:
iface.remove_slave(slave_name)
def verify(self, cur_iface_infos):
@@ -304,6 +304,7 @@ class Ifaces:
cur_iface_infos=cur_iface_infos,
save_to_disk=self._save_to_disk,
)
+ cur_ifaces._remove_unknown_interface_type_slaves()
for iface in self._ifaces.values():
if iface.is_desired:
if iface.is_virtual and iface.original_dict.get(
diff --git a/libnmstate/nm/device.py b/libnmstate/nm/device.py
index a175b71..fdf05bc 100644
--- a/libnmstate/nm/device.py
+++ b/libnmstate/nm/device.py
@@ -166,6 +166,4 @@ def get_device_common_info(dev):
def is_externally_managed(nmdev):
nm_ac = nmdev.get_active_connection()
- return (
- nm_ac and NM.ActivationStateFlags.EXTERNAL & nm_ac.get_state_flags()
- )
+ return nm_ac and NM.ActivationStateFlags.EXTERNAL & nm_ac.get_state_flags()
--
2.28.0

View File

@ -1,68 +0,0 @@
From 3c5337a22273717df6fb51818216816bbff77035 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Thu, 30 Jul 2020 16:54:40 +0800
Subject: [PATCH] nm profile: Remove inactivate profile of desired interface
For changed/desired interface, NM should remove all its inactive
profiles so that it could update and activate the same one.
Integration test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/applier.py | 10 +++++++++-
libnmstate/nm/connection.py | 9 +++++++--
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index 9cd8f9a..4d40862 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -105,6 +105,15 @@ def apply_changes(context, net_state, save_to_disk):
cur_con_profile = connection.ConnectionProfile(
context, profile=con_profile
)
+
+ if save_to_disk:
+ # TODO: Need handle save_to_disk=False
+ connection.delete_iface_profiles_except(
+ context,
+ ifname,
+ cur_con_profile.profile if cur_con_profile else None,
+ )
+
original_desired_iface_state = {}
if net_state.ifaces.get(ifname):
iface = net_state.ifaces[ifname]
@@ -137,7 +146,6 @@ def apply_changes(context, net_state, save_to_disk):
con_profiles.append(new_con_profile)
else:
# Missing connection, attempting to create a new one.
- connection.delete_iface_inactive_connections(context, ifname)
new_con_profile.add(save_to_disk)
con_profiles.append(new_con_profile)
context.wait_all_finish()
diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
index 02890bc..5804f13 100644
--- a/libnmstate/nm/connection.py
+++ b/libnmstate/nm/connection.py
@@ -496,9 +496,14 @@ def get_device_active_connection(nm_device):
return active_conn
-def delete_iface_inactive_connections(context, ifname):
+def delete_iface_profiles_except(context, ifname, excluded_profile):
for con in list_connections_by_ifname(context, ifname):
- con.delete()
+ if (
+ not excluded_profile
+ or not con.profile
+ or con.profile.get_uuid() != excluded_profile.get_uuid()
+ ):
+ con.delete()
def list_connections_by_ifname(context, ifname):
--
2.28.0

View File

@ -1,120 +0,0 @@
From 21e06fd5af76cc1fe65497222a04c1cffa2bc546 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Thu, 6 Aug 2020 13:07:59 +0800
Subject: [PATCH] ovsdb: Preserve the NM external_ids
For newly created OVS internal interface with customer external_ids,
the ovsdb plugin will remove the NM external_ids `NM.connection.uuid`.
The fix is read the current `NM.connection.uuid` before applying
configure and merge it with original desired state.
Integration test cases added.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/plugins/nmstate_plugin_ovsdb.py | 69 +++++++++++++---------
1 file changed, 40 insertions(+), 29 deletions(-)
diff --git a/libnmstate/plugins/nmstate_plugin_ovsdb.py b/libnmstate/plugins/nmstate_plugin_ovsdb.py
index 83965e1..12ab10d 100644
--- a/libnmstate/plugins/nmstate_plugin_ovsdb.py
+++ b/libnmstate/plugins/nmstate_plugin_ovsdb.py
@@ -161,7 +161,14 @@ class NmstateOvsdbPlugin(NmstatePlugin):
return ifaces
def apply_changes(self, net_state, save_to_disk):
+ # State might changed after other plugin invoked apply_changes()
self.refresh_content()
+ cur_iface_to_ext_ids = {}
+ for iface_info in self.get_interfaces():
+ cur_iface_to_ext_ids[iface_info[Interface.NAME]] = iface_info[
+ OvsDB.OVS_DB_SUBTREE
+ ][OvsDB.EXTERNAL_IDS]
+
pending_changes = []
for iface in net_state.ifaces.values():
if not iface.is_changed and not iface.is_desired:
@@ -174,7 +181,34 @@ class NmstateOvsdbPlugin(NmstatePlugin):
table_name = "Interface"
else:
continue
- pending_changes.extend(_generate_db_change(table_name, iface))
+ ids_after_nm_applied = cur_iface_to_ext_ids.get(iface.name, {})
+ ids_before_nm_applied = (
+ iface.to_dict()
+ .get(OvsDB.OVS_DB_SUBTREE, {})
+ .get(OvsDB.EXTERNAL_IDS, {})
+ )
+ original_desire_ids = iface.original_dict.get(
+ OvsDB.OVS_DB_SUBTREE, {}
+ ).get(OvsDB.EXTERNAL_IDS)
+
+ desire_ids = []
+
+ if original_desire_ids is None:
+ desire_ids = ids_before_nm_applied
+ else:
+ desire_ids = original_desire_ids
+
+ # should include external_id created by NetworkManager.
+ if NM_EXTERNAL_ID in ids_after_nm_applied:
+ desire_ids[NM_EXTERNAL_ID] = ids_after_nm_applied[
+ NM_EXTERNAL_ID
+ ]
+ if desire_ids != ids_after_nm_applied:
+ pending_changes.append(
+ _generate_db_change_external_ids(
+ table_name, iface.name, desire_ids
+ )
+ )
if pending_changes:
if not save_to_disk:
raise NmstateNotImplementedError(
@@ -242,38 +276,15 @@ class NmstateOvsdbPlugin(NmstatePlugin):
)
-def _generate_db_change(table_name, iface_state):
- return _generate_db_change_external_ids(table_name, iface_state)
-
-
-def _generate_db_change_external_ids(table_name, iface_state):
- pending_changes = []
- desire_ids = iface_state.original_dict.get(OvsDB.OVS_DB_SUBTREE, {}).get(
- OvsDB.EXTERNAL_IDS
- )
+def _generate_db_change_external_ids(table_name, iface_name, desire_ids):
if desire_ids and not isinstance(desire_ids, dict):
raise NmstateValueError("Invalid external_ids, should be dictionary")
- if desire_ids or desire_ids == {}:
- # should include external_id required by NetworkManager.
- merged_ids = (
- iface_state.to_dict()
- .get(OvsDB.OVS_DB_SUBTREE, {})
- .get(OvsDB.EXTERNAL_IDS, {})
- )
- if NM_EXTERNAL_ID in merged_ids:
- desire_ids[NM_EXTERNAL_ID] = merged_ids[NM_EXTERNAL_ID]
-
- # Convert all value to string
- for key, value in desire_ids.items():
- desire_ids[key] = str(value)
+ # Convert all value to string
+ for key, value in desire_ids.items():
+ desire_ids[key] = str(value)
- pending_changes.append(
- _Changes(
- table_name, OvsDB.EXTERNAL_IDS, iface_state.name, desire_ids
- )
- )
- return pending_changes
+ return _Changes(table_name, OvsDB.EXTERNAL_IDS, iface_name, desire_ids)
NMSTATE_PLUGIN = NmstateOvsdbPlugin
--
2.28.0

View File

@ -1,44 +0,0 @@
From 913b739c8fea8e9b14d3785371c8e4f48723dbd6 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Tue, 18 Aug 2020 17:55:12 +0800
Subject: [PATCH] ovsdb: Allowing remove all ports from OVS bridge
When removing all ports from OVS bridge, the OVSDB will have no
information regarding this bridge, which cause OVSDB failed to find
the correct row.
Silently ignore row not found failure and let verification stage do the
work.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/plugins/nmstate_plugin_ovsdb.py | 8 --------
1 file changed, 8 deletions(-)
diff --git a/libnmstate/plugins/nmstate_plugin_ovsdb.py b/libnmstate/plugins/nmstate_plugin_ovsdb.py
index 12ab10d..f667e8f 100644
--- a/libnmstate/plugins/nmstate_plugin_ovsdb.py
+++ b/libnmstate/plugins/nmstate_plugin_ovsdb.py
@@ -222,19 +222,11 @@ class NmstateOvsdbPlugin(NmstatePlugin):
def _db_write(self, changes):
changes_index = {change.row_name: change for change in changes}
changed_tables = set(change.table_name for change in changes)
- updated_names = []
for changed_table in changed_tables:
for row in self._idl.tables[changed_table].rows.values():
if row.name in changes_index:
change = changes_index[row.name]
setattr(row, change.column_name, change.column_value)
- updated_names.append(change.row_name)
- new_rows = set(changes_index.keys()) - set(updated_names)
- if new_rows:
- raise NmstatePluginError(
- f"BUG: row {new_rows} does not exists in OVS DB "
- "and currently we don't create new row"
- )
def _start_transaction(self):
self._transaction = Transaction(self._idl)
--
2.28.0

View File

@ -1,111 +0,0 @@
From 36ee761dd0d671439323077e4f77a89071fdcd9c Mon Sep 17 00:00:00 2001
From: Edward Haas <edwardh@redhat.com>
Date: Wed, 7 Oct 2020 20:13:12 +0300
Subject: [PATCH 1/2] nm, bridge, ovs: Collect only existing profiles
During the reporting flow, connections that are in teardown process
no longer point to a valid profile. Avoid collecting such profiles (in
practice, these are actually `None` objects).
Signed-off-by: Edward Haas <edwardh@redhat.com>
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/bridge.py | 6 +++---
libnmstate/nm/ovs.py | 4 +++-
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/libnmstate/nm/bridge.py b/libnmstate/nm/bridge.py
index b885f7a..0ca6c2d 100644
--- a/libnmstate/nm/bridge.py
+++ b/libnmstate/nm/bridge.py
@@ -260,9 +260,9 @@ def _get_slave_profiles_by_name(master_device):
for dev in master_device.get_slaves():
active_con = connection.get_device_active_connection(dev)
if active_con:
- slaves_profiles_by_name[
- dev.get_iface()
- ] = active_con.props.connection
+ profile = active_con.props.connection
+ if profile:
+ slaves_profiles_by_name[dev.get_iface()] = profile
return slaves_profiles_by_name
diff --git a/libnmstate/nm/ovs.py b/libnmstate/nm/ovs.py
index 2518773..eb373c3 100644
--- a/libnmstate/nm/ovs.py
+++ b/libnmstate/nm/ovs.py
@@ -279,5 +279,7 @@ def _get_slave_profiles(master_device, devices_info):
if active_con:
master = active_con.props.master
if master and (master.get_iface() == master_device.get_iface()):
- slave_profiles.append(active_con.props.connection)
+ profile = active_con.props.connection
+ if profile:
+ slave_profiles.append(profile)
return slave_profiles
--
2.28.0
From caf638d75e57da8770cd884782475f1c5668fd6d Mon Sep 17 00:00:00 2001
From: Edward Haas <edwardh@redhat.com>
Date: Wed, 7 Oct 2020 12:26:42 +0300
Subject: [PATCH 2/2] nm, ovs: Fix report crash when OVS has dup iface names
In case of an existing OVS deployment which uses an identical name for
the bridge, port and interface, libnmstate.show() exploded.
It is now possible to report such deployments.
Signed-off-by: Edward Haas <edwardh@redhat.com>
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/ovs.py | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/libnmstate/nm/ovs.py b/libnmstate/nm/ovs.py
index eb373c3..d1f26ba 100644
--- a/libnmstate/nm/ovs.py
+++ b/libnmstate/nm/ovs.py
@@ -140,7 +140,12 @@ def get_port_by_slave(nmdev):
def get_ovs_info(context, bridge_device, devices_info):
- port_profiles = _get_slave_profiles(bridge_device, devices_info)
+ ovs_ports_info = (
+ info
+ for info in devices_info
+ if is_ovs_port_type_id(info[1]["type_id"])
+ )
+ port_profiles = _get_slave_profiles(bridge_device, ovs_ports_info)
ports = _get_bridge_ports_info(context, port_profiles, devices_info)
options = _get_bridge_options(context, bridge_device)
@@ -203,8 +208,21 @@ def _get_bridge_port_info(context, port_profile, devices_info):
vlan_mode = port_setting.props.vlan_mode
port_name = port_profile.get_interface_name()
- port_device = context.get_nm_dev(port_name)
- port_slave_profiles = _get_slave_profiles(port_device, devices_info)
+ port_device = next(
+ dev
+ for dev, devinfo in devices_info
+ if devinfo["name"] == port_name
+ and is_ovs_port_type_id(devinfo["type_id"])
+ )
+ devices_info_excluding_bridges_and_ports = (
+ info
+ for info in devices_info
+ if not is_ovs_bridge_type_id(info[1]["type_id"])
+ and not is_ovs_port_type_id(info[1]["type_id"])
+ )
+ port_slave_profiles = _get_slave_profiles(
+ port_device, devices_info_excluding_bridges_and_ports
+ )
port_slave_names = [c.get_interface_name() for c in port_slave_profiles]
if port_slave_names:
--
2.28.0

View File

@ -1,49 +0,0 @@
From 8a7f1758da4cba81d65ba4b9b06bbf4b750a6f87 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Thu, 22 Oct 2020 14:09:27 +0800
Subject: [PATCH 1/2] nm bond: Ignore ad_actor_system=00:00:00:00:00:00
The ad_actor_system=00:00:00:00:00:00 is invalid in kernel as that's the
default value of ad_actor_system.
NM plugin should not set that value.
Test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/nm/bond.py | 9 +++++++++
tests/integration/nm/bond_test.py | 12 ++++++++++++
2 files changed, 21 insertions(+)
diff --git a/libnmstate/nm/bond.py b/libnmstate/nm/bond.py
index 9ea3648..d196965 100644
--- a/libnmstate/nm/bond.py
+++ b/libnmstate/nm/bond.py
@@ -38,6 +38,8 @@ NM_SUPPORTED_BOND_OPTIONS = NM.SettingBond.get_valid_options(
SYSFS_BOND_OPTION_FOLDER_FMT = "/sys/class/net/{ifname}/bonding"
+BOND_AD_ACTOR_SYSTEM_USE_BOND_MAC = "00:00:00:00:00:00"
+
def create_setting(options, wired_setting):
bond_setting = NM.SettingBond.new()
@@ -48,6 +50,13 @@ def create_setting(options, wired_setting):
):
# When in MAC restricted mode, MAC address should be unset.
wired_setting.props.cloned_mac_address = None
+ if (
+ option_name == "ad_actor_system"
+ and option_value == BOND_AD_ACTOR_SYSTEM_USE_BOND_MAC
+ ):
+ # The all zero ad_actor_system is the kernel default value
+ # And it is invalid to set as all zero
+ continue
if option_value != SYSFS_EMPTY_VALUE:
success = bond_setting.add_option(option_name, str(option_value))
if not success:
--
2.25.4

View File

@ -1,33 +0,0 @@
From 46104725c121def5d85f492afd91e618c1c7c240 Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Tue, 27 Oct 2020 12:23:18 +0100
Subject: [PATCH 2/2] nm.ipv6: call clear_routing_rules() when creating the
setting
In order to remove IPv6 routing rules Nmstate needs to call
clear_routing_rules() on the IPv6 setting as it is done on the IPv4
setting.
Added a testcase for this.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/nm/ipv6.py | 1 +
tests/integration/route_test.py | 15 +++++++++++++++
2 files changed, 16 insertions(+)
diff --git a/libnmstate/nm/ipv6.py b/libnmstate/nm/ipv6.py
index f252578..9777c89 100644
--- a/libnmstate/nm/ipv6.py
+++ b/libnmstate/nm/ipv6.py
@@ -106,6 +106,7 @@ def create_setting(config, base_con_profile):
setting_ip.props.never_default = False
setting_ip.props.ignore_auto_dns = False
setting_ip.clear_routes()
+ setting_ip.clear_routing_rules()
setting_ip.props.gateway = None
setting_ip.props.route_table = Route.USE_DEFAULT_ROUTE_TABLE
setting_ip.props.route_metric = Route.USE_DEFAULT_METRIC
--
2.25.4

View File

@ -1,103 +0,0 @@
From 2595c75cb8488e855fc5d98bcc944c6c0ad96b54 Mon Sep 17 00:00:00 2001
From: Quique Llorente <ellorent@redhat.com>
Date: Tue, 24 Nov 2020 12:52:35 +0100
Subject: [PATCH 1/2] ovs: Ignore ovs-port always
At containerize nmstate we cannot run "systemctl openvswitch status" so
even with a openvswitch running at host it will appear as non running
but the ovs information will arrive from NM dbus interface. This breaks
nmstatectl show since ovs-port is not part of nmstate and is not
included in the schema. This change just ignore ovs-port even if
openvswitch appear as not running.
Signed-off-by: Quique Llorente <ellorent@redhat.com>
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/plugin.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libnmstate/nm/plugin.py b/libnmstate/nm/plugin.py
index 4032359..6b6217d 100644
--- a/libnmstate/nm/plugin.py
+++ b/libnmstate/nm/plugin.py
@@ -123,6 +123,8 @@ class NetworkManagerPlugin(NmstatePlugin):
if nm_bond.is_bond_type_id(type_id):
bondinfo = nm_bond.get_bond_info(dev)
iface_info.update(_ifaceinfo_bond(bondinfo))
+ elif nm_ovs.is_ovs_port_type_id(type_id):
+ continue
elif NmstatePlugin.OVS_CAPABILITY in capabilities:
if nm_ovs.is_ovs_bridge_type_id(type_id):
iface_info["bridge"] = nm_ovs.get_ovs_info(
@@ -133,8 +135,6 @@ class NetworkManagerPlugin(NmstatePlugin):
)
elif nm_ovs.is_ovs_interface_type_id(type_id):
iface_info.update(nm_ovs.get_interface_info(act_con))
- elif nm_ovs.is_ovs_port_type_id(type_id):
- continue
info.append(iface_info)
--
2.29.2
From 3202bdd08737087160ff96bcf921793ce6b8335c Mon Sep 17 00:00:00 2001
From: Quique Llorente <ellorent@redhat.com>
Date: Wed, 25 Nov 2020 10:28:43 +0100
Subject: [PATCH 2/2] ovs: Ignore OVS capabilities at get interfaces
At containerize nmstate we cannot run "systemctl openvswitch status" so
even with a openvswitch running at host it will appear as non running
but the ovs information will arrive from NM dbus interface. This breaks
nmstatectl show since ovs-port is not part of nmstate and is not
included in the schema. This change removed the whole OVS compatibility check
when processing the interfaces reporting.
Signed-off-by: Quique Llorente <ellorent@redhat.com>
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/plugin.py | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/libnmstate/nm/plugin.py b/libnmstate/nm/plugin.py
index 6b6217d..06a5acd 100644
--- a/libnmstate/nm/plugin.py
+++ b/libnmstate/nm/plugin.py
@@ -97,7 +97,6 @@ class NetworkManagerPlugin(NmstatePlugin):
def get_interfaces(self):
info = []
- capabilities = self.capabilities
devices_info = [
(dev, nm_device.get_device_common_info(dev))
@@ -123,18 +122,15 @@ class NetworkManagerPlugin(NmstatePlugin):
if nm_bond.is_bond_type_id(type_id):
bondinfo = nm_bond.get_bond_info(dev)
iface_info.update(_ifaceinfo_bond(bondinfo))
+ elif nm_ovs.is_ovs_bridge_type_id(type_id):
+ iface_info["bridge"] = nm_ovs.get_ovs_info(
+ self.context, dev, devices_info
+ )
+ iface_info = _remove_ovs_bridge_unsupported_entries(iface_info)
+ elif nm_ovs.is_ovs_interface_type_id(type_id):
+ iface_info.update(nm_ovs.get_interface_info(act_con))
elif nm_ovs.is_ovs_port_type_id(type_id):
continue
- elif NmstatePlugin.OVS_CAPABILITY in capabilities:
- if nm_ovs.is_ovs_bridge_type_id(type_id):
- iface_info["bridge"] = nm_ovs.get_ovs_info(
- self.context, dev, devices_info
- )
- iface_info = _remove_ovs_bridge_unsupported_entries(
- iface_info
- )
- elif nm_ovs.is_ovs_interface_type_id(type_id):
- iface_info.update(nm_ovs.get_interface_info(act_con))
info.append(iface_info)
--
2.29.2

View File

@ -1,38 +0,0 @@
From d7393d40aaedeea5dd8291519cddeecdfdabc849 Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Mon, 7 Dec 2020 00:51:19 +0100
Subject: [PATCH] ifaces: do not remove unmanaged orphan interfaces
If there are unmanaged OVS interface present in the network state, NM
may report uncomplete information. Therefore, nmstate could consider
them as orphan and remove it when modifying the network state.
In order to fix this, nmstate should not consider it orphan and remove
it when it is not on desired state or marked as changed.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/ifaces/ifaces.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 1c2ffd5..703e672 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -242,8 +242,10 @@ class Ifaces:
def _mark_orphen_as_absent(self):
for iface in self._ifaces.values():
- if iface.need_parent and (
- not iface.parent or not self._ifaces.get(iface.parent)
+ if (
+ iface.need_parent
+ and (not iface.parent or not self._ifaces.get(iface.parent))
+ and (iface.is_desired or iface.is_changed)
):
iface.mark_as_changed()
iface.state = InterfaceState.ABSENT
--
2.18.4

View File

@ -1,185 +0,0 @@
From 1d0656c4197f0119d156b0df7b13bffeb5c46861 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Mon, 4 Jan 2021 11:37:18 +0800
Subject: [PATCH] sriov: Use verification retry to wait VF been created
When reactivating i40e interface with SR-IOV enabled, the kernel
takes some time(1 seconds or more in my test) to get the VF interface
ready in kernel. So at the time of libnmstate returns with success, the
VF interface might not be ready for use yet.
To fix that, we include VF interfaces in desire state when PV is
changed/desired. The verification retry will wait the VF to be ready for
use.
Unit test case and integration test case included.
Also fixed SRIOV integration test cases which are now all passing on i40e
NIC.
To test on real SRIOV NIC:
cd tests/integration/
sudo env TEST_REAL_NIC=ens1f1 pytest-3 sriov_test.py -vvv
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/ifaces/ethernet.py | 28 ++++
libnmstate/ifaces/ifaces.py | 23 +++
libnmstate/nm/sriov.py | 2 +-
libnmstate/nm/wired.py | 19 ++-
tests/integration/sriov_test.py | 242 ++++++++++++++++++++++----------
tests/lib/nm/wired_test.py | 5 +
6 files changed, 231 insertions(+), 88 deletions(-)
diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
index b346c36..644fe6d 100644
--- a/libnmstate/ifaces/ethernet.py
+++ b/libnmstate/ifaces/ethernet.py
@@ -18,6 +18,9 @@
#
from libnmstate.schema import Ethernet
+from libnmstate.schema import Interface
+from libnmstate.schema import InterfaceType
+from libnmstate.schema import InterfaceState
from .base_iface import BaseIface
@@ -46,6 +49,31 @@ class EthernetIface(BaseIface):
_capitalize_sriov_vf_mac(state)
return state
+ @property
+ def sriov_total_vfs(self):
+ return (
+ self.raw.get(Ethernet.CONFIG_SUBTREE, {})
+ .get(Ethernet.SRIOV_SUBTREE, {})
+ .get(Ethernet.SRIOV.TOTAL_VFS, 0)
+ )
+
+ def create_sriov_vf_ifaces(self):
+ return [
+ EthernetIface(
+ {
+ # According to manpage of systemd.net-naming-scheme(7),
+ # SRIOV VF interface will have v{slot} in device name.
+ # Currently, nmstate has no intention to support
+ # user-defined udev rule on SRIOV interface naming policy.
+ Interface.NAME: f"{self.name}v{i}",
+ Interface.TYPE: InterfaceType.ETHERNET,
+ # VF will be in DOWN state initialy.
+ Interface.STATE: InterfaceState.DOWN,
+ }
+ )
+ for i in range(0, self.sriov_total_vfs)
+ ]
+
def _capitalize_sriov_vf_mac(state):
vfs = (
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 703e672..7723f43 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -97,6 +97,7 @@ class Ifaces:
self._ifaces[iface.name] = iface
self._create_virtual_slaves()
+ self._create_sriov_vfs_when_changed()
self._validate_unknown_slaves()
self._validate_unknown_parent()
self._gen_metadata()
@@ -124,6 +125,28 @@ class Ifaces:
for iface in new_ifaces:
self._ifaces[iface.name] = iface
+ def _create_sriov_vfs_when_changed(self):
+ """
+ When plugin set the TOTAL_VFS of PF, it might take 1 seconds or
+ more to have the VFs to be ready.
+ Nmstate should use verification retry to make sure VFs are full ready.
+ To do that, we include VFs into desire state.
+ """
+ new_ifaces = []
+ for iface in self._ifaces.values():
+ if (
+ iface.is_up
+ and (iface.is_desired or iface.is_changed)
+ and iface.type == InterfaceType.ETHERNET
+ and iface.sriov_total_vfs > 0
+ ):
+ for new_iface in iface.create_sriov_vf_ifaces():
+ if new_iface.name not in self._ifaces:
+ new_iface.mark_as_desired()
+ new_ifaces.append(new_iface)
+ for new_iface in new_ifaces:
+ self._ifaces[new_iface.name] = new_iface
+
def _pre_edit_validation_and_cleanup(self):
self._validate_over_booked_slaves()
self._validate_vlan_mtu()
diff --git a/libnmstate/nm/sriov.py b/libnmstate/nm/sriov.py
index f544732..25b150c 100644
--- a/libnmstate/nm/sriov.py
+++ b/libnmstate/nm/sriov.py
@@ -68,7 +68,7 @@ def create_setting(context, iface_state, base_con_profile):
sriov_config = iface_state.get(Ethernet.CONFIG_SUBTREE, {}).get(
Ethernet.SRIOV_SUBTREE
)
- if sriov_config:
+ if sriov_config and sriov_config.get(Ethernet.SRIOV.TOTAL_VFS):
if not _has_sriov_capability(context, ifname):
raise NmstateNotSupportedError(
f"Interface '{ifname}' does not support SR-IOV"
diff --git a/libnmstate/nm/wired.py b/libnmstate/nm/wired.py
index 64662ac..5fea2a5 100644
--- a/libnmstate/nm/wired.py
+++ b/libnmstate/nm/wired.py
@@ -162,15 +162,18 @@ def _get_mac_address_from_sysfs(ifname):
def _get_ethernet_info(device, iface):
+
ethernet = {}
+ sriov_info = sriov.get_info(device)
+ if sriov_info:
+ ethernet.update(sriov_info)
+
try:
speed = int(device.get_speed())
if speed > 0:
ethernet[Ethernet.SPEED] = speed
- else:
- return None
except AttributeError:
- return None
+ pass
ethtool_results = minimal_ethtool(iface)
auto_setting = ethtool_results[Ethernet.AUTO_NEGOTIATION]
@@ -178,17 +181,11 @@ def _get_ethernet_info(device, iface):
ethernet[Ethernet.AUTO_NEGOTIATION] = True
elif auto_setting is False:
ethernet[Ethernet.AUTO_NEGOTIATION] = False
- else:
- return None
duplex_setting = ethtool_results[Ethernet.DUPLEX]
if duplex_setting in [Ethernet.HALF_DUPLEX, Ethernet.FULL_DUPLEX]:
ethernet[Ethernet.DUPLEX] = duplex_setting
- else:
- return None
-
- sriov_info = sriov.get_info(device)
- if sriov_info:
- ethernet.update(sriov_info)
+ if not ethernet:
+ return None
return ethernet
--
2.25.4

View File

@ -1,92 +0,0 @@
From 47bd6db50e33aaa3d3d5e3b70d5f3039122b3a5c Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Sat, 5 Sep 2020 00:36:41 +0800
Subject: [PATCH] nm route: Add support of multiple gateways
Since NetworkManager 1.22.0, the `NM.SettingIPConfig.props.routes`
support assigning multiple gateway.
Integration test case updated for this.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/route.py | 30 +++---------------------------
1 file changed, 3 insertions(+), 27 deletions(-)
diff --git a/libnmstate/nm/route.py b/libnmstate/nm/route.py
index 53bcf7c..548b218 100644
--- a/libnmstate/nm/route.py
+++ b/libnmstate/nm/route.py
@@ -21,7 +21,6 @@ from operator import itemgetter
import socket
from libnmstate import iplib
-from libnmstate.error import NmstateNotImplementedError
from libnmstate.error import NmstateValueError
from libnmstate.nm import active_connection as nm_ac
from libnmstate.schema import Interface
@@ -31,7 +30,6 @@ from libnmstate.schema import RouteRule
from .common import GLib
from .common import NM
-NM_ROUTE_TABLE_ATTRIBUTE = "table"
IPV4_DEFAULT_GATEWAY_DESTINATION = "0.0.0.0/0"
IPV6_DEFAULT_GATEWAY_DESTINATION = "::/0"
@@ -116,7 +114,7 @@ def get_config(acs_and_ip_profiles):
def _get_per_route_table_id(nm_route, default_table_id):
- table = nm_route.get_attribute(NM_ROUTE_TABLE_ATTRIBUTE)
+ table = nm_route.get_attribute(NM.IP_ROUTE_ATTRIBUTE_TABLE)
return int(table.get_uint32()) if table else default_table_id
@@ -152,19 +150,7 @@ def _get_default_route_config(gateway, metric, default_table_id, iface_name):
def add_routes(setting_ip, routes):
for route in routes:
- if route[Route.DESTINATION] in (
- IPV4_DEFAULT_GATEWAY_DESTINATION,
- IPV6_DEFAULT_GATEWAY_DESTINATION,
- ):
- if setting_ip.get_gateway():
- raise NmstateNotImplementedError(
- "Only a single default gateway is supported due to a "
- "limitation of NetworkManager: "
- "https://bugzilla.redhat.com/1707396"
- )
- _add_route_gateway(setting_ip, route)
- else:
- _add_specfic_route(setting_ip, route)
+ _add_specfic_route(setting_ip, route)
def _add_specfic_route(setting_ip, route):
@@ -181,22 +167,12 @@ def _add_specfic_route(setting_ip, route):
)
table_id = route.get(Route.TABLE_ID, Route.USE_DEFAULT_ROUTE_TABLE)
ip_route.set_attribute(
- NM_ROUTE_TABLE_ATTRIBUTE, GLib.Variant.new_uint32(table_id)
+ NM.IP_ROUTE_ATTRIBUTE_TABLE, GLib.Variant.new_uint32(table_id)
)
# Duplicate route entry will be ignored by libnm.
setting_ip.add_route(ip_route)
-def _add_route_gateway(setting_ip, route):
- setting_ip.props.gateway = route[Route.NEXT_HOP_ADDRESS]
- setting_ip.props.route_table = route.get(
- Route.TABLE_ID, Route.USE_DEFAULT_ROUTE_TABLE
- )
- setting_ip.props.route_metric = route.get(
- Route.METRIC, Route.USE_DEFAULT_METRIC
- )
-
-
def get_static_gateway_iface(family, iface_routes):
"""
Return one interface with gateway for given IP family.
--
2.27.0

View File

@ -1,419 +0,0 @@
From 803ad90f11eb57221e7805e5cba8c309bafe1de8 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Wed, 13 Jan 2021 23:51:31 +0800
Subject: [PATCH 1/2] nm: Better handling for timeout
When creating 1000 VLAN along with 1000 bridge using each VLAN,
NetworkManager might trigger two timeout:
* The callback raises `Gio.IOErrorEnum.TIMED_OUT` error.
* NetworkManager never call callback, nmstate idle check trigger the
timeout.
To solve above issue:
* Increase the nmstate idle check timeout to 5 minutes.
* For actions like add profile and activate profile, we add a
fallback checker which check whether requested action is already
finished using `GLib.timeout_source_new()`
* When `Gio.IOErrorEnum.TIMED_OUT` happens, ignore the failure and wait
fallback checker.
* The fallback checker is only started 15 seconds after action started,
so this does not impact small desire state.
Test results on RHEL 8.3 i7-8665U 2G RAM:
10m29.212s to create 1000 VLAN and 1000 bridge over each VLAN.
Changed the integration test case to test 500 VLANs + 500 bridges.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/connection.py | 67 ++++++++++++++++++++++++++++++++++++-
libnmstate/nm/context.py | 3 +-
2 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
index 1f6c734..374a379 100644
--- a/libnmstate/nm/connection.py
+++ b/libnmstate/nm/connection.py
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2018-2020 Red Hat, Inc.
+# Copyright (c) 2018-2021 Red Hat, Inc.
#
# This file is part of nmstate
#
@@ -24,11 +24,14 @@ from libnmstate.error import NmstateLibnmError
from libnmstate.error import NmstateInternalError
from libnmstate.error import NmstateValueError
+from .common import GLib
+from .common import Gio
from .common import NM
from . import ipv4
from . import ipv6
ACTIVATION_TIMEOUT_FOR_BRIDGE = 35 # Bridge STP requires 30 seconds.
+FALLBACK_CHECKER_INTERNAL = 15
class ConnectionProfile:
@@ -40,6 +43,7 @@ class ConnectionProfile:
self._nm_ac = None
self._ac_handlers = set()
self._dev_handlers = set()
+ self._fallback_checker = None
def create(self, settings):
self.profile = NM.SimpleConnection.new()
@@ -102,6 +106,26 @@ class ConnectionProfile:
self._add_connection2_callback,
user_data,
)
+ self._fallback_checker = GLib.timeout_source_new(
+ FALLBACK_CHECKER_INTERNAL * 1000
+ )
+ self._fallback_checker.set_callback(
+ self._profile_add_fallback_checker_callback, action
+ )
+ self._fallback_checker.attach(self._ctx.context)
+
+ def _profile_add_fallback_checker_callback(self, action):
+ for nm_profile in self._ctx.client.get_connections():
+ if nm_profile.get_uuid() == self.profile.get_uuid():
+ self._fallback_checker_cleanup()
+ self._ctx.finish_async(action)
+ return GLib.SOURCE_REMOVE
+ return GLib.SOURCE_CONTINUE
+
+ def _fallback_checker_cleanup(self):
+ if self._fallback_checker:
+ self._fallback_checker.destroy()
+ self._fallback_checker = None
def delete(self):
if not self.profile:
@@ -152,6 +176,26 @@ class ConnectionProfile:
self._active_connection_callback,
user_data,
)
+ self._fallback_checker = GLib.timeout_source_new(
+ FALLBACK_CHECKER_INTERNAL * 1000
+ )
+ self._fallback_checker.set_callback(
+ self._activation_fallback_checker_callback, action
+ )
+ self._fallback_checker.attach(self._ctx.context)
+
+ def _activation_fallback_checker_callback(self, action):
+ if self.devname:
+ self._nm_dev = self._ctx.get_nm_dev(self.devname)
+ if self._nm_dev:
+ self._activation_progress_check(action)
+ return GLib.SOURCE_CONTINUE
+ else:
+ logging.warn(
+ "Failed to get interface name from profile, "
+ "can not perform flalback check on activation"
+ )
+ return GLib.SOURCE_REMOVE
@property
def profile(self):
@@ -213,6 +257,18 @@ class ConnectionProfile:
try:
nm_act_con = src_object.activate_connection_finish(result)
+ except GLib.Error as e:
+ if e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT):
+ logging.debug(
+ f"{action} timeout on activation, "
+ "using fallback method to wait activation"
+ )
+ return
+ else:
+ self._ctx.fail(
+ NmstateLibnmError(f"{action} failed: error={e}")
+ )
+ return
except Exception as e:
self._ctx.fail(NmstateLibnmError(f"{action} failed: error={e}"))
return
@@ -366,6 +422,7 @@ class ConnectionProfile:
def _activation_clean_up(self):
self._remove_ac_handlers()
self._remove_dev_handlers()
+ self._fallback_checker_cleanup()
def _is_activating(self):
if not self._nm_ac or not self._nm_dev:
@@ -396,6 +453,13 @@ class ConnectionProfile:
action = user_data
try:
profile = src_object.add_connection2_finish(result)[0]
+ except GLib.Error as e:
+ if e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT):
+ logging.debug(
+ f"{action} timeout, using fallback method to "
+ "wait profile creation"
+ )
+ return
except Exception as e:
self._ctx.fail(
NmstateLibnmError(f"{action} failed with error: {e}")
@@ -410,6 +474,7 @@ class ConnectionProfile:
)
)
else:
+ self._fallback_checker_cleanup()
self._ctx.finish_async(action)
def _update2_callback(self, src_object, result, user_data):
diff --git a/libnmstate/nm/context.py b/libnmstate/nm/context.py
index 373ffe8..bc5c41c 100644
--- a/libnmstate/nm/context.py
+++ b/libnmstate/nm/context.py
@@ -31,8 +31,7 @@ from .common import Gio
# last finish async action.
IDLE_CHECK_INTERNAL = 5
-# libnm dbus connection has reply timeout 25 seconds.
-IDLE_TIMEOUT = 25
+IDLE_TIMEOUT = 60 * 5 # 5 minutes
# NetworkManage is using dbus in libnm while the dbus has limitation on
# maximum number of pending replies per connection.(RHEL/CentOS 8 is 1024)
--
2.27.0
From ac82d18f96aa2313583efa1477be441291e2957c Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Sun, 17 Jan 2021 11:18:10 +0800
Subject: [PATCH 2/2] nm: Use fallback checker on profile deactivation and
delete
When NM is under heave loads, NM might raise timeout error when
try to deactivate or delete a profile, to solve that this patch
introduce the same method in 2407f98
to have a fallback checker on whether profile is deactivated/deleted
every 15 seconds.
No test case required as current `test_lot_of_vlans_with_bridges` test
case has `state: absent` which is good enough for testing this patch.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/active_connection.py | 61 ++++++++++++++++++++++++------
libnmstate/nm/connection.py | 30 +++++++++++++++
2 files changed, 80 insertions(+), 11 deletions(-)
diff --git a/libnmstate/nm/active_connection.py b/libnmstate/nm/active_connection.py
index 062c78a..b235e8b 100644
--- a/libnmstate/nm/active_connection.py
+++ b/libnmstate/nm/active_connection.py
@@ -21,12 +21,14 @@ import logging
from libnmstate.error import NmstateLibnmError
+from .common import Gio
from .common import GLib
from .common import GObject
from .common import NM
NM_AC_STATE_CHANGED_SIGNAL = "state-changed"
+FALLBACK_CHECKER_INTERNAL = 15
class ActivationError(Exception):
@@ -37,6 +39,8 @@ class ActiveConnection:
def __init__(self, context=None, nm_ac_con=None):
self._ctx = context
self._act_con = nm_ac_con
+ self._signal_handler = None
+ self._fallback_checker = None
nmdevs = None
if nm_ac_con:
@@ -75,19 +79,35 @@ class ActiveConnection:
action = f"Deactivate profile: {self.devname}"
self._ctx.register_async(action)
- handler_id = act_connection.connect(
+ self._signal_handler = act_connection.connect(
NM_AC_STATE_CHANGED_SIGNAL,
self._wait_state_changed_callback,
action,
)
if act_connection.props.state != NM.ActiveConnectionState.DEACTIVATING:
- user_data = (handler_id, action)
+ user_data = action
self._ctx.client.deactivate_connection_async(
act_connection,
self._ctx.cancellable,
self._deactivate_connection_callback,
user_data,
)
+ self._fallback_checker = GLib.timeout_source_new(
+ FALLBACK_CHECKER_INTERNAL * 1000
+ )
+ self._fallback_checker.set_callback(
+ self._deactivation_fallback_checker_callback, action
+ )
+ self._fallback_checker.attach(self._ctx.context)
+
+ def _clean_up(self):
+ if self._signal_handler:
+ if self._act_con:
+ self._act_con.handler_disconnect(self._signal_handler)
+ self._signal_handler = None
+ if self._fallback_checker:
+ self._fallback_checker.destroy()
+ self._fallback_checker = None
def _wait_state_changed_callback(self, act_con, state, reason, action):
if self._ctx.is_cancelled():
@@ -96,13 +116,13 @@ class ActiveConnection:
logging.debug(
"Connection deactivation succeeded on %s", self.devname,
)
+ self._clean_up()
self._ctx.finish_async(action)
def _deactivate_connection_callback(self, src_object, result, user_data):
- handler_id, action = user_data
+ action = user_data
if self._ctx.is_cancelled():
- if self._act_con:
- self._act_con.handler_disconnect(handler_id)
+ self._clean_up()
return
try:
@@ -116,16 +136,20 @@ class ActiveConnection:
"Connection is not active on {}, no need to "
"deactivate".format(self.devname)
)
+ elif e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT):
+ logging.debug(
+ f"{action} timeout, using fallback method to wait profile "
+ "deactivation"
+ )
+ return
else:
- if self._act_con:
- self._act_con.handler_disconnect(handler_id)
+ self._clean_up()
self._ctx.fail(
NmstateLibnmError(f"{action} failed: error={e}")
)
return
except Exception as e:
- if self._act_con:
- self._act_con.handler_disconnect(handler_id)
+ self._clean_up()
self._ctx.fail(
NmstateLibnmError(
f"BUG: Unexpected error when activating {self.devname} "
@@ -135,8 +159,7 @@ class ActiveConnection:
return
if not success:
- if self._act_con:
- self._act_con.handler_disconnect(handler_id)
+ self._clean_up()
self._ctx.fail(
NmstateLibnmError(
f"{action} failed: error='None returned from "
@@ -144,6 +167,22 @@ class ActiveConnection:
)
)
+ def _deactivation_fallback_checker_callback(self, action):
+ if self.devname:
+ self._nmdev = self._ctx.get_nm_dev(self.devname)
+ if self._nmdev:
+ self._act_con = self._nmdev.get_active_connection()
+ if (
+ self._act_con
+ and self._act_con.props.state
+ != NM.ActiveConnectionState.DEACTIVATED
+ ):
+ return GLib.SOURCE_CONTINUE
+
+ self._clean_up()
+ self._ctx.finish_async(action)
+ return GLib.SOURCE_REMOVE
+
@property
def nm_active_connection(self):
return self._act_con
diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
index 374a379..af7296f 100644
--- a/libnmstate/nm/connection.py
+++ b/libnmstate/nm/connection.py
@@ -144,6 +144,13 @@ class ConnectionProfile:
self._delete_connection_callback,
user_data,
)
+ self._fallback_checker = GLib.timeout_source_new(
+ FALLBACK_CHECKER_INTERNAL * 1000
+ )
+ self._fallback_checker.set_callback(
+ self._delete_fallback_checker_callback, action
+ )
+ self._fallback_checker.attach(self._ctx.context)
def activate(self):
if self.con_id:
@@ -504,11 +511,24 @@ class ConnectionProfile:
action = user_data
try:
success = src_object.delete_finish(result)
+ except GLib.Error as e:
+ if e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT):
+ logging.debug(
+ f"{action} timeout, using fallback method to wait profile "
+ "deletion"
+ )
+ return
+ else:
+ self._ctx.fail(
+ NmstateLibnmError(f"{action} failed with error: {e}")
+ )
+ return
except Exception as e:
self._ctx.fail(NmstateLibnmError(f"{action} failed: error={e}"))
return
if success:
+ self._fallback_checker_cleanup()
self._ctx.finish_async(action)
else:
self._ctx.fail(
@@ -518,6 +538,16 @@ class ConnectionProfile:
)
)
+ def _delete_fallback_checker_callback(self, action):
+ if self.profile:
+ for nm_profile in self._ctx.client.get_connections():
+ if nm_profile.get_uuid() == self.profile.get_uuid():
+ return GLib.SOURCE_CONTINUE
+
+ self._fallback_checker_cleanup()
+ self._ctx.finish_async(action)
+ return GLib.SOURCE_REMOVE
+
def _reset_profile(self):
self._con_profile = None
--
2.27.0

View File

@ -1,58 +0,0 @@
From 013860d2576a34a277178e6afba0935498dc4f72 Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Wed, 3 Feb 2021 11:59:05 +0100
Subject: [PATCH] connection: retry on profile activation if libnm error
happened
When activating a profile if NetworkManager fails during the activation,
Nmstate should retry it once.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/nm/connection.py | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
index 5b9a3aee2..45cb69019 100644
--- a/libnmstate/nm/connection.py
+++ b/libnmstate/nm/connection.py
@@ -180,7 +180,8 @@ def activate(self):
"BUG: Cannot activate a profile with empty profile id and "
"empty NM.Device"
)
- user_data = action
+ retry = True
+ user_data = action, retry
self._ctx.register_async(action)
self._ctx.client.activate_connection_async(
self.profile,
@@ -267,7 +268,7 @@ def _active_connection_callback(self, src_object, result, user_data):
if self._ctx.is_cancelled():
self._activation_clean_up()
return
- action = user_data
+ action, retry = user_data
try:
nm_act_con = src_object.activate_connection_finish(result)
@@ -279,6 +280,20 @@ def _active_connection_callback(self, src_object, result, user_data):
)
return
else:
+ if retry:
+ retry = False
+ user_data = action, retry
+ specific_object = None
+ logging.debug(f"Action {action} failed, trying again.")
+ self._ctx.client.activate_connection_async(
+ self.profile,
+ self.nmdevice,
+ specific_object,
+ self._ctx.cancellable,
+ self._active_connection_callback,
+ user_data,
+ )
+ return
self._ctx.fail(
NmstateLibnmError(f"{action} failed: error={e}")
)

View File

@ -1,214 +0,0 @@
From a85b3dddf82f9e71774229740fbae6ea843d86d6 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Mon, 18 Jan 2021 15:55:43 +0800
Subject: [PATCH 1/2] ifaces: Don't validate undesired interface for overbook
There is no need to validate overbooked interface for undesired
controller interface.
Unit test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/ifaces/ifaces.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 7723f43..ee75125 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -437,6 +437,8 @@ class Ifaces:
"""
slave_master_map = {}
for iface in self._ifaces.values():
+ if not (iface.is_changed or iface.is_desired) or not iface.is_up:
+ continue
for slave_name in iface.slaves:
cur_master = slave_master_map.get(slave_name)
if cur_master:
--
2.27.0
From 644d8e5f5072caaba7151e66f211eceb02ae79c3 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Thu, 21 Jan 2021 20:43:34 +0800
Subject: [PATCH 2/2] nm vlan/vxlan: Use uuid for VLAN/VxLAN parent
When parent of VLAN/VxLAN is holding the same name of OVS bridge or OVS
port, NetworkManager will fail with error failed to find interface index
of that parent.
The root cause is NetworkManager try to use user space interface as
VLAN/VxLAN parent.
To workaround that, use profile UUID for `NM.SettingVlan.props.parent`
and `NM.SettingVxlan.props.parent`.
Integration test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/nm/applier.py | 98 +++++++++++++++++++++++++++++++++++--
libnmstate/nm/connection.py | 7 +++
2 files changed, 100 insertions(+), 5 deletions(-)
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index 26a057f..8e38df5 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -66,8 +66,6 @@ MASTER_IFACE_TYPES = (
def apply_changes(context, net_state, save_to_disk):
- con_profiles = []
-
if (
not save_to_disk
and _has_ovs_interface_desired_or_changed(net_state)
@@ -87,6 +85,10 @@ def apply_changes(context, net_state, save_to_disk):
_create_proxy_ifaces_desired_state(ifaces_desired_state)
)
+ # A list of tuple holding both current ConnectionProfile and new/updated
+ # ConnectionProfile.
+ pending_con_profiles = []
+
for iface_desired_state in filter(
lambda s: s.get(Interface.STATE) != InterfaceState.ABSENT,
ifaces_desired_state,
@@ -131,7 +133,7 @@ def apply_changes(context, net_state, save_to_disk):
# anything besides state:up and not been marked as changed.
# We don't need to do this once we support querying on-disk
# configure
- con_profiles.append(cur_con_profile)
+ pending_con_profiles.append((cur_con_profile, None))
continue
new_con_profile = _build_connection_profile(
context,
@@ -143,12 +145,25 @@ def apply_changes(context, net_state, save_to_disk):
set_conn = new_con_profile.profile.get_setting_connection()
set_conn.props.interface_name = iface_desired_state[Interface.NAME]
if cur_con_profile and cur_con_profile.profile:
- cur_con_profile.update(new_con_profile, save_to_disk)
- con_profiles.append(new_con_profile)
+ pending_con_profiles.append((cur_con_profile, new_con_profile))
else:
# Missing connection, attempting to create a new one.
+ pending_con_profiles.append((None, new_con_profile))
+
+ pending_con_profiles = _use_uuid_for_parent(
+ context, pending_con_profiles, save_to_disk
+ )
+
+ con_profiles = []
+ for cur_con_profile, new_con_profile in pending_con_profiles:
+ if cur_con_profile and new_con_profile:
+ cur_con_profile.update(new_con_profile, save_to_disk)
+ con_profiles.append(new_con_profile)
+ elif cur_con_profile is None and new_con_profile:
new_con_profile.add(save_to_disk)
con_profiles.append(new_con_profile)
+ elif cur_con_profile:
+ con_profiles.append(cur_con_profile)
context.wait_all_finish()
_set_ifaces_admin_state(context, ifaces_desired_state, con_profiles)
@@ -655,3 +670,76 @@ def _mark_nm_external_subordinate_changed(context, net_state):
subordinate_iface = net_state.ifaces.get(subordinate)
if subordinate_iface:
subordinate_iface.mark_as_changed()
+
+
+def _use_uuid_for_parent(context, pending_con_profiles, save_to_disk):
+ """
+ When parent of VLAN/VxLAN is holding the same name with
+ OVS bridge or OVS port, we should use UUID instead of interface name
+ """
+ new_pending_con_profiles = []
+ kernel_iface_name_to_uuid = {}
+ for cur_nm_profile in context.client.get_connections():
+ connection_type = cur_nm_profile.get_connection_type()
+ if connection_type not in (
+ NM.SETTING_OVS_BRIDGE_SETTING_NAME,
+ NM.SETTING_OVS_PORT_SETTING_NAME,
+ ):
+ kernel_iface_name_to_uuid[
+ cur_nm_profile.get_interface_name()
+ ] = cur_nm_profile.get_uuid()
+ # Override existing kernel_iface_name_to_uuid with pending changes.
+ for cur_con_profile, new_con_profile in pending_con_profiles:
+ if new_con_profile and new_con_profile.profile:
+ uuid = new_con_profile.profile.get_uuid()
+ connection_type = new_con_profile.profile.get_connection_type()
+ iface_name = new_con_profile.profile.get_interface_name()
+ elif cur_con_profile and cur_con_profile.profile:
+ uuid = cur_con_profile.profile.get_uuid()
+ connection_type = cur_con_profile.profile.get_connection_type()
+ iface_name = cur_con_profile.profile.get_interface_name()
+ else:
+ continue
+
+ if connection_type not in (
+ NM.SETTING_OVS_BRIDGE_SETTING_NAME,
+ NM.SETTING_OVS_PORT_SETTING_NAME,
+ ):
+ kernel_iface_name_to_uuid[iface_name] = uuid
+
+ for cur_con_profile, new_con_profile in pending_con_profiles:
+ new_pending_con_profiles.append((cur_con_profile, new_con_profile))
+ if not new_con_profile:
+ continue
+ nm_profile = new_con_profile.profile
+ if not nm_profile:
+ continue
+ connection_type = nm_profile.get_connection_type()
+ nm_setting = None
+ if connection_type == NM.SETTING_VLAN_SETTING_NAME:
+ nm_setting = nm_profile.get_setting_vlan()
+ elif connection_type == NM.SETTING_VXLAN_SETTING_NAME:
+ nm_setting = nm_profile.get_setting_vxlan()
+ else:
+ continue
+ if not nm_setting:
+ continue
+ parent_iface_name = nm_setting.props.parent
+ parent_uuid = kernel_iface_name_to_uuid.get(parent_iface_name)
+ if parent_uuid:
+ updated_con_profile = connection.ConnectionProfile(context)
+ new_nm_settings = []
+ for cur_nm_setting in nm_profile.get_settings():
+ new_nm_setting = cur_nm_setting.duplicate()
+ if new_nm_setting.get_name() in (
+ NM.SETTING_VLAN_SETTING_NAME,
+ NM.SETTING_VXLAN_SETTING_NAME,
+ ):
+ new_nm_setting.props.parent = parent_uuid
+ new_nm_settings.append(new_nm_setting)
+ updated_con_profile.create(new_nm_settings)
+ new_pending_con_profiles.pop()
+ new_pending_con_profiles.append(
+ (cur_con_profile, updated_con_profile)
+ )
+ return new_pending_con_profiles
diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
index af7296f..5b9a3ae 100644
--- a/libnmstate/nm/connection.py
+++ b/libnmstate/nm/connection.py
@@ -63,6 +63,13 @@ class ConnectionProfile:
if self.con_id:
self.profile = self._ctx.client.get_connection_by_id(self.con_id)
+ def import_by_uuid(self, uuid):
+ for nm_profile in self._ctx.client.get_connections():
+ if nm_profile.get_uuid() == uuid:
+ self.profile = nm_profile
+ return
+ logging.debug(f"Failed to find {uuid} profile")
+
def update(self, con_profile, save_to_disk=True):
flags = NM.SettingsUpdate2Flags.BLOCK_AUTOCONNECT
if save_to_disk:
--
2.27.0

View File

@ -1,82 +0,0 @@
From cc7e6da98cc992d2e6d8aa85cd78f02fb8ef8ac3 Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Sun, 21 Feb 2021 22:43:57 +0100
Subject: [PATCH 2/5] SR-IOV: Do not create VF profiles automatically
As VFs are Ethernet type and Nmstate is automatically adding them to the
desired state and marking them as desired, the profile is being created.
This is wrong as Nmstate should only wait for verification of the VFs.
In order to fix this, the new VF interfaces are being marked as so. This
way Nmstate is not creating the profiles anymore.
Integration test case added.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/ifaces/ethernet.py | 10 ++++
libnmstate/ifaces/ifaces.py | 1 +
libnmstate/nm/applier.py | 5 ++
tests/integration/nm/sriov_test.py | 92 ++++++++++++++++++++++++++++++
4 files changed, 108 insertions(+)
create mode 100644 tests/integration/nm/sriov_test.py
diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
index 3e1bdc5d..f1ece5f5 100644
--- a/libnmstate/ifaces/ethernet.py
+++ b/libnmstate/ifaces/ethernet.py
@@ -25,6 +25,9 @@ from libnmstate.schema import InterfaceState
from .base_iface import BaseIface
+IS_NEW_SR_IOV_VF = "_is_new_sr_iov_vf"
+
+
class EthernetIface(BaseIface):
def merge(self, other):
"""
@@ -57,6 +60,13 @@ class EthernetIface(BaseIface):
.get(Ethernet.SRIOV.TOTAL_VFS, 0)
)
+ @property
+ def is_new_sr_iov_vf(self):
+ return self.raw.get(IS_NEW_SR_IOV_VF)
+
+ def mark_as_new_sr_iov_vf(self):
+ self.raw[IS_NEW_SR_IOV_VF] = True
+
def create_sriov_vf_ifaces(self):
return [
EthernetIface(
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 67ab91c4..a7af9712 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -144,6 +144,7 @@ class Ifaces:
for new_iface in iface.create_sriov_vf_ifaces():
if new_iface.name not in self._ifaces:
new_iface.mark_as_desired()
+ new_iface.mark_as_new_sr_iov_vf()
new_ifaces.append(new_iface)
for new_iface in new_ifaces:
self._ifaces[new_iface.name] = new_iface
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index 8e38df54..cd319ffb 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -119,6 +119,11 @@ def apply_changes(context, net_state, save_to_disk):
original_desired_iface_state = {}
if net_state.ifaces.get(ifname):
iface = net_state.ifaces[ifname]
+ if iface.type == InterfaceType.ETHERNET and iface.is_new_sr_iov_vf:
+ # For new vfs automatically added to the desired state is just
+ # for verification, Nmstate should not create a profile for
+ # them.
+ continue
if iface.is_desired:
original_desired_iface_state = iface.original_dict
if (
--
2.29.2

View File

@ -1,112 +0,0 @@
From c55d39e8485a26490afc36d71fd9d20528ab6fd2 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Thu, 18 Feb 2021 18:01:17 +0800
Subject: [PATCH 1/5] SRIOV: Remove VF interface when total-vfs decrease
When `Ethernet.SRIOV.TOTAL_VFS` decrease, we should mark removed VF
interface as absent.
Both integration and unit test cases have been include.
The integration test has been tested on real SRIOV hardware.
Signed-off-by: Gris Ge <fge@redhat.com>
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/ifaces/base_iface.py | 5 +++++
libnmstate/ifaces/ethernet.py | 20 ++++++++++++++++++
libnmstate/ifaces/ifaces.py | 24 +++++++++++++++++++++
tests/integration/sriov_test.py | 37 +++++++++++++++++++++++++++++++++
4 files changed, 86 insertions(+)
diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py
index 564d583a..b4ade867 100644
--- a/libnmstate/ifaces/base_iface.py
+++ b/libnmstate/ifaces/base_iface.py
@@ -177,6 +177,11 @@ class BaseIface:
def mark_as_desired(self):
self._is_desired = True
+ def mark_as_absent_by_desire(self):
+ self.mark_as_desired()
+ self._info[Interface.STATE] = InterfaceState.ABSENT
+ self._origin_info[Interface.STATE] = InterfaceState.ABSENT
+
def to_dict(self):
return deepcopy(self._info)
diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
index 644fe6dd..3e1bdc5d 100644
--- a/libnmstate/ifaces/ethernet.py
+++ b/libnmstate/ifaces/ethernet.py
@@ -74,6 +74,26 @@ class EthernetIface(BaseIface):
for i in range(0, self.sriov_total_vfs)
]
+ def remove_vfs_entry_when_total_vfs_decreased(self):
+ vfs_count = len(
+ self._info[Ethernet.CONFIG_SUBTREE]
+ .get(Ethernet.SRIOV_SUBTREE, {})
+ .get(Ethernet.SRIOV.VFS_SUBTREE, [])
+ )
+ if vfs_count > self.sriov_total_vfs:
+ [
+ self._info[Ethernet.CONFIG_SUBTREE][Ethernet.SRIOV_SUBTREE][
+ Ethernet.SRIOV.VFS_SUBTREE
+ ].pop()
+ for _ in range(self.sriov_total_vfs, vfs_count)
+ ]
+
+ def get_delete_vf_interface_names(self, old_sriov_total_vfs):
+ return [
+ f"{self.name}v{i}"
+ for i in range(self.sriov_total_vfs, old_sriov_total_vfs)
+ ]
+
def _capitalize_sriov_vf_mac(state):
vfs = (
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index ee75125d..67ab91c4 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -99,6 +99,7 @@ class Ifaces:
self._create_virtual_slaves()
self._create_sriov_vfs_when_changed()
self._validate_unknown_slaves()
+ self._mark_vf_interface_as_absent_when_sriov_vf_decrease()
self._validate_unknown_parent()
self._gen_metadata()
for iface in self._ifaces.values():
@@ -147,6 +148,29 @@ class Ifaces:
for new_iface in new_ifaces:
self._ifaces[new_iface.name] = new_iface
+ def _mark_vf_interface_as_absent_when_sriov_vf_decrease(self):
+ """
+ When SRIOV TOTAL_VFS decreased, we should mark certain VF interfaces
+ as absent and also remove the entry in `Ethernet.SRIOV.VFS_SUBTREE`.
+ """
+ for iface_name, iface in self._ifaces.items():
+ if iface.type != InterfaceType.ETHERNET or not iface.is_up:
+ continue
+ if iface_name not in self.current_ifaces:
+ continue
+ cur_iface = self.current_ifaces[iface_name]
+ if (
+ cur_iface.sriov_total_vfs != 0
+ and iface.sriov_total_vfs < cur_iface.sriov_total_vfs
+ ):
+ iface.remove_vfs_entry_when_total_vfs_decreased()
+ for vf_name in iface.get_delete_vf_interface_names(
+ cur_iface.sriov_total_vfs
+ ):
+ vf_iface = self._ifaces.get(vf_name)
+ if vf_iface:
+ vf_iface.mark_as_absent_by_desire()
+
def _pre_edit_validation_and_cleanup(self):
self._validate_over_booked_slaves()
self._validate_vlan_mtu()
--
2.29.2

View File

@ -0,0 +1,166 @@
From 80c97b27707b036f0a54988ade4bda3ccb342b34 Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Mon, 22 Feb 2021 12:03:11 +0100
Subject: [PATCH 1/2] SR-IOV: increase the verification timeout if SR-IOV is
present
Certain drivers like i40e take a long time to modify the VFs in the
kernel. Nmstate is timing out on verification because of that. In order
to fix this, nmstate is incresing the verification time if SR-IOV is
present on the desired state.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/ifaces/ethernet.py | 6 ++++++
libnmstate/netapplier.py | 18 +++++++++++++++++-
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
index 292b7bc..ca8501b 100644
--- a/libnmstate/ifaces/ethernet.py
+++ b/libnmstate/ifaces/ethernet.py
@@ -65,6 +65,12 @@ class EthernetIface(BaseIface):
def is_peer(self):
return self._is_peer
+ @property
+ def is_sriov(self):
+ return self.raw.get(Ethernet.CONFIG_SUBTREE, {}).get(
+ Ethernet.SRIOV_SUBTREE
+ )
+
def create_sriov_vf_ifaces(self):
return [
EthernetIface(
diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py
index cf208d1..3c5759b 100644
--- a/libnmstate/netapplier.py
+++ b/libnmstate/netapplier.py
@@ -24,6 +24,7 @@ import time
from libnmstate import validator
from libnmstate.error import NmstateVerificationError
+from libnmstate.schema import InterfaceType
from .nmstate import create_checkpoints
from .nmstate import destroy_checkpoints
@@ -37,6 +38,7 @@ from .version import get_version
MAINLOOP_TIMEOUT = 35
VERIFY_RETRY_INTERNAL = 1
VERIFY_RETRY_TIMEOUT = 5
+VERIFY_RETRY_TIMEOUT_INCREASE = 4
def apply(
@@ -109,7 +111,13 @@ def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk):
plugin.apply_changes(net_state, save_to_disk)
verified = False
if verify_change:
- for _ in range(VERIFY_RETRY_TIMEOUT):
+ if _net_state_contains_sriov_interface(net_state):
+ # If SR-IOV is present, the verification timeout is being increased
+ # to avoid timeouts due to slow drivers like i40e.
+ verify_retry = VERIFY_RETRY_TIMEOUT * VERIFY_RETRY_TIMEOUT_INCREASE
+ else:
+ verify_retry = VERIFY_RETRY_TIMEOUT
+ for _ in range(verify_retry):
try:
_verify_change(plugins, net_state)
verified = True
@@ -120,6 +128,14 @@ def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk):
_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 = show_with_plugins(plugins)
net_state.verify(current_state)
--
2.27.0
From 439fe3a51a82060c5b62974c6c9fbdf403c4196b Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Mon, 22 Feb 2021 13:33:06 +0100
Subject: [PATCH 2/2] SR-IOV: fail on verification if `total_vfs` does not
match vfs len
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/ifaces/ethernet.py | 11 +++++++++++
libnmstate/ifaces/ifaces.py | 14 ++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
index ca8501b..55772ce 100644
--- a/libnmstate/ifaces/ethernet.py
+++ b/libnmstate/ifaces/ethernet.py
@@ -61,6 +61,14 @@ class EthernetIface(BaseIface):
.get(Ethernet.SRIOV.TOTAL_VFS, 0)
)
+ @property
+ def sriov_vfs(self):
+ return (
+ self.raw.get(Ethernet.CONFIG_SUBTREE, {})
+ .get(Ethernet.SRIOV_SUBTREE, {})
+ .get(Ethernet.SRIOV.VFS_SUBTREE, [])
+ )
+
@property
def is_peer(self):
return self._is_peer
@@ -108,6 +116,9 @@ class EthernetIface(BaseIface):
for i in range(self.sriov_total_vfs, old_sriov_total_vfs)
]
+ def check_total_vfs_matches_vf_list(self, total_vfs):
+ return total_vfs == len(self.sriov_vfs)
+
def _capitalize_sriov_vf_mac(state):
vfs = (
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 44c9e61..6c94a98 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -238,6 +238,8 @@ class Ifaces:
if new_iface.name not in self._kernel_ifaces:
new_iface.mark_as_desired()
new_ifaces.append(new_iface)
+ else:
+ self._kernel_ifaces[new_iface.name].mark_as_desired()
for new_iface in new_ifaces:
self._kernel_ifaces[new_iface.name] = new_iface
@@ -656,6 +658,18 @@ class Ifaces:
cur_iface.state_for_verify(),
)
)
+ elif (
+ iface.type == InterfaceType.ETHERNET and iface.is_sriov
+ ):
+ if not cur_iface.check_total_vfs_matches_vf_list(
+ iface.sriov_total_vfs
+ ):
+ raise NmstateVerificationError(
+ "The NIC exceeded the waiting time for "
+ "verification and it is failing because "
+ "the `total_vfs` does not match the VF "
+ "list length."
+ )
def gen_dns_metadata(self, dns_state, route_state):
iface_metadata = dns_state.gen_metadata(self, route_state)
--
2.27.0

View File

@ -0,0 +1,29 @@
From b26ab850172a41557cad42cc011bd00d7c108c88 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Tue, 23 Feb 2021 13:52:19 +0800
Subject: [PATCH] nmstatectl: Fix return code of set command
The deprecated command `set` should still return the error like
old behaviour.
Signed-off-by: Gris Ge <fge@redhat.com>
---
nmstatectl/nmstatectl.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nmstatectl/nmstatectl.py b/nmstatectl/nmstatectl.py
index df59942..c94d603 100644
--- a/nmstatectl/nmstatectl.py
+++ b/nmstatectl/nmstatectl.py
@@ -323,7 +323,7 @@ def show(args):
def set(args):
warnings.warn("Using 'set' is deprecated, use 'apply' instead.")
- apply(args)
+ return apply(args)
def apply(args):
--
2.27.0

View File

@ -1,79 +0,0 @@
From a43609607abe30b973f1cb78cb1754f1a9a91514 Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Mon, 22 Feb 2021 13:33:06 +0100
Subject: [PATCH 4/5] SR-IOV: fail on verification if `total_vfs` does not
match vfs len
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/ifaces/ethernet.py | 11 +++++++++++
libnmstate/ifaces/ifaces.py | 17 +++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
index 1c3ca266..4903bde7 100644
--- a/libnmstate/ifaces/ethernet.py
+++ b/libnmstate/ifaces/ethernet.py
@@ -52,6 +52,14 @@ class EthernetIface(BaseIface):
_capitalize_sriov_vf_mac(state)
return state
+ @property
+ def sriov_vfs(self):
+ return (
+ self.raw.get(Ethernet.CONFIG_SUBTREE, {})
+ .get(Ethernet.SRIOV_SUBTREE, {})
+ .get(Ethernet.SRIOV.VFS_SUBTREE, {})
+ )
+
@property
def sriov_total_vfs(self):
return (
@@ -109,6 +117,9 @@ class EthernetIface(BaseIface):
for i in range(self.sriov_total_vfs, old_sriov_total_vfs)
]
+ def check_total_vfs_matches_vf_list(self, total_vfs):
+ return total_vfs == len(self.sriov_vfs)
+
def _capitalize_sriov_vf_mac(state):
vfs = (
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index b212ebb5..1d30d0c6 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -146,6 +146,11 @@ class Ifaces:
new_iface.mark_as_desired()
new_iface.mark_as_new_sr_iov_vf()
new_ifaces.append(new_iface)
+ else:
+ # When the VFs are being modified, all VFs link are
+ # being removed from kernel and created again. Nmstate
+ # must verify they have been created.
+ self._ifaces[new_iface.name].mark_as_desired()
for new_iface in new_ifaces:
self._ifaces[new_iface.name] = new_iface
@@ -403,6 +408,18 @@ class Ifaces:
cur_iface.state_for_verify(),
)
)
+ elif (
+ iface.type == InterfaceType.ETHERNET and iface.is_sriov
+ ):
+ if not cur_iface.check_total_vfs_matches_vf_list(
+ iface.sriov_total_vfs
+ ):
+ raise NmstateVerificationError(
+ "The NIC exceeded the waiting time for "
+ "verification and it is failing because "
+ "the `total_vfs` does not match the VF "
+ "list length."
+ )
def gen_dns_metadata(self, dns_state, route_state):
iface_metadata = dns_state.gen_metadata(self, route_state)
--
2.29.2

View File

@ -1,102 +0,0 @@
From afb3f2dfeb5364e8a9a0bde4ad9062f1585a8795 Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Mon, 22 Feb 2021 12:03:11 +0100
Subject: [PATCH 3/5] SR-IOV: increase the verification timeout if SR-IOV is
present
Certain drivers like i40e take a long time to modify the VFs in the
kernel. Nmstate is timing out on verification because of that. In order
to fix this, nmstate is incresing the verification time if SR-IOV is
present on the desired state.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/ifaces/ethernet.py | 5 +++++
libnmstate/ifaces/ifaces.py | 4 ++++
libnmstate/netapplier.py | 18 +++++++++++++++++-
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
index f1ece5f5..1c3ca266 100644
--- a/libnmstate/ifaces/ethernet.py
+++ b/libnmstate/ifaces/ethernet.py
@@ -67,6 +67,11 @@ class EthernetIface(BaseIface):
def mark_as_new_sr_iov_vf(self):
self.raw[IS_NEW_SR_IOV_VF] = True
+ def is_sriov(self):
+ return self.raw.get(Ethernet.CONFIG_SUBTREE, {}).get(
+ Ethernet.SRIOV_SUBTREE, {}
+ )
+
def create_sriov_vf_ifaces(self):
return [
EthernetIface(
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index a7af9712..b212ebb5 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -325,6 +325,10 @@ class Ifaces:
def current_ifaces(self):
return self._cur_ifaces
+ @property
+ def all_ifaces(self):
+ return self._ifaces
+
@property
def state_to_edit(self):
return [
diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py
index 24df4d56..6aa1ac07 100644
--- a/libnmstate/netapplier.py
+++ b/libnmstate/netapplier.py
@@ -23,6 +23,7 @@ import time
from libnmstate import validator
from libnmstate.error import NmstateVerificationError
+from libnmstate.schema import InterfaceType
from .nmstate import create_checkpoints
from .nmstate import destroy_checkpoints
@@ -35,6 +36,7 @@ from .net_state import NetState
MAINLOOP_TIMEOUT = 35
VERIFY_RETRY_INTERNAL = 1
VERIFY_RETRY_TIMEOUT = 5
+VERIFY_RETRY_TIMEOUT_INCREASE = 4
def apply(
@@ -104,7 +106,13 @@ def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk):
plugin.apply_changes(net_state, save_to_disk)
verified = False
if verify_change:
- for _ in range(VERIFY_RETRY_TIMEOUT):
+ if _net_state_contains_sriov_interface(net_state):
+ # If SR-IOV is present, the verification timeout is being increased
+ # to avoid timeouts due to slow drivers like i40e.
+ verify_retry = VERIFY_RETRY_TIMEOUT * VERIFY_RETRY_TIMEOUT_INCREASE
+ else:
+ verify_retry = VERIFY_RETRY_TIMEOUT
+ for _ in range(verify_retry):
try:
_verify_change(plugins, net_state)
verified = True
@@ -115,6 +123,14 @@ def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk):
_verify_change(plugins, net_state)
+def _net_state_contains_sriov_interface(net_state):
+ for iface in net_state.ifaces.all_ifaces.values():
+ if iface.type == InterfaceType.ETHERNET and iface.is_sriov:
+ return True
+
+ return False
+
+
def _verify_change(plugins, net_state):
current_state = show_with_plugins(plugins)
net_state.verify(current_state)
--
2.29.2

View File

@ -1,38 +0,0 @@
From cb4cbff0da01bb1e6546c2b28ef2760ec1f89f74 Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Wed, 24 Feb 2021 17:12:13 +0100
Subject: [PATCH 5/5] bridge: do not bring up existing ports
Nmstate should not try to bring up existing ports on a bridge when
modifying it. The ports will be bring up when being added to the it, if
the bridge is being modified later, the ports are not going to be
automatically up.
Ref: https://bugzilla.redhat.com/1932247
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/ifaces/ifaces.py | 5 +
tests/integration/nm/linux_bridge_test.py | 112 +++++++++++++++++++++-
2 files changed, 116 insertions(+), 1 deletion(-)
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 1d30d0c6..5f1f548d 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -194,7 +194,12 @@ class Ifaces:
"""
for iface in self._ifaces.values():
if iface.is_up and iface.is_master:
+ cur_iface = self.current_ifaces.get(iface.name)
for slave_name in iface.slaves:
+ if cur_iface and slave_name in cur_iface.slaves:
+ # Nmstate should not touch the port interface which has
+ # already been included in current interface.
+ continue
slave_iface = self._ifaces[slave_name]
if not slave_iface.is_desired and not slave_iface.is_up:
slave_iface.mark_as_up()
--
2.29.2

View File

@ -0,0 +1,223 @@
From ccdcd8f86544a6364109a0c0142d05a5afacf64e Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Tue, 2 Mar 2021 15:31:20 +0800
Subject: [PATCH] nm: Don't touch unmanaged interface unless desired
We should ignore NetworkManager unmanaged interface when applying and
verifying unless certain interface listed in desired state explicitly.
Introduced new plugin interface
`NmstatePlugin.get_ignored_kernel_interface_names()` where plugin may
include a list of interface names which should be ignored during
verification stage.
Integration test case added to simulate CNV usage on partial editing
a linux bridge holding NM unmanaged interface.
Signed-off-by: Gris Ge <fge@redhat.com>
---
libnmstate/ifaces/base_iface.py | 3 ++
libnmstate/ifaces/ifaces.py | 26 ++++++++--------
libnmstate/netapplier.py | 6 ++++
libnmstate/nispor/plugin.py | 6 +++-
libnmstate/nm/plugin.py | 25 ++++++++++++++++
libnmstate/plugin.py | 7 +++++
tests/integration/linux_bridge_test.py | 8 +----
tests/integration/nm/linux_bridge_test.py | 36 ++++++++++++++++++++++-
8 files changed, 95 insertions(+), 22 deletions(-)
diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py
index 227c1d20..e3f2a1ca 100644
--- a/libnmstate/ifaces/base_iface.py
+++ b/libnmstate/ifaces/base_iface.py
@@ -322,6 +322,9 @@ class BaseIface:
def mark_as_up(self):
self.raw[Interface.STATE] = InterfaceState.UP
+ def mark_as_ignored(self):
+ self.raw[Interface.STATE] = InterfaceState.IGNORE
+
@property
def is_controller(self):
return False
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 6c94a986..efa24aa3 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -95,7 +95,6 @@ class Ifaces:
self._kernel_ifaces = {}
self._user_space_ifaces = _UserSpaceIfaces()
self._cur_user_space_ifaces = _UserSpaceIfaces()
- self._ignored_ifaces = set()
if cur_iface_infos:
for iface_info in cur_iface_infos:
cur_iface = _to_specific_iface_obj(iface_info, save_to_disk)
@@ -143,10 +142,6 @@ class Ifaces:
):
# Ignore interface with unknown type
continue
- if iface.is_ignore:
- self._ignored_ifaces.add(
- (iface.name, iface.type, iface.is_user_space_only)
- )
if cur_iface:
iface.merge(cur_iface)
iface.mark_as_desired()
@@ -169,6 +164,10 @@ class Ifaces:
self._pre_edit_validation_and_cleanup()
+ @property
+ def _ignored_ifaces(self):
+ return [iface for iface in self.all_ifaces() if iface.is_ignore]
+
def _apply_copy_mac_from(self):
for iface in self.all_kernel_ifaces.values():
if iface.type not in (
@@ -284,7 +283,7 @@ class Ifaces:
if not defiend in desire state
"""
for iface in self.all_ifaces():
- if iface.is_up and iface.is_controller:
+ if iface.is_desired and iface.is_up and iface.is_controller:
for port_name in iface.port:
port_iface = self._kernel_ifaces[port_name]
if not port_iface.is_desired and not port_iface.is_up:
@@ -550,13 +549,14 @@ class Ifaces:
return None
def _remove_iface(self, iface_name, iface_type):
- cur_iface = self._cur_kernel_ifaces.get(iface_name, iface_type)
+ cur_iface = self._user_space_ifaces.get(iface_name, iface_type)
if cur_iface:
self._user_space_ifaces.remove(cur_iface)
else:
cur_iface = self._kernel_ifaces.get(iface_name)
if (
- iface_type
+ cur_iface
+ and iface_type
and iface_type != InterfaceType.UNKNOWN
and iface_type == cur_iface.type
):
@@ -813,14 +813,14 @@ class Ifaces:
port_controller_map[port_name] = iface.name
def _remove_ignore_interfaces(self, ignored_ifaces):
- for iface_name, iface_type, _ in ignored_ifaces:
- self._remove_iface(iface_name, iface_type)
+ for iface in ignored_ifaces:
+ self._remove_iface(iface.name, iface.type)
# Only kernel interface can be used as port
ignored_kernel_iface_names = set(
- iface_name
- for iface_name, _, is_user_space_only in ignored_ifaces
- if not is_user_space_only
+ iface.name
+ for iface in ignored_ifaces
+ if not iface.is_user_space_only
)
# Remove ignored port
diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py
index 3c5759b4..a020f003 100644
--- a/libnmstate/netapplier.py
+++ b/libnmstate/netapplier.py
@@ -107,8 +107,14 @@ def rollback(*, checkpoint=None):
def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk):
+ for plugin in plugins:
+ for iface_name in plugin.get_ignored_kernel_interface_names():
+ iface = net_state.ifaces.all_kernel_ifaces.get(iface_name)
+ if iface and not iface.is_desired:
+ iface.mark_as_ignored()
for plugin in plugins:
plugin.apply_changes(net_state, save_to_disk)
+
verified = False
if verify_change:
if _net_state_contains_sriov_interface(net_state):
diff --git a/libnmstate/nispor/plugin.py b/libnmstate/nispor/plugin.py
index dc0ea760..19b21d56 100644
--- a/libnmstate/nispor/plugin.py
+++ b/libnmstate/nispor/plugin.py
@@ -159,7 +159,11 @@ class NisporPlugin(NmstatePlugin):
np_state = NisporNetState.retrieve()
logging.debug(f"Nispor: current network state {np_state}")
for iface in net_state.ifaces.all_ifaces():
- if iface.is_desired:
+ if iface.is_ignore:
+ logging.debug(
+ f"Nispor: Interface {iface.name} {iface.type} ignored"
+ )
+ elif iface.is_desired:
logging.debug(
f"Nispor: desired network state {iface.to_dict()}"
)
diff --git a/libnmstate/nm/plugin.py b/libnmstate/nm/plugin.py
index 302b4cca..335d93c7 100644
--- a/libnmstate/nm/plugin.py
+++ b/libnmstate/nm/plugin.py
@@ -36,6 +36,7 @@ from .checkpoint import get_checkpoints
from .common import NM
from .context import NmContext
from .device import get_device_common_info
+from .device import get_iface_type
from .device import list_devices
from .dns import get_running as get_dns_running
from .dns import get_running_config as get_dns_running_config
@@ -268,6 +269,21 @@ class NetworkManagerPlugin(NmstatePlugin):
)
return NmProfiles(None).generate_config_strings(net_state)
+ def get_ignored_kernel_interface_names(self):
+ """
+ Return a list of unmanged kernel interface names.
+ """
+ ignored_ifaces = set()
+ for nm_dev in list_devices(self.client):
+ if (
+ nm_dev
+ and nm_dev.get_iface()
+ and not nm_dev.get_managed()
+ and _is_kernel_iface(nm_dev)
+ ):
+ ignored_ifaces.add(nm_dev.get_iface())
+ return list(ignored_ifaces)
+
def _remove_ovs_bridge_unsupported_entries(iface_info):
"""
@@ -283,3 +299,12 @@ def _remove_ovs_bridge_unsupported_entries(iface_info):
def _nm_utils_decode_version():
return f"{NM.MAJOR_VERSION}.{NM.MINOR_VERSION}.{NM.MICRO_VERSION}"
+
+
+def _is_kernel_iface(nm_dev):
+ iface_type = get_iface_type(nm_dev)
+ return iface_type != InterfaceType.UNKNOWN and iface_type not in (
+ InterfaceType.OVS_BRIDGE,
+ InterfaceType.OVS_INTERFACE,
+ InterfaceType.OVS_PORT,
+ )
diff --git a/libnmstate/plugin.py b/libnmstate/plugin.py
index ef3874ff..e1d9ad58 100644
--- a/libnmstate/plugin.py
+++ b/libnmstate/plugin.py
@@ -128,3 +128,10 @@ class NmstatePlugin(metaclass=ABCMeta):
persistently.
"""
return []
+
+ def get_ignored_kernel_interface_names(self):
+ """
+ Return a list of kernel interface names which should be ignored
+ during verification stage.
+ """
+ return []
--
2.29.2

View File

@ -1,77 +0,0 @@
From fb9352b902f8fec813c149e02e97c7014ef6399c Mon Sep 17 00:00:00 2001
From: Fernando Fernandez Mancera <ffmancera@riseup.net>
Date: Mon, 1 Mar 2021 16:25:57 +0100
Subject: [PATCH] nm.applier: ignore not desired unmanaged ifaces
Nmstate will ignore not desired unmanaged interfaces. If an existing
unmanaged port of a bridge is not being included in the desired state
bridge port list, it will not be removed. Basically, Nmstate will not be
able to remove unmanaged ports from a bridge.
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/ifaces/ifaces.py | 11 ++++++---
libnmstate/nm/applier.py | 11 ++++++++-
tests/integration/linux_bridge_test.py | 1 +
tests/integration/nm/linux_bridge_test.py | 30 +++--------------------
4 files changed, 21 insertions(+), 32 deletions(-)
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index 5f1f548d..cfc306c6 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -193,7 +193,7 @@ class Ifaces:
if not defiend in desire state
"""
for iface in self._ifaces.values():
- if iface.is_up and iface.is_master:
+ if iface.is_desired and iface.is_up and iface.is_master:
cur_iface = self.current_ifaces.get(iface.name)
for slave_name in iface.slaves:
if cur_iface and slave_name in cur_iface.slaves:
@@ -353,14 +353,17 @@ class Ifaces:
def _remove_unknown_interface_type_slaves(self):
"""
- When master containing slaves with unknown interface type, they should
- be removed from master slave list before verifying.
+ When master containing slaves with unknown interface type or down
+ state, they should be removed from master slave list before verifying.
"""
for iface in self._ifaces.values():
if iface.is_up and iface.is_master and iface.slaves:
for slave_name in iface.slaves:
slave_iface = self._ifaces[slave_name]
- if slave_iface.type == InterfaceType.UNKNOWN:
+ if (
+ slave_iface.type == InterfaceType.UNKNOWN
+ or slave_iface.state != InterfaceState.UP
+ ):
iface.remove_slave(slave_name)
def verify(self, cur_iface_infos):
diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
index cd319ffb..da2dab5b 100644
--- a/libnmstate/nm/applier.py
+++ b/libnmstate/nm/applier.py
@@ -80,7 +80,16 @@ def apply_changes(context, net_state, save_to_disk):
_preapply_dns_fix(context, net_state)
_mark_nm_external_subordinate_changed(context, net_state)
- ifaces_desired_state = net_state.ifaces.state_to_edit
+ ifaces_desired_state = []
+ for iface in net_state.ifaces.all_ifaces.values():
+ nm_dev = context.get_nm_dev(iface.name)
+ if nm_dev and not nm_dev.get_managed() and not iface.is_desired:
+ # We don't change NM.Device from unmanaged to managed unless
+ # been asked explicitly in desire state
+ continue
+ if iface.is_changed or iface.is_desired:
+ ifaces_desired_state.append(iface.to_dict())
+
ifaces_desired_state.extend(
_create_proxy_ifaces_desired_state(ifaces_desired_state)
)
--
2.29.2

View File

@ -1,146 +0,0 @@
From 34b689a86c7cd4bcf5ab5cf7d903acba7d60f4be Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Wed, 10 Feb 2021 23:28:37 +0800
Subject: [PATCH] bridge/bond: New property `Interface.COPY_MAC_FROM`
Introducing `Interface.COPY_MAC_FROM` allowing bridge or bond
to use MAC address from specified port.
Example:
```yaml
---
interfaces:
- name: bond99
type: bond
state: up
copy-mac-from: eth1
link-aggregation:
mode: balance-rr
port:
- eth2
- eth1
```
Integration test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/ifaces/base_iface.py | 14 +++++++++++
libnmstate/ifaces/ifaces.py | 30 +++++++++++++++++++++++
libnmstate/schema.py | 1 +
libnmstate/schemas/operational-state.yaml | 4 +++
tests/integration/bond_test.py | 13 ++++++++++
tests/integration/linux_bridge_test.py | 14 +++++++++++
6 files changed, 76 insertions(+)
diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py
index b4ade867..3dbcfe52 100644
--- a/libnmstate/ifaces/base_iface.py
+++ b/libnmstate/ifaces/base_iface.py
@@ -391,6 +391,20 @@ class BaseIface:
for family, rules in route_rule_metadata.items():
self.raw[family][BaseIface.ROUTE_RULES_METADATA] = rules
+ @property
+ def copy_mac_from(self):
+ return self._info.get(Interface.COPY_MAC_FROM)
+
+ def apply_copy_mac_from(self, mac):
+ """
+ * Add MAC to original desire.
+ * Remove Interface.COPY_MAC_FROM from original desire.
+ * Update MAC of merge iface
+ """
+ self.raw[Interface.MAC] = mac
+ self._origin_info[Interface.MAC] = mac
+ self._origin_info.pop(Interface.COPY_MAC_FROM, None)
+
def _remove_empty_description(state):
if state.get(Interface.DESCRIPTION) == "":
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index cfc306c6..1e4d2231 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -101,12 +101,42 @@ class Ifaces:
self._validate_unknown_slaves()
self._mark_vf_interface_as_absent_when_sriov_vf_decrease()
self._validate_unknown_parent()
+ self._apply_copy_mac_from()
self._gen_metadata()
for iface in self._ifaces.values():
iface.pre_edit_validation_and_cleanup()
self._pre_edit_validation_and_cleanup()
+ def _apply_copy_mac_from(self):
+ for iface in self._ifaces.values():
+ if iface.type not in (
+ InterfaceType.LINUX_BRIDGE,
+ InterfaceType.BOND,
+ ):
+ continue
+ if not iface.copy_mac_from:
+ continue
+
+ if iface.copy_mac_from not in iface.slaves:
+ raise NmstateValueError(
+ f"The interface {iface.name} is holding invalid "
+ f"{Interface.COPY_MAC_FROM} property "
+ f"as {iface.copy_mac_from} is not in the port "
+ f"list: {iface.port}"
+ )
+ port_iface = self._ifaces.get(iface.copy_mac_from)
+ # TODO: bridge/bond might refering the mac from new veth in the
+ # same desire state, it too complex to support that.
+ if not port_iface:
+ raise NmstateValueError(
+ f"The interface {iface.name} is holding invalid "
+ f"{Interface.COPY_MAC_FROM} property "
+ f"as the port {iface.copy_mac_from} does not exists yet"
+ )
+
+ iface.apply_copy_mac_from(port_iface.mac)
+
def _create_virtual_slaves(self):
"""
Certain master interface could have virtual slaves which does not
diff --git a/libnmstate/schema.py b/libnmstate/schema.py
index 8a86c48d..455dbf8f 100644
--- a/libnmstate/schema.py
+++ b/libnmstate/schema.py
@@ -44,6 +44,7 @@ class Interface:
MAC = "mac-address"
MTU = "mtu"
+ COPY_MAC_FROM = "copy-mac-from"
class Route:
diff --git a/libnmstate/schemas/operational-state.yaml b/libnmstate/schemas/operational-state.yaml
index d856aa5c..8fdeaeec 100644
--- a/libnmstate/schemas/operational-state.yaml
+++ b/libnmstate/schemas/operational-state.yaml
@@ -249,6 +249,8 @@ definitions:
type: string
enum:
- bond
+ copy-mac-from:
+ type: string
link-aggregation:
type: object
properties:
@@ -267,6 +269,8 @@ definitions:
- $ref: "#/definitions/interface-linux-bridge/ro"
ro:
properties:
+ copy-mac-from:
+ type: string
bridge:
type: object
properties:
--
2.29.2

View File

@ -1,16 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEfUQ+BAINyWGvqJXerIciWuEsPqMFAl8a02UACgkQrIciWuEs
PqMSmg//T/2C0mP+zb1pnIfcPZvc8dlNgnvtTIN8EK23b6UvxrFYKuPmqRw+Dsir
N/9enPTUgQKAOtZs7BdtZlCmsaU2bWAF11UgRY+gcSkSVeG0j/kxHLG3sE4RbFiD
QPKJrqRE6m+ybTOiJ0oVXkR7f2i/AVmZE3+eZHn1TzHQoKZA8MJyExYWmk7wMkfG
KzE7jvZQ1M4Q6aZKxo4wjAkhAhFLio9HhWnl8z1bLpWWFVHqqMJ04QniDsepczCm
ISr6grG2TW6bS93lRCdDkS4yGCAYrwZ/5eyN5eOTd/et7FqG/ExFHdVaaro5I1W5
cbOYyZ1cI/avA9vCWCkC7DUJOh3i5BzzhHaxS65qqpM7fiLIrHZhaLQaLByMO48d
zo1wDEwIyNvuP4bIVwRycuDhtcLnPs5QwVbfW4HKkn4ULO+inr3lJk8V3ZZ3Ghmz
qKCrpteJTK/yJl9N2MrXxPvYYe388m4A6GGSVml4mYCd2ZMBrQ8k8fSPdlodmzVJ
J5gpeJRqm9sdrbv7tmuDfNOjAu0o9MP0/OSA0Lb5ho3pyylGnhsZ7Zkwbn2U/1b2
zgPmVWJqRhjq01VgderCOerxow7OvetNicrNg/9e7+eFAHV7VUowdKf3vCyk0+nl
WGeZLxi21w2Q2RH1ThPo6uMxL3pIp7dsbVrqM7oLpUoZzGtFnq8=
=4Hsb
-----END PGP SIGNATURE-----

View File

@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEfUQ+BAINyWGvqJXerIciWuEsPqMFAmAueEYACgkQrIciWuEs
PqPJxw//UmWdCJgoClFkEpWqWUkjMmfkGh70PoxtuOeNrAYwHv9zCHDBFjLtaDG1
18+jakwrVIlTiFHZqfz1g3o0Te86w5rKiU2y0FBtBPZ1cdcJZk2Is5wb/JJo78n+
9sWRfZmSN0SMiJ5HfzhTdZ2RsZYRSaDPSUxchRY03LzsBusxoK8swWu9oUHqmYKd
S3k4skP5ZQvpHtKzq9w1lfU4YAw42sRvXHW/++HqGgE0rypf+Wlcu9C+It9kVLCr
3rdp8iTGTg+LRg7LxFmEsRlmPpxhO25LaxjFvYSFnhE94xOt28KXeHBYs8hPPRwh
+z2w6zJ3hgIrLh10IPzePTWk//KNWDRaAJXQTCma1UE4jdL3+wbxb8H3vl5VCrBU
3fuj3JwfvFU1NuDf+yuJ0sCKzNXaAzpYYgfIaCaPdtBpg5jl2DTEfEF8QZKYuJDU
ueCo6reBAlwJzce433aSzBXshFbHG/RdD09duS3p84Dn6ljEN3GfJwUAC9s9TsAQ
U5+rWog6zMpVke/9yqwEf1KmqtLM3/+Ih30CHb3ZoPTf05KB14k0d1CLDdC9d9dy
gN0w8xjdTUXbUXW/XIvRVX9KWqyNI6lnZoL0MWzPwUmMxwPJkRpAVpLKpgyUrVpD
yjtLTFDZJNmfmbi2b0myFFcc2chaXmYlpLCP8vfRYJA3mCee6Xg=
=b/Mh
-----END PGP SIGNATURE-----

View File

@ -3,39 +3,17 @@
%define libname libnmstate %define libname libnmstate
Name: nmstate Name: nmstate
Version: 0.3.4 Version: 1.0.2
Release: 27%{?dist} Release: 5%{?dist}
Summary: Declarative network manager API Summary: Declarative network manager API
License: LGPLv2+ License: LGPLv2+
URL: https://github.com/%{srcname}/%{srcname} URL: https://github.com/%{srcname}/%{srcname}
Source0: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz Source0: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz
Source1: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz.asc Source1: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz.asc
Source2: https://www.nmstate.io/nmstate.gpg Source2: https://www.nmstate.io/nmstate.gpg
Patch1: BZ_1858762-hide_ovs_patch_port_mtu.patch Patch1: BZ_1931751-nmstate-fix-return-code.patch
Patch2: BZ_1861263-handle-external-managed-interface.patch Patch2: BZ_1931355-SRIOV-wait-VF-mount-decrease.patch
Patch3: BZ_1861668_ignore_unknown_iface.patch Patch3: BZ_1932247-nm-Don-t-touch-unmanaged-interface-unless-desired.patch
Patch4: BZ_1862025-remove_existing_profiles.patch
Patch5: BZ_1858758-fix_ovs_bond.patch
Patch6: BZ_1859844-fix_converting_memory_only.patch
Patch7: BZ_1866269-preserve_nm_uuid_in_ovsdb.patch
Patch8: BZ_1869345_ovsdb_remove_all_ports.patch
Patch9: BZ_1887349-Allow-duplicate-iface-name-in-ovs.patch
Patch10: BZ_1890497-nm-bond-Ignore-ad_actor_system-00-00-00-00-00-00.patch
Patch11: BZ_1890497-nm.ipv6-call-clear_routing_rules-when-creating-the-s.patch
Patch12: BZ_1901571_do_not_check_ovs_daemon_when_showing.patch
Patch13: BZ_1904889-do-not-remove-unmanaged-ovs-bridge.patch
Patch14: BZ_1910193-support-multiple-gateways.patch
Patch15: BZ_1908724-sriov-use-verification-retry-to-wait-VF-been-created.patch
Patch16: BZ_1916073_better-handling-for-timeout.patch
Patch17: BZ_1918712_use_uuid_for_vlan_vxlan_parent.patch
Patch18: BZ_1916073_retry_on_failure_when_activate.patch
Patch19: BZ_1931290-SRIOV-Remove-VF-interface-when-total-vfs-decrease.patch
Patch20: BZ_1931290-SRIOV-Do-not-create-VF-profiles-automatically.patch
Patch21: BZ_1931784-SR-IOV-increase-the-verification-timeout-if-SR-IOV.patch
Patch22: BZ_1931784-SR-IOV-fail-on-verification-if-total_vfs-does-not-ma.patch
Patch23: BZ_1932247-bridge-do-not-bring-up-existing-ports.patch
Patch24: BZ_1932247-nm.applier-ignore-not-desired-unmanaged-ifaces.patch
Patch25: BZ_1933538-bridge-bond-New-property-Interface.COPY_MAC_FROM.patch
BuildArch: noarch BuildArch: noarch
BuildRequires: python3-devel BuildRequires: python3-devel
BuildRequires: python3-setuptools BuildRequires: python3-setuptools
@ -62,6 +40,8 @@ Recommends: NetworkManager-config-server
# required for OVS and team support # required for OVS and team support
Suggests: NetworkManager-ovs Suggests: NetworkManager-ovs
Suggests: NetworkManager-team Suggests: NetworkManager-team
Requires: nispor
Requires: python3dist(varlink)
%package -n nmstate-plugin-ovsdb %package -n nmstate-plugin-ovsdb
Summary: nmstate plugin for OVS database manipulation Summary: nmstate plugin for OVS database manipulation
@ -106,53 +86,58 @@ gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0}
%{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_ovsdb* %{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_ovsdb*
%changelog %changelog
* Wed Mar 03 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-27 * Wed Mar 03 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.2-5
- Introduce `Interface.COPY_MAC_FROM` from bond and bridge. RHBZ#1933538 - New patch for fixing unmanaged interfaces being managed. RHBZ#1932247
* Wed Mar 03 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-26 * Tue Feb 23 2021 Gris Ge <fge@redhat.com> - 1.0.2-4
- New patch do not activating SR-IOV VFs automatically. RHBZ#1931290 - New patch for SRIOV decrease VF amount. RHBZ#1931355
- New patch fixing Nmstate waits when SR-IOV VFs are decreased. RHBZ#1931784
- New patch fixing Nmstate activating unmanaged bridge ports. RHBZ#1932247
* Fri Feb 05 2021 Gris Ge <fge@redhat.com> - 0.3.4-25 * Tue Feb 23 2021 Gris Ge <fge@redhat.com> - 1.0.2-3
- Remove patch for fixing the autoconnect on existing profile. RHBZ#1918712 - Fix actiation failure when decrease VF mount on i40e. RHBZ#1931355
* Thu Feb 04 2021 Gris Ge <fge@redhat.com> - 0.3.4-24 * Tue Feb 23 2021 Gris Ge <fge@redhat.com> - 1.0.2-2
- New patch fixing activation failure with 1000 interfaces. RHBZ#1916073 - Fix nmstatectl return code of `set` command. RHBZ#1931751
* Wed Feb 03 2021 Gris Ge <fge@redhat.com> - 0.3.4-23 * Fri Feb 19 2021 Gris Ge <fge@redhat.com> - 1.0.2-1
- Enforcing autoconnect on existing profile. RHBZ#1918712 - Upgrade to 1.0.2.
* Fri Jan 22 2021 Gris Ge <fge@redhat.com> - 0.3.4-22 * Wed Feb 10 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.2-0.3
- Fix creating VLAN/VxLAN over interface also used for OVS. RHBZ#1918712 - Fix sources name
* Sun Jan 17 2021 Gris Ge <fge@redhat.com> - 0.3.4-21 * Wed Feb 10 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.2-0.2
- Additional patch for profile deactivation and deletion. RHBZ#1916073 - Upgrade to 1.0.2 alpha 2
* Thu Jan 14 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-20 * Tue Jan 26 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.2-0.1
- Better handling for timeout on activation. RHBZ#1916073 - Upgrade to 1.0.2 alpha 1
* Mon Jan 04 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-19 * Tue Jan 19 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.1-1
- Use verification retry to wait SR-IOV VF been created. RHBZ#1908724 - Upgrade to 1.0.1. RHBZ#1881287
* Wed Dec 23 2020 Gris Ge <fge@redhat.com> - 0.3.4-18 * Tue Jan 05 2021 Gris Ge <fge@redhat.com> - 1.0.1-0.1
- Support multiple gateways. RHBZ#1910193 - Upgrade to 1.0.1 alpha 1
* Mon Dec 07 2020 Gris Ge <fge@redhat.com> - 0.3.4-17 * Tue Dec 08 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.0-1
- Rebuild to retrigger the CI gating. RHBZ#1904889 - Upgrade to 1.0.0
* Mon Dec 07 2020 Gris Ge <fge@redhat.com> - 0.3.4-16 * Mon Nov 16 2020 Gris Ge <fge@redhat.com> - 1.0.0-0.1
- Don't remove unmanaged OVS interface. RHBZ#1904889 - Upgrade to 1.0.0 alpha 1
* Thu Nov 26 2020 Gris Ge <fge@redhat.com> - 0.3.4-15 * Wed Oct 28 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.4.1-2
- Fix `libnmstate.show()` in container with OVS bridge. RHBZ#1901571 - Allow VRF port to hold IP information
* Wed Nov 04 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-14 * Thu Oct 22 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.4.1-1
- Ignore bond zero ad_actor_system. RHBZ#1890497 - Upgrade to 0.4.1
- Fix clear IPv6 routing rules.
* Wed Oct 14 2020 Gris Ge <fge@redhat.com> - 0.3.4-13 * Tue Oct 20 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.4.0-3
- Allowing duplicate interface name of ovs. RHBZ#1887349 - Add nispor as a dependency for CI gating
* Tue Oct 20 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.4.0-2
- Rebuild for CI gating
- Remove old patches from the repository
* Mon Sep 14 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.4.0-1
- Upgrade to 0.4.0
- Sync. up with upstream spec file.
* Tue Aug 18 2020 Gris Ge <fge@redhat.com> - 0.3.4-12 * Tue Aug 18 2020 Gris Ge <fge@redhat.com> - 0.3.4-12
- New patch: OVSDB: Allowing remove all OVS ports. RHBZ#1869345 - New patch: OVSDB: Allowing remove all OVS ports. RHBZ#1869345