import nmstate-0.3.4-25.el8_3

This commit is contained in:
CentOS Sources 2021-02-16 02:43:23 -05:00 committed by Andrew Lukoshko
commit 654b4d10bc
22 changed files with 2349 additions and 0 deletions

2
.gitignore vendored Normal file
View File

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

2
.nmstate.metadata Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,16 @@
-----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-----

326
SPECS/nmstate.spec Normal file
View File

@ -0,0 +1,326 @@
%?python_enable_dependency_generator
%define srcname nmstate
%define libname libnmstate
Name: nmstate
Version: 0.3.4
Release: 25%{?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
BuildArch: noarch
BuildRequires: python3-devel
BuildRequires: python3-setuptools
BuildRequires: gnupg2
Requires: python3-setuptools
Requires: python3-%{libname} = %{?epoch:%{epoch}:}%{version}-%{release}
%description
Nmstate is a library with an accompanying command line tool that manages host
networking settings in a declarative manner and aimed to satisfy enterprise
needs to manage host networking through a northbound declarative API and multi
provider support on the southbound.
%package -n python3-%{libname}
Summary: nmstate Python 3 API library
Requires: NetworkManager-libnm >= 1:1.26.0
# Use Recommends for NetworkManager because only access to NM DBus is required,
# but NM could be running on a different host
Recommends: NetworkManager
# Avoid automatically generated profiles
Recommends: NetworkManager-config-server
# Use Suggests for NetworkManager-ovs and NetworkManager-team since it is only
# required for OVS and team support
Suggests: NetworkManager-ovs
Suggests: NetworkManager-team
%package -n nmstate-plugin-ovsdb
Summary: nmstate plugin for OVS database manipulation
Requires: python3-%{libname} = %{?epoch:%{epoch}:}%{version}-%{release}
# The python-openvswitch rpm pacakge is not in the same repo with nmstate,
# hence state it as Recommends, no requires.
Recommends: python3dist(ovs)
%description -n python3-%{libname}
This package contains the Python 3 library for nmstate.
%description -n nmstate-plugin-ovsdb
This package contains the nmstate plugin for OVS database manipulation.
%prep
gpg2 --import --import-options import-export,import-minimal %{SOURCE2} > ./gpgkey-mantainers.gpg
gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0}
%autosetup -p1
%build
%py3_build
%install
%py3_install
%files
%doc README.md
%doc examples/
%{_mandir}/man8/nmstatectl.8*
%{python3_sitelib}/nmstatectl
%{_bindir}/nmstatectl
%files -n python3-%{libname}
%license LICENSE
%{python3_sitelib}/%{libname}
%{python3_sitelib}/%{srcname}-*.egg-info/
%exclude %{python3_sitelib}/%{libname}/plugins/nmstate_plugin_*
%exclude %{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_*
%files -n nmstate-plugin-ovsdb
%{python3_sitelib}/%{libname}/plugins/nmstate_plugin_ovsdb*
%{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_ovsdb*
%changelog
* Fri Feb 05 2021 Gris Ge <fge@redhat.com> - 0.3.4-25
- Remove patch for fixing the autoconnect on existing profile. RHBZ#1918712
* Thu Feb 04 2021 Gris Ge <fge@redhat.com> - 0.3.4-24
- New patch fixing activation failure with 1000 interfaces. RHBZ#1916073
* Wed Feb 03 2021 Gris Ge <fge@redhat.com> - 0.3.4-23
- Enforcing autoconnect on existing profile. RHBZ#1918712
* 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
* Sun Jan 17 2021 Gris Ge <fge@redhat.com> - 0.3.4-21
- Additional patch for profile deactivation and deletion. RHBZ#1916073
* Thu Jan 14 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-20
- Better handling for timeout on activation. RHBZ#1916073
* 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
* Wed Dec 23 2020 Gris Ge <fge@redhat.com> - 0.3.4-18
- Support multiple gateways. RHBZ#1910193
* Mon Dec 07 2020 Gris Ge <fge@redhat.com> - 0.3.4-17
- Rebuild to retrigger the CI gating. RHBZ#1904889
* Mon Dec 07 2020 Gris Ge <fge@redhat.com> - 0.3.4-16
- Don't remove unmanaged OVS interface. RHBZ#1904889
* Thu Nov 26 2020 Gris Ge <fge@redhat.com> - 0.3.4-15
- Fix `libnmstate.show()` in container with OVS bridge. RHBZ#1901571
* 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.
* Wed Oct 14 2020 Gris Ge <fge@redhat.com> - 0.3.4-13
- Allowing duplicate interface name of ovs. RHBZ#1887349
* Tue Aug 18 2020 Gris Ge <fge@redhat.com> - 0.3.4-12
- New patch: OVSDB: Allowing remove all OVS ports. RHBZ#1869345
* Tue Aug 18 2020 Gris Ge <fge@redhat.com> - 0.3.4-11
- OVSDB: Allowing remove all OVS ports. RHBZ#1869345
* Thu Aug 06 2020 Gris Ge <fge@redhat.com> - 0.3.4-10
- OVSDB: Preserv old external_ids. RHBZ#1866269
* Tue Aug 04 2020 Gris Ge <fge@redhat.com> - 0.3.4-9
- Fix converting memory only profile to persistent. RHBZ#1859844
* Mon Aug 03 2020 Gris Ge <fge@redhat.com> - 0.3.4-8
- Fix failure when adding ovs bond to existing bridge. RHBZ#1858758
* Thu Jul 30 2020 Gris Ge <fge@redhat.com> - 0.3.4-7
- Remove existing inactivate NM profiles. RHBZ#1862025
* Wed Jul 29 2020 Gris Ge <fge@redhat.com> - 0.3.4-6
- New build to retrigger the CI gating.
* Wed Jul 29 2020 Gris Ge <fge@redhat.com> - 0.3.4-5
- Use new patch. RHBZ#1861668
* Wed Jul 29 2020 Gris Ge <fge@redhat.com> - 0.3.4-4
- Ignore unknown interface. RHBZ#1861668
* Tue Jul 28 2020 Gris Ge <fge@redhat.com> - 0.3.4-3
- Add support NetworkManaged exteranl managed interface. RHBZ#1861263
* Tue Jul 28 2020 Gris Ge <fge@redhat.com> - 0.3.4-2
- Hide MTU for OVS patch port. RHBZ#1858762
* Sat Jul 25 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-1
- Upgrade to 0.3.4
* Fri Jul 24 2020 Gris Ge <fge@redhat.com> - 0.3.3-3
- Allowing child been marked absent. RHBZ#1859148
* Mon Jul 06 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.3-2
- Fix bug 1850698
* Thu Jul 02 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.3-1
- Upgrade to 0.3.3
* Mon Jun 29 2020 Gris Ge <fge@redhat.com> - 0.3.2-6
- Improve performance by remove unneeded calls. RHBZ#1820009
* Mon Jun 29 2020 Gris Ge <fge@redhat.com> - 0.3.2-5
- Sort the pretty state with priority. RHBZ#1806474
* Mon Jun 29 2020 Gris Ge <fge@redhat.com> - 0.3.2-4
- Canonicalize IP address. RHBZ#1816612
* Mon Jun 29 2020 Gris Ge <fge@redhat.com> - 0.3.2-3
- Improve VLAN MTU error message. RHBZ#1788763
* Mon Jun 29 2020 Gris Ge <fge@redhat.com> - 0.3.2-2
- Fix bug 1850698
* Mon Jun 15 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.2-1
- Upgrade to 0.3.2
- Sync. up with upstream spec file
* Thu Jun 11 2020 Gris Ge <fge@redhat.com> - 0.3.1-1
- Upgrade to 0.3.1
* Wed May 13 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.0-1
- Upgrade to 0.3.0
- Sync. up with upstream spec file.
- Update signature verification.
* Tue Mar 31 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.2.9-1
- Upgrade to 0.2.9
* Wed Mar 25 2020 Gris Ge <fge@redhat.com> - 0.2.6-6
- Support 3+ DNS name server(IPv4 only or IPv6 only). RHBZ #1816043
* Fri Mar 20 2020 Gris Ge <fge@redhat.com> - 0.2.6-5
- Support static DNS with DHCP. RHBZ #1815112
* Thu Mar 12 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.2.6-4.8
- Fix bond mac and options regression. RHBZ #1809330
* Mon Mar 09 2020 Gris Ge <fge@redhat.com> - 0.2.6-3.8
- Fix change bond mode. RHBZ #1809330
* Mon Mar 02 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.2.6-2.7
- Fix cmd stuck when trying to create ovs-bond. RHBZ #1806249.
* Tue Feb 25 2020 Gris Ge <fge@redhat.com> - 0.2.6-1
- Upgrade to 0.2.6
* Thu Feb 20 2020 Gris Ge <fge@redhat.com> - 0.2.5-1
- Upgrade to 0.2.5
* Thu Feb 13 2020 Gris Ge <fge@redhat.com> - 0.2.4-2
- Fix failure when editing existing OVS interface. RHBZ #1786935
* Thu Feb 13 2020 Gris Ge <fge@redhat.com> - 0.2.4-1
- Upgrade to 0.2.4
* Wed Feb 05 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.2.3-1
- Upgrade to 0.2.3
* Tue Feb 04 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.2.2-3
- Fix the incorrect source
* Tue Feb 04 2020 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.2.2-2
- Upgrade to 0.2.2
* Wed Jan 22 2020 Gris Ge <fge@redhat.com> - 0.2.0-3.1
- Fix the memeory leak of NM.Client. RHBZ #1784707
* Mon Dec 02 2019 Gris Ge <fge@redhat.com> - 0.2.0-2
- Fix the incorrect source tarbal.
* Mon Dec 02 2019 Gris Ge <fge@redhat.com> - 0.2.0-1
- Upgrade to nmstate 0.2.0
* Mon Dec 02 2019 Gris Ge <fge@redhat.com> - 0.1.1-4
- Fix the problem found by CI gating.
* Mon Dec 02 2019 Gris Ge <fge@redhat.com> - 0.1.1-3
- Bump dist number as RHEL 8.1.1 took 0.1.1-2.
* Mon Dec 02 2019 Gris Ge <fge@redhat.com> - 0.1.1-2
- Upgrade to nmstate 0.1.1.
* Tue Sep 10 2019 Gris Ge <fge@redhat.com> - 0.0.8-15
- Detach slaves without deleting them: RHBZ #1749632
* Fri Sep 06 2019 Gris Ge <fge@redhat.com> - 0.0.8-14
- Preserve (dynamic) IPv6 address base on MAC address: RHBZ #1748825
* Fri Sep 06 2019 Gris Ge <fge@redhat.com> - 0.0.8-13
- Prioritize master interfaces activaction: RHBZ #1749314
* Mon Sep 02 2019 Gris Ge <fge@redhat.com> - 0.0.8-12
- Fix slave activatoin race: RHBZ #1741440
* Mon Sep 02 2019 Gris Ge <fge@redhat.com - 0.0.8-11
- Add NetworkManager-config-server dependency: Fix RHBZ #1740085
* Thu Aug 15 2019 Gris Ge <fge@redhat.com> - 0.0.8-10
- Fix RHBZ #1740125
* Wed Aug 14 2019 Gris Ge <fge@redhat.com> - 0.0.8-9
- Fix RHBZ #1741049
* Wed Aug 14 2019 Gris Ge <fge@redhat.com> - 0.0.8-8
- Fix RHBZ #1740584
* Tue Aug 13 2019 Gris Ge <fge@redhat.com> - 0.0.8-7
- Fix RHBZ #1740554
* Tue Aug 13 2019 Gris Ge <fge@redhat.com> - 0.0.8-6
- Bump release tag as CNV took the -5.
* Tue Aug 13 2019 Gris Ge <fge@redhat.com> - 0.0.8-5
- Bump release tag as CNV took the -4.
* Tue Aug 13 2019 Gris Ge <fge@redhat.com> - 0.0.8-4
- Disable reapply on ipv6 to fix bug 1738101.
* Fri Jul 26 2019 Gris Ge <fge@redhat.com> - 0.0.8-3
- Fix the license to meet Fedora/RHEL guideline.
* Fri Jul 26 2019 Gris Ge <fge@redhat.com> - 0.0.8-2
- Relicense to LGPL2.1+.
* Fri Jul 26 2019 Gris Ge <fge@redhat.com> - 0.0.8-1
- Upgrade to 0.0.8.
* Fri Jun 14 2019 Gris Ge <fge@redhat.com> - 0.0.7-1
- Upgrade to 0.0.7.
* Mon Apr 22 2019 Gris Ge <fge@redhat.com> - 0.0.5-3
- Add missing runtime dependency.
* Thu Mar 21 2019 Gris Ge <fge@redhat.com> - 0.0.5-2
- Rebuild to enable CI testing.
* Mon Mar 18 2019 Gris Ge <fge@redhat.com> - 0.0.5-1
- Initial release