diff --git a/SOURCES/BZ_1817466-use-kernel-runtime-bridge-status.patch b/SOURCES/BZ_1817466-use-kernel-runtime-bridge-status.patch new file mode 100644 index 0000000..d75d82e --- /dev/null +++ b/SOURCES/BZ_1817466-use-kernel-runtime-bridge-status.patch @@ -0,0 +1,112 @@ +From 3b5cfe36b1b754369cb8d3cc0d461068bf6bb8aa Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Wed, 25 Mar 2020 12:21:15 +0100 +Subject: [PATCH 06/10] nm.bridge: get ports from sysfs + +Unmanaged ports were not being shown when showing or editting the +bridge. In order to fix that, nmstate is retrieving the bridge ports +from sysfs instead of NetworkManager on-disk configuration. + +Ref: https://bugzilla.redhat.com/1806452 + +Signed-off-by: Fernando Fernandez Mancera +--- + libnmstate/nm/bridge.py | 55 +++++++++++++++++--------- + tests/integration/linux_bridge_test.py | 34 ++++++++++++++++ + 2 files changed, 71 insertions(+), 18 deletions(-) + +diff --git a/libnmstate/nm/bridge.py b/libnmstate/nm/bridge.py +index e0ab8e4..672214d 100644 +--- a/libnmstate/nm/bridge.py ++++ b/libnmstate/nm/bridge.py +@@ -17,6 +17,9 @@ + # along with this program. If not, see . + # + ++import glob ++import os ++ + from libnmstate.error import NmstateNotImplementedError + from libnmstate.nm import connection + from libnmstate.nm import nmclient +@@ -25,6 +28,12 @@ from libnmstate.schema import LinuxBridge as LB + + BRIDGE_TYPE = "bridge" + ++BRIDGE_PORT_NMSTATE_TO_SYSFS = { ++ LB.Port.STP_HAIRPIN_MODE: "hairpin_mode", ++ LB.Port.STP_PATH_COST: "path_cost", ++ LB.Port.STP_PRIORITY: "priority", ++} ++ + + def create_setting(options, base_con_profile): + bridge_setting = _get_current_bridge_setting(base_con_profile) +@@ -106,10 +115,10 @@ def get_info(nmdev): + if not bridge_setting: + return info + +- port_profiles = _get_slave_profiles(nmdev) ++ port_names_sysfs = _get_slaves_names_from_sysfs(nmdev.get_iface()) + props = bridge_setting.props + info[LB.CONFIG_SUBTREE] = { +- LB.PORT_SUBTREE: _get_bridge_ports_info(port_profiles), ++ LB.PORT_SUBTREE: _get_bridge_ports_info(port_names_sysfs), + LB.OPTIONS_SUBTREE: { + LB.Options.MAC_AGEING_TIME: props.ageing_time, + LB.Options.GROUP_FORWARD_MASK: props.group_forward_mask, +@@ -139,25 +148,35 @@ def _get_bridge_setting(nmdev): + return bridge_setting + + +-def _get_bridge_ports_info(port_profiles): +- ports_info = [] +- for p in port_profiles: +- port_info = _get_bridge_port_info(p) +- if port_info: +- ports_info.append(port_info) +- return ports_info ++def _get_bridge_ports_info(port_names_sysfs): ++ return [_get_bridge_port_info(name) for name in port_names_sysfs] + + +-def _get_bridge_port_info(port_profile): +- """Report port information.""" ++def _get_bridge_port_info(port_name): ++ """Report port runtime information from sysfs.""" ++ port = {LB.Port.NAME: port_name} ++ for option, option_sysfs in BRIDGE_PORT_NMSTATE_TO_SYSFS.items(): ++ sysfs_path = f"/sys/class/net/{port_name}/brport/{option_sysfs}" ++ with open(sysfs_path) as f: ++ option_value = int(f.read()) ++ if option == LB.Port.STP_HAIRPIN_MODE: ++ option_value = bool(option_value) ++ port[option] = option_value ++ return port + +- port_setting = port_profile.get_setting_bridge_port() +- return { +- LB.Port.NAME: port_profile.get_interface_name(), +- LB.Port.STP_PRIORITY: port_setting.props.priority, +- LB.Port.STP_HAIRPIN_MODE: port_setting.props.hairpin_mode, +- LB.Port.STP_PATH_COST: port_setting.props.path_cost, +- } ++ ++def _get_slaves_names_from_sysfs(master): ++ """ ++ We need to use glob in order to get the slaves name due to bug in ++ NetworkManager. ++ Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1809547 ++ """ ++ slaves = [] ++ for sysfs_slave in glob.iglob(f"/sys/class/net/{master}/lower_*"): ++ # The format is lower_, we need to remove the "lower_" prefix ++ prefix_length = len("lower_") ++ slaves.append(os.path.basename(sysfs_slave)[prefix_length:]) ++ return slaves + + + def _get_slave_profiles(master_device): +-- +2.25.1 + diff --git a/SOURCES/BZ_1817477-retry-verification-after-failing.patch b/SOURCES/BZ_1817477-retry-verification-after-failing.patch new file mode 100644 index 0000000..295728a --- /dev/null +++ b/SOURCES/BZ_1817477-retry-verification-after-failing.patch @@ -0,0 +1,212 @@ +From 73d51a5f40b21010a35ce0b33fa5738ddbb8f97d Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Mon, 16 Mar 2020 11:53:28 +0100 +Subject: [PATCH 03/10] netapplier: retry verification after failing + +When failing on verification nmstate is retrying it up to 5 times with a +sleep interval of 1 seconds. This will fix two issues: + + * During the verification sometimes ethtool is reporting 'unknown' in + duplex state. This is due a lack of time for some NICs to update the + information. We have noticed it only with Broadcom BCM57840 NetXtreme II + NIC but it could affect to other NICs. + + * When switching from DHCP without DHCP server to static IPV6(IPv4 + disabled), the nmstate will fail with verification failure showing IPv6 + is still disabled. + +Signed-off-by: Fernando Fernandez Mancera + +In addition: + +netapplier: do not modify the desired_state during verification + +The desired_state is being modified during verification. Instead of +modifying the desired_state, nmstate should make a copy of it and then +modify the copy. + +Signed-off-by: Fernando Fernandez Mancera + +In addition: + +nm.nmclient: remove delayed quit from the mainloop + +The delayed quit is not needed anymore as nmstate is retrying the +verification step up to five times with an sleep interval of one second. + +Signed-off-by: Fernando Fernandez Mancera + +In addition: + +nm.checkpoint: fix incorrect interval when adding a timer + +According to GLib documentation the interval specified when using +timeout_add() is on miliseconds. + +Signed-off-by: Fernando Fernandez Mancera + +In addition: + +nm sriov: Properly return when device not exists + +When interface does not exists, the `nm.sriov._has_sriov_capability()` +will raise: + +```python + def _has_sriov_capability(ifname): + dev = device.get_device_by_name(ifname) +> if nmclient.NM.DeviceCapabilities.SRIOV & dev.props.capabilities: +E AttributeError: 'NoneType' object has no attribute 'props' +``` + +Fixed by add additional check on whether device is None. + +Also fixed the integration test which incorrectly using eth0 which is +not test NIC, changed to eth1. + +Signed-off-by: Gris Ge +Signed-off-by: Fernando Fernandez Mancera + +In addition: + +nm.sriov: fix typo in sr-iov device capabilities + +Signed-off-by: Fernando Fernandez Mancera +--- + examples/eth1_with_sriov.yml | 13 +++++++++++++ + libnmstate/netapplier.py | 24 +++++++++++++++++++----- + libnmstate/nm/checkpoint.py | 2 +- + libnmstate/nm/nmclient.py | 16 ++-------------- + libnmstate/nm/sriov.py | 7 +++---- + tests/integration/examples_test.py | 14 ++++++++++++++ + 6 files changed, 52 insertions(+), 24 deletions(-) + create mode 100644 examples/eth1_with_sriov.yml + +diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py +index d9d10cf..e001793 100644 +--- a/libnmstate/netapplier.py ++++ b/libnmstate/netapplier.py +@@ -36,9 +36,12 @@ from libnmstate.error import NmstateError + from libnmstate.error import NmstateLibnmError + from libnmstate.error import NmstatePermissionError + from libnmstate.error import NmstateValueError ++from libnmstate.error import NmstateVerificationError + from libnmstate.nm import nmclient + + MAINLOOP_TIMEOUT = 35 ++VERIFY_RETRY_INTERNAL = 1 ++VERIFY_RETRY_TIMEOUT = 5 + + + @_warn_keyword_as_positional +@@ -159,8 +162,18 @@ def _apply_ifaces_state( + ifaces2add + ifaces2edit, + con_profiles=ifaces_add_configs + ifaces_edit_configs, + ) ++ verified = False + if verify_change: +- _verify_change(desired_state) ++ for _ in range(VERIFY_RETRY_TIMEOUT): ++ try: ++ _verify_change(desired_state) ++ verified = True ++ break ++ except NmstateVerificationError: ++ time.sleep(VERIFY_RETRY_INTERNAL) ++ if not verified: ++ _verify_change(desired_state) ++ + if not commit: + return checkpoint + except nm.checkpoint.NMCheckPointPermissionError: +@@ -204,10 +217,11 @@ def _list_new_interfaces(desired_state, current_state): + + def _verify_change(desired_state): + current_state = state.State(netinfo.show()) +- desired_state.verify_interfaces(current_state) +- desired_state.verify_routes(current_state) +- desired_state.verify_dns(current_state) +- desired_state.verify_route_rule(current_state) ++ verifiable_desired_state = copy.deepcopy(desired_state) ++ verifiable_desired_state.verify_interfaces(current_state) ++ verifiable_desired_state.verify_routes(current_state) ++ verifiable_desired_state.verify_dns(current_state) ++ verifiable_desired_state.verify_route_rule(current_state) + + + @contextmanager +diff --git a/libnmstate/nm/checkpoint.py b/libnmstate/nm/checkpoint.py +index bb8f5d6..771a46b 100644 +--- a/libnmstate/nm/checkpoint.py ++++ b/libnmstate/nm/checkpoint.py +@@ -127,7 +127,7 @@ class CheckPoint: + raise NMCheckPointCreationError(str(e)) + + GLib.timeout_add( +- self._timeout * 0.5, self._refresh_checkpoint_timeout, None, ++ self._timeout * 500, self._refresh_checkpoint_timeout, None, + ) + + def _refresh_checkpoint_timeout(self, data): +diff --git a/libnmstate/nm/nmclient.py b/libnmstate/nm/nmclient.py +index 31c6020..cc5ee9b 100644 +--- a/libnmstate/nm/nmclient.py ++++ b/libnmstate/nm/nmclient.py +@@ -41,8 +41,6 @@ GObject + _mainloop = None + _nmclient = None + +-MAINLOOP_TEARDOWN_DELAY = 500 # msec +- + NM_MANAGER_ERROR_DOMAIN = "nm-manager-error-quark" + _NMCLIENT_CLEANUP_TIMEOUT = 5 + +@@ -138,7 +136,8 @@ class _MainLoop: + logging.debug("Executing NM action: func=%s", func.__name__) + func(*args, **kwargs) + else: +- self._execute_teardown_action() ++ logging.debug("NM action queue exhausted, quiting mainloop") ++ self._mainloop.quit() + + def push_action(self, func, *args, **kwargs): + action = (func, args, kwargs) +@@ -245,17 +244,6 @@ class _MainLoop: + self.execute_next_action() + return False + +- def _execute_teardown_action(self): +- # Some actions are not getting updated fast enough in the +- # kernel/cache. Wait a bit before quitting the mainloop. +- GLib.timeout_add( +- MAINLOOP_TEARDOWN_DELAY, self._execute_graceful_quit, None +- ) +- +- def _execute_graceful_quit(self, _): +- logging.debug("NM action queue exhausted, quiting mainloop") +- self._mainloop.quit() +- + + class _NmClient: + def __init__(self): +diff --git a/libnmstate/nm/sriov.py b/libnmstate/nm/sriov.py +index 2a627cc..cfc0f9a 100644 +--- a/libnmstate/nm/sriov.py ++++ b/libnmstate/nm/sriov.py +@@ -129,10 +129,9 @@ def _remove_sriov_vfs_in_setting(vfs_config, sriov_setting, vf_ids_to_remove): + + def _has_sriov_capability(ifname): + dev = device.get_device_by_name(ifname) +- if nmclient.NM.DeviceCapabilities.SRIOV & dev.props.capabilities: +- return True +- +- return False ++ return dev and ( ++ nmclient.NM.DeviceCapabilities.SRIOV & dev.props.capabilities ++ ) + + + def get_info(device): +-- +2.25.1 + diff --git a/SOURCES/BZ_1817478-allow-missing-arp-ip-target-if-arp-interval-is-disabled.patch b/SOURCES/BZ_1817478-allow-missing-arp-ip-target-if-arp-interval-is-disabled.patch new file mode 100644 index 0000000..d76fa81 --- /dev/null +++ b/SOURCES/BZ_1817478-allow-missing-arp-ip-target-if-arp-interval-is-disabled.patch @@ -0,0 +1,84 @@ +From 37f47c373a99771d42799941ba96cf4f8a173c75 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Thu, 26 Mar 2020 19:50:03 +0800 +Subject: [PATCH 09/10] state: allow missing arp_ip_target when ARP monitoring + disabled + +Got verification failure when disabling `arp_interval` of existing bond +regarding `arp_ip_target: ''` not included in new state. + +This is because NetworkManager does not store `arp_ip_target: ''` in +on-disk profile when `arp_interval` is zero/disabled. + +The fix is allowing backend to skip `arp_ip_target` when ARP monitoring +is disabled(`arp_interval==0`) by fix the bond option before +verification. + +Added function `libnmstate.applier.bond.iface_state_pre_verify_fix()`. + +Introduced `self._pre_verification_fix()` to `State.verify_interfaces()` +allowing `state.py` to delegating state modification to interface +specific function without knowing the detail. + +Integration test case added. + +Signed-off-by: Gris Ge +--- + libnmstate/appliers/bond.py | 13 +++++++++ + libnmstate/state.py | 10 +++++++ + tests/integration/bond_test.py | 39 +++++++++++++++++++++++--- + tests/integration/testlib/assertlib.py | 21 ++++++++++++++ + 4 files changed, 79 insertions(+), 4 deletions(-) + +diff --git a/libnmstate/appliers/bond.py b/libnmstate/appliers/bond.py +index 785d8f3..5602adb 100644 +--- a/libnmstate/appliers/bond.py ++++ b/libnmstate/appliers/bond.py +@@ -113,3 +113,16 @@ def get_bond_named_option_value_by_id(option_name, option_id_value): + with contextlib.suppress(ValueError, IndexError): + return option_value[int(option_id_value)] + return option_id_value ++ ++ ++def fix_bond_option_arp_monitor(cur_iface_state): ++ """ ++ Fix the current iface_state by ++ adding 'arp_ip_target=""' when ARP monitor is disabled by `arp_interval=0` ++ """ ++ bond_options = cur_iface_state[Bond.CONFIG_SUBTREE][Bond.OPTIONS_SUBTREE] ++ if ( ++ bond_options.get("arp_interval") in ("0", 0) ++ and "arp_ip_target" not in bond_options ++ ): ++ bond_options["arp_ip_target"] = "" +diff --git a/libnmstate/state.py b/libnmstate/state.py +index 960558a..98be33d 100644 +--- a/libnmstate/state.py ++++ b/libnmstate/state.py +@@ -321,6 +321,7 @@ class State: + + metadata.remove_ifaces_metadata(self) + other_state.sanitize_dynamic_ip() ++ other_state._pre_verification_fix() + + self.merge_interfaces(other_state) + +@@ -329,6 +330,15 @@ class State: + + self._assert_interfaces_equal(other_state) + ++ def _pre_verification_fix(self): ++ """ ++ Invoking iface specific fixes. ++ Supposed to only run againt current state. ++ """ ++ for iface_state in self.interfaces.values(): ++ if iface_state.get(Interface.TYPE) == InterfaceType.BOND: ++ bond.fix_bond_option_arp_monitor(iface_state) ++ + def verify_routes(self, other_state): + for iface_name, routes in self.config_iface_routes.items(): + other_routes = other_state.config_iface_routes.get(iface_name, []) +-- +2.25.1 + diff --git a/SOURCES/BZ_1819738-Don-t-merge-bond-options-when-bond-mode-changed.patch b/SOURCES/BZ_1819738-Don-t-merge-bond-options-when-bond-mode-changed.patch new file mode 100644 index 0000000..7703150 --- /dev/null +++ b/SOURCES/BZ_1819738-Don-t-merge-bond-options-when-bond-mode-changed.patch @@ -0,0 +1,211 @@ +From 238fb9542c4365ae13f6b4f0c2cd6ffcd427e561 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Wed, 1 Apr 2020 20:26:40 +0800 +Subject: [PATCH] bond: Don't merge bond options when bond mode changed + +When changing bond mode, user is required to provide all bond options +and no bond options will be merged from current state. + +When changing bond mode, full deactivation is required in NetworkManager +do to bug https://bugzilla.redhat.com/show_bug.cgi?id=1819137 + +Procedure to fix this issue: + * Generate a metadata if bond mode changed. + * After merge, use desire bond option only if bond mode changed. + * In NM profile modification, deactivate the interface before + modification if bond mode changed. + +Added integration test case and unit test cases. + +Signed-off-by: Gris Ge +--- + libnmstate/appliers/bond.py | 62 +++++++++++++++++++++++++++++++++++++ + libnmstate/metadata.py | 2 ++ + libnmstate/nm/applier.py | 18 +++++++++++ + libnmstate/state.py | 9 ++++++ + 4 files changed, 91 insertions(+) + +diff --git a/libnmstate/appliers/bond.py b/libnmstate/appliers/bond.py +index 5602adb..2a3f0a1 100644 +--- a/libnmstate/appliers/bond.py ++++ b/libnmstate/appliers/bond.py +@@ -18,9 +18,16 @@ + # + + import contextlib ++import logging + + from libnmstate.schema import Bond + from libnmstate.schema import BondMode ++from libnmstate.schema import Interface ++from libnmstate.schema import InterfaceState ++from libnmstate.schema import InterfaceType ++ ++BOND_MODE_CHANGED_METADATA = "_bond_mode_changed" ++NON_UP_STATES = (InterfaceState.DOWN, InterfaceState.ABSENT) + + + class BondNamedOptions: +@@ -126,3 +133,58 @@ def fix_bond_option_arp_monitor(cur_iface_state): + and "arp_ip_target" not in bond_options + ): + bond_options["arp_ip_target"] = "" ++ ++ ++def generate_bond_mode_change_metadata(desire_state, current_state): ++ for iface_name, iface_state in desire_state.interfaces.items(): ++ current_iface_state = current_state.interfaces.get(iface_name, {}) ++ if ( ++ iface_state.get( ++ Interface.TYPE, current_iface_state.get(Interface.TYPE) ++ ) ++ != InterfaceType.BOND ++ ): ++ continue ++ if iface_state.get(Interface.STATE) in NON_UP_STATES: ++ # Ignore bond mode change on absent/down interface ++ continue ++ current_bond_mode = current_iface_state.get( ++ Bond.CONFIG_SUBTREE, {} ++ ).get(Bond.MODE) ++ desire_bond_mode = iface_state.get(Bond.CONFIG_SUBTREE, {}).get( ++ Bond.MODE ++ ) ++ if ( ++ desire_bond_mode ++ and current_bond_mode ++ and desire_bond_mode != current_bond_mode ++ ): ++ logging.warning( ++ "Discarding all current bond options as interface " ++ f"{iface_name} has bond mode changed" ++ ) ++ iface_state[BOND_MODE_CHANGED_METADATA] = True ++ ++ ++def remove_bond_mode_change_metadata(iface_state): ++ iface_state.pop(BOND_MODE_CHANGED_METADATA, None) ++ ++ ++def is_bond_mode_changed(iface_state): ++ return iface_state.get(BOND_MODE_CHANGED_METADATA) ++ ++ ++def discard_merged_data_on_mode_change(merged_iface_state, desire_iface_state): ++ """ ++ When bond mode changed, use original desire bond options instead of merging ++ from current state. ++ """ ++ if is_bond_mode_changed(merged_iface_state): ++ if merged_iface_state.get(Bond.CONFIG_SUBTREE, {}).get( ++ Bond.OPTIONS_SUBTREE ++ ): ++ merged_iface_state[Bond.CONFIG_SUBTREE][ ++ Bond.OPTIONS_SUBTREE ++ ] = desire_iface_state[Bond.CONFIG_SUBTREE].get( ++ Bond.OPTIONS_SUBTREE, {} ++ ) +diff --git a/libnmstate/metadata.py b/libnmstate/metadata.py +index b268ba3..bdc8b3c 100644 +--- a/libnmstate/metadata.py ++++ b/libnmstate/metadata.py +@@ -53,6 +53,7 @@ def generate_ifaces_metadata(desired_state, current_state): + metadata is generated on interfaces, usable by the provider when + configuring the interface. + """ ++ bond.generate_bond_mode_change_metadata(desired_state, current_state) + _generate_link_master_metadata( + desired_state.interfaces, + current_state.interfaces, +@@ -91,6 +92,7 @@ def remove_ifaces_metadata(ifaces_state): + iface_state.pop(MASTER, None) + iface_state.pop(MASTER_TYPE, None) + iface_state.pop(BRPORT_OPTIONS, None) ++ bond.remove_bond_mode_change_metadata(iface_state) + iface_state.get(Interface.IPV4, {}).pop(ROUTES, None) + iface_state.get(Interface.IPV6, {}).pop(ROUTES, None) + iface_state.get(Interface.IPV4, {}).pop(DNS_METADATA, None) +diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py +index 1291469..1ca0647 100644 +--- a/libnmstate/nm/applier.py ++++ b/libnmstate/nm/applier.py +@@ -18,6 +18,7 @@ + # + + import base64 ++import logging + import hashlib + import itertools + from operator import attrgetter +@@ -28,6 +29,7 @@ from libnmstate.schema import InterfaceState + from libnmstate.schema import InterfaceType + from libnmstate.schema import LinuxBridge as LB + from libnmstate.schema import OVSBridge as OvsB ++from libnmstate.appliers.bond import is_bond_mode_changed + + from . import bond + from . import bridge +@@ -169,6 +171,7 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + master_ifaces_to_edit = set() + ifaces_to_edit = set() + remove_devs_actions = {} ++ devs_to_deactivate_beforehand = [] + + for iface_desired_state in ifaces_desired_state: + ifname = iface_desired_state[Interface.NAME] +@@ -199,6 +202,18 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + new_ifaces_to_activate.add(ifname) + else: + if iface_desired_state[Interface.STATE] == InterfaceState.UP: ++ if is_bond_mode_changed(iface_desired_state): ++ # NetworkManager leaves leftover in sysfs for bond ++ # options when changing bond mode, bug: ++ # https://bugzilla.redhat.com/show_bug.cgi?id=1819137 ++ # Workaround: delete the bond interface from kernel and ++ # create again via full deactivation beforehand. ++ logging.debug( ++ f"Bond interface {ifname} is changing bond mode, " ++ "will do full deactivation before applying changes" ++ ) ++ devs_to_deactivate_beforehand.append(nmdev) ++ + if _is_master_iface(iface_desired_state): + master_ifaces_to_edit.add( + (nmdev, con_profiles_by_devname[ifname].profile) +@@ -230,6 +245,9 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + ) + ) + ++ for dev in devs_to_deactivate_beforehand: ++ device.deactivate(dev) ++ + # Do not remove devices that are marked for editing. + for dev, _ in itertools.chain(master_ifaces_to_edit, ifaces_to_edit): + remove_devs_actions.pop(dev, None) +diff --git a/libnmstate/state.py b/libnmstate/state.py +index 98be33d..12bc414 100644 +--- a/libnmstate/state.py ++++ b/libnmstate/state.py +@@ -390,10 +390,19 @@ class State: + entries that appear only on one state are ignored. + This is a reverse recursive update operation. + """ ++ origin_self_state = State(self.state) + other_state = State(other_state.state) + for name in self.interfaces.keys() & other_state.interfaces.keys(): + dict_update(other_state.interfaces[name], self.interfaces[name]) + self._ifaces_state[name] = other_state.interfaces[name] ++ iface_state = self.interfaces[name] ++ if iface_state.get(Interface.TYPE) == InterfaceType.BOND: ++ origin_self_iface_state = origin_self_state.interfaces.get( ++ name ++ ) ++ bond.discard_merged_data_on_mode_change( ++ iface_state, origin_self_iface_state ++ ) + + def complement_master_interfaces_removal(self, other_state): + """ +-- +2.18.2 + diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec index 66686ff..afefec7 100644 --- a/SPECS/nmstate.spec +++ b/SPECS/nmstate.spec @@ -4,7 +4,7 @@ Name: nmstate Version: 0.2.6 -Release: 6%{?dist} +Release: 13%{?dist} Summary: Declarative network manager API License: LGPLv2+ URL: https://github.com/%{srcname}/%{srcname} @@ -16,6 +16,10 @@ Patch2: BZ_1809330-Bond-mode-change.patch Patch3: BZ_1809330-Fix-bond-regression.patch Patch4: BZ_1815112-Support-static-dns-on-DHCP.patch Patch5: BZ_1816043_Support_3_nameserver.patch +Patch6: BZ_1817477-retry-verification-after-failing.patch +Patch7: BZ_1817466-use-kernel-runtime-bridge-status.patch +Patch8: BZ_1817478-allow-missing-arp-ip-target-if-arp-interval-is-disabled.patch +Patch9: BZ_1819738-Don-t-merge-bond-options-when-bond-mode-changed.patch BuildArch: noarch BuildRequires: python3-devel BuildRequires: python3-setuptools @@ -70,6 +74,27 @@ gpgv2 --keyring %{SOURCE2} %{SOURCE1} %{SOURCE0} %{python3_sitelib}/%{srcname}-*.egg-info/ %changelog +* Thu Apr 09 2020 Gris Ge - 0.2.6-13 +- Don't merge bond option when mode changed. RHBZ #1819738 + +* Thu Apr 09 2020 Gris Ge - 0.2.6-12 +- Rebuild due to failure of RCM tagging system. + +* Wed Apr 01 2020 Gris Ge - 0.2.6-11 +- Rebuild due to failure of RCM tagging system. + +* Wed Apr 01 2020 Gris Ge - 0.2.6-10 +- Dump the build number for z-stream. + +* Fri Mar 27 2020 Fernando Fernandez Mancera - 0.2.6-9 +- Allow missing arp_ip_target when ARP monitoring is disabled. RHBZ #1817478 + +* Fri Mar 27 2020 Fernando Fernandez Mancera - 0.2.6-8 +- Fix missing linux bridge slaves reporting. RHBZ #1817466 + +* Fri Mar 27 2020 Fernando Fernandez Mancera - 0.2.6-7 +- Fix verification errors due to outdated values. RHBZ #1817477 + * Wed Mar 25 2020 Gris Ge - 0.2.6-6 - Support 3+ DNS name server(IPv4 only or IPv6 only). RHBZ #1816043