diff --git a/.gitignore b/.gitignore index da093cf..4e6d7d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -SOURCES/gpgkey-F7910D93CA83D77348595C0E899014C0463C12BB.gpg -SOURCES/nmstate-0.2.10.tar.gz +SOURCES/nmstate-0.3.4.tar.gz +SOURCES/nmstate.gpg diff --git a/.nmstate.metadata b/.nmstate.metadata index cd27192..3f697a6 100644 --- a/.nmstate.metadata +++ b/.nmstate.metadata @@ -1,2 +1,2 @@ -7bcc63976a8d449b3adc57f40d7a476106889042 SOURCES/gpgkey-F7910D93CA83D77348595C0E899014C0463C12BB.gpg -bc4887583287e29494aca5dbbca58699dc5cfc3d SOURCES/nmstate-0.2.10.tar.gz +d732a1ccb1dfc54741a9d602179c809c3223af3a SOURCES/nmstate-0.3.4.tar.gz +b5f872551d434e2c62b30d70471efaeede83ab44 SOURCES/nmstate.gpg diff --git a/SOURCES/BZ_1858758-fix_ovs_bond.patch b/SOURCES/BZ_1858758-fix_ovs_bond.patch new file mode 100644 index 0000000..d71cfcd --- /dev/null +++ b/SOURCES/BZ_1858758-fix_ovs_bond.patch @@ -0,0 +1,40 @@ +From 862e669fcfe02b49c0e24af210d6466197962b97 Mon Sep 17 00:00:00 2001 +From: Gris Ge +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 +--- + 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 + diff --git a/SOURCES/BZ_1858762-hide_ovs_patch_port_mtu.patch b/SOURCES/BZ_1858762-hide_ovs_patch_port_mtu.patch new file mode 100644 index 0000000..3e5bea9 --- /dev/null +++ b/SOURCES/BZ_1858762-hide_ovs_patch_port_mtu.patch @@ -0,0 +1,105 @@ +From bc2f8445d493f8a5a4ff1ceead13d2b3ac5325cc Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +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 +--- + 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 +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 +Signed-off-by: Fernando Fernandez Mancera +--- + 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 . + # + ++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 + diff --git a/SOURCES/BZ_1859844-fix_converting_memory_only.patch b/SOURCES/BZ_1859844-fix_converting_memory_only.patch new file mode 100644 index 0000000..ef80e65 --- /dev/null +++ b/SOURCES/BZ_1859844-fix_converting_memory_only.patch @@ -0,0 +1,59 @@ +From fc7e6b2329409b95ab1726b7b65f14c284bf67ab Mon Sep 17 00:00:00 2001 +From: Gris Ge +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 +--- + 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 + diff --git a/SOURCES/BZ_1861263-handle-external-managed-interface.patch b/SOURCES/BZ_1861263-handle-external-managed-interface.patch new file mode 100644 index 0000000..dc59dc4 --- /dev/null +++ b/SOURCES/BZ_1861263-handle-external-managed-interface.patch @@ -0,0 +1,65 @@ +From ea7f304cc1ad32c3f2c25b49bf6b2663f348496a Mon Sep 17 00:00:00 2001 +From: Gris Ge +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 +--- + 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 + diff --git a/SOURCES/BZ_1861668_ignore_unknown_iface.patch b/SOURCES/BZ_1861668_ignore_unknown_iface.patch new file mode 100644 index 0000000..665dd7d --- /dev/null +++ b/SOURCES/BZ_1861668_ignore_unknown_iface.patch @@ -0,0 +1,200 @@ +From a8590744fbdd4bce9ab340ac49a7add31727b990 Mon Sep 17 00:00:00 2001 +From: Gris Ge +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 + diff --git a/SOURCES/BZ_1862025-remove_existing_profiles.patch b/SOURCES/BZ_1862025-remove_existing_profiles.patch new file mode 100644 index 0000000..2a07082 --- /dev/null +++ b/SOURCES/BZ_1862025-remove_existing_profiles.patch @@ -0,0 +1,68 @@ +From 3c5337a22273717df6fb51818216816bbff77035 Mon Sep 17 00:00:00 2001 +From: Gris Ge +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 +--- + 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 + diff --git a/SOURCES/BZ_1866269-preserve_nm_uuid_in_ovsdb.patch b/SOURCES/BZ_1866269-preserve_nm_uuid_in_ovsdb.patch new file mode 100644 index 0000000..214aa8c --- /dev/null +++ b/SOURCES/BZ_1866269-preserve_nm_uuid_in_ovsdb.patch @@ -0,0 +1,120 @@ +From 21e06fd5af76cc1fe65497222a04c1cffa2bc546 Mon Sep 17 00:00:00 2001 +From: Gris Ge +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 +--- + 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 + diff --git a/SOURCES/BZ_1869345_ovsdb_remove_all_ports.patch b/SOURCES/BZ_1869345_ovsdb_remove_all_ports.patch new file mode 100644 index 0000000..e1a1ac3 --- /dev/null +++ b/SOURCES/BZ_1869345_ovsdb_remove_all_ports.patch @@ -0,0 +1,44 @@ +From 913b739c8fea8e9b14d3785371c8e4f48723dbd6 Mon Sep 17 00:00:00 2001 +From: Gris Ge +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 +--- + 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 + diff --git a/SOURCES/nmstate-0.2.10.tar.gz.asc b/SOURCES/nmstate-0.2.10.tar.gz.asc deleted file mode 100644 index 6a2ad0a..0000000 --- a/SOURCES/nmstate-0.2.10.tar.gz.asc +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN PGP SIGNATURE----- -Version: GnuPG v2.0.22 (GNU/Linux) - -iQIcBAABAgAGBQJenrWKAAoJEImQFMBGPBK7qPwQAMNvomlJRYIcU+/aAz87mDk3 -UGBV6JpXraID7T2xejHL4nEynuT4ib/iguLTrrkBXXDMgVCWsHJwqlL4Q0h9ptoO -M2/EJRmXZTEfJUlf1kWk6ODoQ5NJNpCLC+Ip0BMzO0zJysjdKsqDvSm5Cw650syE -8SI69Jj/UpOd/Nv8pgHsbhjejMcYIC6CfLiinEB1q/AtZC+tvvrxfmZzp+g3mbaM -tm1yNdsQRVB4w41h4yRFO44VWWg9AN+LoxrCUePONNCKbecf0tEBCnrBrK2hxdak -LGrAHMSN/F2MPaayULhyyZbbRQIsF2099XJs7e9uYayiHyOgh1N/dX+TC9ridFWM -XlnU2SvVHCUwtwr5CGAOW7VWcggoE48/QDl8RVj53gHat29Ry/GM9gbpSWVneFqi -InovzvfjNV0V/+l7Ug21LkkBHsIYJ22Fj6X7IEekijCz+x4zOcl2xHY7SKiVcnVs -/sYO0vBVnPUVS91CMVXvsUYmRfnDkMsiKvmFxVud1WpZeTgd1AW4zg3M1ueelDFi -c8aqYZr3/ziBvR1wjCz8kKYp3VhCQWRd4PjS6fqfY+cuuVxGdyzxBFYqK/h+D2AT -mk23RDMNpx+vGfa2cVLRMOSGfADxelRaRdo1TS2qKTpjE0ukM7FWhY3rRONiw4tL -5HKl5RrTQRfqMk2onmX3 -=cS1L ------END PGP SIGNATURE----- diff --git a/SOURCES/nmstate-0.3.4.tar.gz.asc b/SOURCES/nmstate-0.3.4.tar.gz.asc new file mode 100644 index 0000000..ac2c220 --- /dev/null +++ b/SOURCES/nmstate-0.3.4.tar.gz.asc @@ -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----- diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec index d6f79eb..98f1636 100644 --- a/SPECS/nmstate.spec +++ b/SPECS/nmstate.spec @@ -3,14 +3,22 @@ %define libname libnmstate Name: nmstate -Version: 0.2.10 -Release: 1%{?dist} +Version: 0.3.4 +Release: 12%{?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: gpgkey-F7910D93CA83D77348595C0E899014C0463C12BB.gpg +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 BuildArch: noarch BuildRequires: python3-devel BuildRequires: python3-setuptools @@ -27,7 +35,7 @@ provider support on the southbound. %package -n python3-%{libname} Summary: nmstate Python 3 API library -Requires: NetworkManager-libnm >= 1:1.22.8 +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 @@ -38,12 +46,22 @@ Recommends: NetworkManager-config-server 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 -gpgv2 --keyring %{SOURCE2} %{SOURCE1} %{SOURCE0} +gpg2 --import --import-options import-export,import-minimal %{SOURCE2} > ./gpgkey-mantainers.gpg +gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0} %autosetup -p1 %build @@ -63,10 +81,85 @@ gpgv2 --keyring %{SOURCE2} %{SOURCE1} %{SOURCE0} %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 -* Tue Apr 21 2020 Fernando Fernandez Mancera - 0.2.10-1 -- Upgrade to 0.2.10 +* Tue Aug 18 2020 Gris Ge - 0.3.4-12 +- New patch: OVSDB: Allowing remove all OVS ports. RHBZ#1869345 + +* Tue Aug 18 2020 Gris Ge - 0.3.4-11 +- OVSDB: Allowing remove all OVS ports. RHBZ#1869345 + +* Thu Aug 06 2020 Gris Ge - 0.3.4-10 +- OVSDB: Preserv old external_ids. RHBZ#1866269 + +* Tue Aug 04 2020 Gris Ge - 0.3.4-9 +- Fix converting memory only profile to persistent. RHBZ#1859844 + +* Mon Aug 03 2020 Gris Ge - 0.3.4-8 +- Fix failure when adding ovs bond to existing bridge. RHBZ#1858758 + +* Thu Jul 30 2020 Gris Ge - 0.3.4-7 +- Remove existing inactivate NM profiles. RHBZ#1862025 + +* Wed Jul 29 2020 Gris Ge - 0.3.4-6 +- New build to retrigger the CI gating. + +* Wed Jul 29 2020 Gris Ge - 0.3.4-5 +- Use new patch. RHBZ#1861668 + +* Wed Jul 29 2020 Gris Ge - 0.3.4-4 +- Ignore unknown interface. RHBZ#1861668 + +* Tue Jul 28 2020 Gris Ge - 0.3.4-3 +- Add support NetworkManaged exteranl managed interface. RHBZ#1861263 + +* Tue Jul 28 2020 Gris Ge - 0.3.4-2 +- Hide MTU for OVS patch port. RHBZ#1858762 + +* Sat Jul 25 2020 Fernando Fernandez Mancera - 0.3.4-1 +- Upgrade to 0.3.4 + +* Fri Jul 24 2020 Gris Ge - 0.3.3-3 +- Allowing child been marked absent. RHBZ#1859148 + +* Mon Jul 06 2020 Fernando Fernandez Mancera - 0.3.3-2 +- Fix bug 1850698 + +* Thu Jul 02 2020 Fernando Fernandez Mancera - 0.3.3-1 +- Upgrade to 0.3.3 + +* Mon Jun 29 2020 Gris Ge - 0.3.2-6 +- Improve performance by remove unneeded calls. RHBZ#1820009 + +* Mon Jun 29 2020 Gris Ge - 0.3.2-5 +- Sort the pretty state with priority. RHBZ#1806474 + +* Mon Jun 29 2020 Gris Ge - 0.3.2-4 +- Canonicalize IP address. RHBZ#1816612 + +* Mon Jun 29 2020 Gris Ge - 0.3.2-3 +- Improve VLAN MTU error message. RHBZ#1788763 + +* Mon Jun 29 2020 Gris Ge - 0.3.2-2 +- Fix bug 1850698 + +* Mon Jun 15 2020 Fernando Fernandez Mancera - 0.3.2-1 +- Upgrade to 0.3.2 +- Sync. up with upstream spec file + +* Thu Jun 11 2020 Gris Ge - 0.3.1-1 +- Upgrade to 0.3.1 + +* Wed May 13 2020 Fernando Fernandez Mancera - 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 - 0.2.9-1 - Upgrade to 0.2.9