import nmstate-1.0.2-5.el8
This commit is contained in:
parent
a764ed7069
commit
8c0f309116
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
||||
SOURCES/nmstate-0.3.4.tar.gz
|
||||
SOURCES/nmstate-1.0.2.tar.gz
|
||||
SOURCES/nmstate.gpg
|
||||
|
@ -1,2 +1,2 @@
|
||||
d732a1ccb1dfc54741a9d602179c809c3223af3a SOURCES/nmstate-0.3.4.tar.gz
|
||||
eeda8a0238732e5dc37e2217ed6e316f76c93145 SOURCES/nmstate-1.0.2.tar.gz
|
||||
b5f872551d434e2c62b30d70471efaeede83ab44 SOURCES/nmstate.gpg
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}")
|
||||
)
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
166
SOURCES/BZ_1931355-SRIOV-wait-VF-mount-decrease.patch
Normal file
166
SOURCES/BZ_1931355-SRIOV-wait-VF-mount-decrease.patch
Normal 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
|
||||
|
29
SOURCES/BZ_1931751-nmstate-fix-return-code.patch
Normal file
29
SOURCES/BZ_1931751-nmstate-fix-return-code.patch
Normal 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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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-----
|
16
SOURCES/nmstate-1.0.2.tar.gz.asc
Normal file
16
SOURCES/nmstate-1.0.2.tar.gz.asc
Normal 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-----
|
@ -3,39 +3,17 @@
|
||||
%define libname libnmstate
|
||||
|
||||
Name: nmstate
|
||||
Version: 0.3.4
|
||||
Release: 27%{?dist}
|
||||
Version: 1.0.2
|
||||
Release: 5%{?dist}
|
||||
Summary: Declarative network manager API
|
||||
License: LGPLv2+
|
||||
URL: https://github.com/%{srcname}/%{srcname}
|
||||
Source0: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz
|
||||
Source1: %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz.asc
|
||||
Source2: https://www.nmstate.io/nmstate.gpg
|
||||
Patch1: BZ_1858762-hide_ovs_patch_port_mtu.patch
|
||||
Patch2: BZ_1861263-handle-external-managed-interface.patch
|
||||
Patch3: BZ_1861668_ignore_unknown_iface.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
|
||||
Patch1: BZ_1931751-nmstate-fix-return-code.patch
|
||||
Patch2: BZ_1931355-SRIOV-wait-VF-mount-decrease.patch
|
||||
Patch3: BZ_1932247-nm-Don-t-touch-unmanaged-interface-unless-desired.patch
|
||||
BuildArch: noarch
|
||||
BuildRequires: python3-devel
|
||||
BuildRequires: python3-setuptools
|
||||
@ -62,6 +40,8 @@ Recommends: NetworkManager-config-server
|
||||
# required for OVS and team support
|
||||
Suggests: NetworkManager-ovs
|
||||
Suggests: NetworkManager-team
|
||||
Requires: nispor
|
||||
Requires: python3dist(varlink)
|
||||
|
||||
%package -n nmstate-plugin-ovsdb
|
||||
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*
|
||||
|
||||
%changelog
|
||||
* Wed Mar 03 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-27
|
||||
- Introduce `Interface.COPY_MAC_FROM` from bond and bridge. RHBZ#1933538
|
||||
* Wed Mar 03 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.2-5
|
||||
- New patch for fixing unmanaged interfaces being managed. RHBZ#1932247
|
||||
|
||||
* Wed Mar 03 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-26
|
||||
- New patch do not activating SR-IOV VFs automatically. RHBZ#1931290
|
||||
- New patch fixing Nmstate waits when SR-IOV VFs are decreased. RHBZ#1931784
|
||||
- New patch fixing Nmstate activating unmanaged bridge ports. RHBZ#1932247
|
||||
* Tue Feb 23 2021 Gris Ge <fge@redhat.com> - 1.0.2-4
|
||||
- New patch for SRIOV decrease VF amount. RHBZ#1931355
|
||||
|
||||
* Fri Feb 05 2021 Gris Ge <fge@redhat.com> - 0.3.4-25
|
||||
- Remove patch for fixing the autoconnect on existing profile. RHBZ#1918712
|
||||
* Tue Feb 23 2021 Gris Ge <fge@redhat.com> - 1.0.2-3
|
||||
- Fix actiation failure when decrease VF mount on i40e. RHBZ#1931355
|
||||
|
||||
* Thu Feb 04 2021 Gris Ge <fge@redhat.com> - 0.3.4-24
|
||||
- New patch fixing activation failure with 1000 interfaces. RHBZ#1916073
|
||||
* Tue Feb 23 2021 Gris Ge <fge@redhat.com> - 1.0.2-2
|
||||
- Fix nmstatectl return code of `set` command. RHBZ#1931751
|
||||
|
||||
* Wed Feb 03 2021 Gris Ge <fge@redhat.com> - 0.3.4-23
|
||||
- Enforcing autoconnect on existing profile. RHBZ#1918712
|
||||
* Fri Feb 19 2021 Gris Ge <fge@redhat.com> - 1.0.2-1
|
||||
- Upgrade to 1.0.2.
|
||||
|
||||
* Fri Jan 22 2021 Gris Ge <fge@redhat.com> - 0.3.4-22
|
||||
- Fix creating VLAN/VxLAN over interface also used for OVS. RHBZ#1918712
|
||||
* Wed Feb 10 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.2-0.3
|
||||
- Fix sources name
|
||||
|
||||
* Sun Jan 17 2021 Gris Ge <fge@redhat.com> - 0.3.4-21
|
||||
- Additional patch for profile deactivation and deletion. RHBZ#1916073
|
||||
* Wed Feb 10 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.2-0.2
|
||||
- Upgrade to 1.0.2 alpha 2
|
||||
|
||||
* Thu Jan 14 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-20
|
||||
- Better handling for timeout on activation. RHBZ#1916073
|
||||
* Tue Jan 26 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.2-0.1
|
||||
- Upgrade to 1.0.2 alpha 1
|
||||
|
||||
* Mon Jan 04 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-19
|
||||
- Use verification retry to wait SR-IOV VF been created. RHBZ#1908724
|
||||
* Tue Jan 19 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.1-1
|
||||
- Upgrade to 1.0.1. RHBZ#1881287
|
||||
|
||||
* Wed Dec 23 2020 Gris Ge <fge@redhat.com> - 0.3.4-18
|
||||
- Support multiple gateways. RHBZ#1910193
|
||||
* Tue Jan 05 2021 Gris Ge <fge@redhat.com> - 1.0.1-0.1
|
||||
- Upgrade to 1.0.1 alpha 1
|
||||
|
||||
* Mon Dec 07 2020 Gris Ge <fge@redhat.com> - 0.3.4-17
|
||||
- Rebuild to retrigger the CI gating. RHBZ#1904889
|
||||
* Tue Dec 08 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.0.0-1
|
||||
- Upgrade to 1.0.0
|
||||
|
||||
* Mon Dec 07 2020 Gris Ge <fge@redhat.com> - 0.3.4-16
|
||||
- Don't remove unmanaged OVS interface. RHBZ#1904889
|
||||
* Mon Nov 16 2020 Gris Ge <fge@redhat.com> - 1.0.0-0.1
|
||||
- Upgrade to 1.0.0 alpha 1
|
||||
|
||||
* Thu Nov 26 2020 Gris Ge <fge@redhat.com> - 0.3.4-15
|
||||
- Fix `libnmstate.show()` in container with OVS bridge. RHBZ#1901571
|
||||
* Wed Oct 28 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.4.1-2
|
||||
- Allow VRF port to hold IP information
|
||||
|
||||
* Wed Nov 04 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-14
|
||||
- Ignore bond zero ad_actor_system. RHBZ#1890497
|
||||
- Fix clear IPv6 routing rules.
|
||||
* Thu Oct 22 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.4.1-1
|
||||
- Upgrade to 0.4.1
|
||||
|
||||
* Wed Oct 14 2020 Gris Ge <fge@redhat.com> - 0.3.4-13
|
||||
- Allowing duplicate interface name of ovs. RHBZ#1887349
|
||||
* Tue Oct 20 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.4.0-3
|
||||
- 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
|
||||
- New patch: OVSDB: Allowing remove all OVS ports. RHBZ#1869345
|
||||
|
Loading…
Reference in New Issue
Block a user