From 245567f87f461367161baa0eb7757c7a4e3f4526 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Nov 05 2019 19:05:14 +0000 Subject: import nmstate-0.0.8-15.el8 --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ff7441 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +SOURCES/gpgkey-F7910D93CA83D77348595C0E899014C0463C12BB.gpg +SOURCES/nmstate-0.0.8.tar.gz diff --git a/.nmstate.metadata b/.nmstate.metadata new file mode 100644 index 0000000..541dd68 --- /dev/null +++ b/.nmstate.metadata @@ -0,0 +1,2 @@ +7bcc63976a8d449b3adc57f40d7a476106889042 SOURCES/gpgkey-F7910D93CA83D77348595C0E899014C0463C12BB.gpg +a0b850163fc7e0a51a090340f91b4678fa6d902b SOURCES/nmstate-0.0.8.tar.gz diff --git a/SOURCES/BZ_1738101-Exclude-ipv6-changes-from-reapply.patch b/SOURCES/BZ_1738101-Exclude-ipv6-changes-from-reapply.patch new file mode 100644 index 0000000..0ded597 --- /dev/null +++ b/SOURCES/BZ_1738101-Exclude-ipv6-changes-from-reapply.patch @@ -0,0 +1,72 @@ +From 38a8e719f93a0e6157f4d788856e34fcf6cbe089 Mon Sep 17 00:00:00 2001 +From: Edward Haas +Date: Wed, 31 Jul 2019 12:35:37 +0300 +Subject: [PATCH] nm.device: Exclude ipv6 changes from reapply + +If ipv6 is changed, fallback from reapply to activate. + +Ref: https://bugzilla.redhat.com/1734470 + +Signed-off-by: Edward Haas +Signed-off-by: Gris Ge +--- + libnmstate/nm/device.py | 38 +++++++++++++++++++++++++++++++------- + 1 file changed, 31 insertions(+), 7 deletions(-) + +diff --git a/libnmstate/nm/device.py b/libnmstate/nm/device.py +index 35f18b1..31ba298 100644 +--- a/libnmstate/nm/device.py ++++ b/libnmstate/nm/device.py +@@ -186,18 +186,42 @@ def _modify_callback(src_object, result, user_data): + + + def _requires_activation(dev, connection_profile): ++ if _mtu_changed(dev, connection_profile): ++ logging.debug( ++ 'Device reapply does not support mtu changes, ' ++ 'fallback to device activation: dev=%s', ++ dev.get_iface(), ++ ) ++ return True ++ if _ipv6_changed(dev, connection_profile): ++ logging.debug( ++ 'Device reapply does not support ipv6 changes, ' ++ 'fallback to device activation: dev=%s', ++ dev.get_iface(), ++ ) ++ return True ++ return False ++ ++ ++def _mtu_changed(dev, connection_profile): + wired_setting = connection_profile.get_setting_wired() + configured_mtu = wired_setting.props.mtu if wired_setting else None + if configured_mtu: + current_mtu = int(dev.get_mtu()) +- if configured_mtu != current_mtu: +- logging.debug( +- 'Device reapply does not support mtu changes, ' +- 'fallback to device activation: dev=%s', +- dev.get_iface(), +- ) +- return True ++ return configured_mtu != current_mtu ++ return False + ++ ++def _ipv6_changed(dev, connection_profile): ++ """ ++ Detecting that the IPv6 method changed is not possible at this stage, ++ therefore, if IPv6 is defined (i.e. the method if not 'ignore'), IPv6 is ++ considered as changed. ++ """ ++ ipv6_setting = connection_profile.get_setting_ip6_config() ++ if ipv6_setting: ++ ignore = nmclient.NM.SETTING_IP6_CONFIG_METHOD_IGNORE ++ return ipv6_setting.props.method != ignore + return False + + +-- +2.22.0 + diff --git a/SOURCES/BZ_1740125_Increase-main-loop-timeout.patch b/SOURCES/BZ_1740125_Increase-main-loop-timeout.patch new file mode 100644 index 0000000..6346734 --- /dev/null +++ b/SOURCES/BZ_1740125_Increase-main-loop-timeout.patch @@ -0,0 +1,41 @@ +From 10688dead995231478001f333672cf6df598c042 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Thu, 15 Aug 2019 14:44:22 +0800 +Subject: [PATCH] nm: Increase main loop timeout + +Increase the mainloop timeout from 20 seconds to 35 seconds. + +When creating new bridge with ipv6 enabled, the bridge activation will +wait on link up which require 30 seconds for STP to add first interface +to the bridge. + +Signed-off-by: Gris Ge +--- + libnmstate/netapplier.py | 4 +++- + 1 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py +index cad148c..a35610b 100644 +--- a/libnmstate/netapplier.py ++++ b/libnmstate/netapplier.py +@@ -38,6 +38,8 @@ from libnmstate.error import NmstatePermissionError + from libnmstate.error import NmstateValueError + from libnmstate.nm import nmclient + ++MAINLOOP_TIMEOUT = 35 ++ + + def apply(desired_state, verify_change=True, commit=True, rollback_timeout=60): + """ +@@ -200,7 +202,7 @@ def _verify_change(desired_state): + def _setup_providers(): + mainloop = nmclient.mainloop() + yield +- success = mainloop.run(timeout=20) ++ success = mainloop.run(timeout=MAINLOOP_TIMEOUT) + if not success: + nmclient.mainloop(refresh=True) + raise NmstateLibnmError( +-- +2.22.1 + diff --git a/SOURCES/BZ_1740554_rollback_to_ipv6_disabled.patch b/SOURCES/BZ_1740554_rollback_to_ipv6_disabled.patch new file mode 100644 index 0000000..bd5d451 --- /dev/null +++ b/SOURCES/BZ_1740554_rollback_to_ipv6_disabled.patch @@ -0,0 +1,56 @@ +From 8a1195070fe3066711e9371899357eca67c03fa5 Mon Sep 17 00:00:00 2001 +From: Edward Haas +Date: Sun, 28 Jul 2019 13:59:43 +0300 +Subject: [PATCH] apply, rollback: Revert to a state where IPv6 is disabled + +In cases where the previous state on an interface included IPv6 +disabled and the processing of the desired state caused it to be +enabled, the rollback failed to re-disable the IPv6 stack. + +In case of a rollback, the previous state is processed and IPv6 is +disabled if needed. + +Signed-off-by: Edward Haas +Signed-off-by: Gris Ge +--- + libnmstate/netapplier.py | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/libnmstate/netapplier.py b/libnmstate/netapplier.py +index f912bb2..cad148c 100644 +--- a/libnmstate/netapplier.py ++++ b/libnmstate/netapplier.py +@@ -22,6 +22,7 @@ from contextlib import contextmanager + import copy + import logging + import six ++import time + + from libnmstate import metadata + from libnmstate import netinfo +@@ -31,6 +32,7 @@ from libnmstate import state + from libnmstate import sysctl + from libnmstate import validator + from libnmstate.error import NmstateConflictError ++from libnmstate.error import NmstateError + from libnmstate.error import NmstateLibnmError + from libnmstate.error import NmstatePermissionError + from libnmstate.error import NmstateValueError +@@ -150,6 +152,14 @@ def _apply_ifaces_state( + raise NmstatePermissionError('Error creating a check point') + except nm.checkpoint.NMCheckPointCreationError: + raise NmstateConflictError('Error creating a check point') ++ except NmstateError: ++ # Assume rollback occured, revert IPv6 stack state. ++ # Checkpoint rollback is async, there is a need to wait for it to ++ # finish before proceeding with other actions. ++ # TODO: https://nmstate.atlassian.net/browse/NMSTATE-103 ++ time.sleep(5) ++ _disable_ipv6(current_state) ++ raise + + + def _create_editable_desired_state( +-- +2.22.0 + diff --git a/SOURCES/BZ_1740584-Ignore-not-active-failure-when-deactivate.patch b/SOURCES/BZ_1740584-Ignore-not-active-failure-when-deactivate.patch new file mode 100644 index 0000000..5295679 --- /dev/null +++ b/SOURCES/BZ_1740584-Ignore-not-active-failure-when-deactivate.patch @@ -0,0 +1,114 @@ +From 6334eb32df01326b85ca6cd589a5ea91117189e6 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Fri, 9 Aug 2019 13:26:02 +0800 +Subject: [PATCH] nm: Ignore not active failure when deactivate + +Ignore the failure: + + Connection deactivation failed on nm2: error=nm-manager-error-quark: The + connection was not active. + +Even we check activate connection status before deactivating, there is +still a small chance that NM deactivated the connection after we +checked. + +This patch also fix the random failure of +`test_add_bond_with_slaves_and_ipv4` test when tear down: + + * The tear down will delete bond99 and eth1/eth2. + * After bond99 got deleted, the connections of eth1/eth2 will be + brought down automatically by NM. + * There is a small chance(about 1 out of 10) to trigger the problem of + deactivating a non-active connection. + +Signed-off-by: Gris Ge +--- + libnmstate/nm/active_connection.py | 27 +++++++++++++++++++----- + tests/lib/nm/active_connection_test.py | 29 ++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+), 5 deletions(-) + create mode 100644 tests/lib/nm/active_connection_test.py + +diff --git a/libnmstate/nm/active_connection.py b/libnmstate/nm/active_connection.py +index 81ec9b5..28a89d5 100644 +--- a/libnmstate/nm/active_connection.py ++++ b/libnmstate/nm/active_connection.py +@@ -20,6 +20,11 @@ + import logging + + from . import nmclient ++from .nmclient import GLib ++from .nmclient import NM ++ ++ ++NM_MANAGER_ERROR_DOMAIN = 'nm-manager-error-quark' + + + class AlternativeACState(object): +@@ -96,12 +101,24 @@ class ActiveConnection(object): + e, + ) + else: +- self._mainloop.quit( +- 'Connection deactivation failed on {}: error={}'.format( +- self._nmdev.get_iface(), e ++ if ( ++ isinstance(e, GLib.GError) ++ # pylint: disable=no-member ++ and e.domain == NM_MANAGER_ERROR_DOMAIN ++ and e.code == NM.ManagerError.CONNECTIONNOTACTIVE ++ # pylint: enable=no-member ++ ): ++ success = True ++ logging.debug( ++ 'Connection is not active on {}, no need to ' ++ 'deactivate'.format(self.devname) + ) +- ) +- return ++ else: ++ self._mainloop.quit( ++ 'Connection deactivation failed on {}: ' ++ 'error={}'.format(self._nmdev.get_iface(), e) ++ ) ++ return + + if success: + logging.debug( +diff --git a/tests/lib/nm/active_connection_test.py b/tests/lib/nm/active_connection_test.py +new file mode 100644 +index 0000000..5bd6d66 +--- /dev/null ++++ b/tests/lib/nm/active_connection_test.py +@@ -0,0 +1,29 @@ ++# ++# Copyright (c) 2019 Red Hat, Inc. ++# ++# This file is part of nmstate ++# ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU Lesser General Public License as published by ++# the Free Software Foundation, either version 2.1 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU Lesser General Public License for more details. ++# ++# You should have received a copy of the GNU Lesser General Public License ++# along with this program. If not, see . ++# ++ ++from libnmstate.nm.nmclient import NM ++from libnmstate.nm.nmclient import GLib ++ ++from libnmstate.nm.active_connection import NM_MANAGER_ERROR_DOMAIN ++ ++ ++def test_nm_manager_error_domain_str(): ++ assert NM_MANAGER_ERROR_DOMAIN == GLib.quark_to_string( ++ NM.ManagerError.quark() ++ ) +-- +2.22.0 + diff --git a/SOURCES/BZ_1741049_vlan_interface_should_activated_after_base.patch b/SOURCES/BZ_1741049_vlan_interface_should_activated_after_base.patch new file mode 100644 index 0000000..298a9f2 --- /dev/null +++ b/SOURCES/BZ_1741049_vlan_interface_should_activated_after_base.patch @@ -0,0 +1,81 @@ +From abe64cecf310d482f1779c10af5f903c4c0bd98c Mon Sep 17 00:00:00 2001 +From: Edward Haas +Date: Tue, 13 Aug 2019 20:37:53 +0800 +Subject: [PATCH] nm.applier: Prioritize base iface activation before the vlan + +When both the base interface and the VLAN interface are created in one +transaction, the base interface must be activated before the vlan one, +otherwise the VLAN activation fails. + +The new VLAN interfaces are now activated after all other interfaces. + +Signed-off-by: Edward Haas +Signed-off-by: Gris Ge +--- + libnmstate/nm/applier.py | 8 ++++++++ + libnmstate/schema.py | 2 ++ + 1 files changed, 10 insertions(+) + +diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py +index ae5cd75..750ca3d 100644 +--- a/libnmstate/nm/applier.py ++++ b/libnmstate/nm/applier.py +@@ -21,6 +21,8 @@ import itertools + import six + + from libnmstate.error import NmstateValueError ++from libnmstate.schema import Interface ++from libnmstate.schema import InterfaceType + from libnmstate.schema import LinuxBridge as LB + + from . import bond +@@ -118,6 +120,7 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + con_profiles_by_devname = _index_profiles_by_devname(con_profiles) + new_ifaces = _get_new_ifaces(con_profiles) + new_ifaces_to_activate = set() ++ new_vlan_ifaces_to_activate = set() + new_ovs_interface_to_activate = set() + new_ovs_port_to_activate = set() + master_ifaces_to_edit = set() +@@ -133,6 +136,8 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + new_ovs_interface_to_activate.add(ifname) + elif iface_desired_state['type'] == ovs.PORT_TYPE: + new_ovs_port_to_activate.add(ifname) ++ elif iface_desired_state[Interface.TYPE] == InterfaceType.VLAN: ++ new_vlan_ifaces_to_activate.add(ifname) + else: + new_ifaces_to_activate.add(ifname) + else: +@@ -186,6 +191,9 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + for dev, con_profile in ifaces_to_edit: + device.modify(dev, con_profile) + ++ for ifname in new_vlan_ifaces_to_activate: ++ device.activate(dev=None, connection_id=ifname) ++ + for dev, actions in six.viewitems(remove_devs_actions): + for action in actions: + action(dev) +diff --git a/libnmstate/schema.py b/libnmstate/schema.py +index 1737003..0af60ef 100644 +--- a/libnmstate/schema.py ++++ b/libnmstate/schema.py +@@ -88,6 +88,7 @@ class InterfaceType(object): + KEY = Interface.TYPE + + BOND = 'bond' ++ DUMMY = 'dummy' + ETHERNET = 'ethernet' + LINUX_BRIDGE = 'linux-bridge' + OVS_BRIDGE = 'ovs-bridge' +@@ -98,6 +99,7 @@ class InterfaceType(object): + + VIRT_TYPES = ( + BOND, ++ DUMMY, + LINUX_BRIDGE, + OVS_BRIDGE, + OVS_PORT, +-- +2.22.1 + diff --git a/SOURCES/BZ_1741440_Fix_Activation_Race_of_Slave.patch b/SOURCES/BZ_1741440_Fix_Activation_Race_of_Slave.patch new file mode 100644 index 0000000..5e92a74 --- /dev/null +++ b/SOURCES/BZ_1741440_Fix_Activation_Race_of_Slave.patch @@ -0,0 +1,170 @@ +From 14e910d61b9b46dac719698043122079b8361ad0 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Thu, 22 Aug 2019 21:46:26 +0800 +Subject: [PATCH] nm: Fix race problem on slave been deactivated by master + +When master is activating, the `AutoconnectSlaves:Yes` will +activate the slaves, this will deactivate slave first. +There is a small chance(less than 1/1000), the +`waitfor_active_connection_async()` fails as the cached active +connection is disconnected. + +The root cause is the active connection been replaced and marked as +DEACTIVATED with reason device disconnected by the following activate calls. + +The fix is to get current active connection from NM.Device, if that is +not the cached one, use the new one for tracking 'state-change' signals. + +Signed-off-by: Gris Ge +--- + libnmstate/nm/active_connection.py | 52 ++++++------------------------ + libnmstate/nm/connection.py | 17 +++++++--- + 2 files changed, 22 insertions(+), 47 deletions(-) + +diff --git a/libnmstate/nm/active_connection.py b/libnmstate/nm/active_connection.py +index 28a89d5e..8a3eb338 100644 +--- a/libnmstate/nm/active_connection.py ++++ b/libnmstate/nm/active_connection.py +@@ -27,13 +27,6 @@ + NM_MANAGER_ERROR_DOMAIN = 'nm-manager-error-quark' + + +-class AlternativeACState(object): +- UNKNOWN = 0 +- ACTIVE = 1 +- ACTIVATING = 2 +- FAIL = 3 +- +- + class ActivationError(Exception): + pass + +@@ -43,16 +36,12 @@ def __init__(self, active_connection=None): + self.handlers = set() + self._act_con = active_connection + self._mainloop = nmclient.mainloop() +- self._state = None + + nmdevs = None + if active_connection: + nmdevs = active_connection.get_devices() + self._nmdev = nmdevs[0] if nmdevs else None + +- if self._act_con: +- self.refresh_state() +- + def import_by_device(self, nmdev=None): + assert self._act_con is None + +@@ -132,37 +121,12 @@ def _deactivate_connection_callback(self, src_object, result, user_data): + % self._nmdev.get_iface() + ) + +- def refresh_state(self): +- self._state = self._act_con.get_state() +- self._state_reason = self._act_con.get_state_reason() +- self._alternative_state = AlternativeACState.UNKNOWN +- +- nm_acs = nmclient.NM.ActiveConnectionState +- nm_acsreason = nmclient.NM.ActiveConnectionStateReason +- if self._state == nm_acs.DEACTIVATED: +- unable_to_activate = ( +- not self._nmdev +- or ( +- self._state_reason is not None +- and self._state_reason != nm_acsreason.DEVICE_DISCONNECTED +- ) +- or self._nmdev.get_active_connection() is not self._act_con +- ) +- if unable_to_activate: +- self._alternative_state = AlternativeACState.FAIL +- # Use the device-state as an alternative to determine if active. +- elif ( +- self.nmdev_state <= nmclient.NM.DeviceState.DISCONNECTED +- or self.nmdev_state > nmclient.NM.DeviceState.DEACTIVATING +- ): +- self._alternative_state = AlternativeACState.FAIL +- + @property + def is_active(self): + nm_acs = nmclient.NM.ActiveConnectionState +- if self._state == nm_acs.ACTIVATED: ++ if self.state == nm_acs.ACTIVATED: + return True +- elif self._state == nm_acs.ACTIVATING: ++ elif self.state == nm_acs.ACTIVATING: + # master connections qualify as activated once they + # reach IP-Config state. That is because they may + # wait for slave devices to attach +@@ -177,12 +141,11 @@ def is_active(self): + + @property + def is_activating(self): +- activation_failed = self._alternative_state == AlternativeACState.FAIL +- return not self.is_active and not activation_failed ++ return self.state == nmclient.NM.ActiveConnectionState.ACTIVATING + + @property + def reason(self): +- return self._state_reason ++ return self._act_con.get_state_reason() + + @property + def nm_active_connection(self): +@@ -203,7 +166,7 @@ def nmdevice(self, nmdev): + + @property + def state(self): +- return self._state ++ return self._act_con.get_state() + + @property + def nmdev_state(self): +@@ -213,6 +176,11 @@ def nmdev_state(self): + else nmclient.NM.DeviceState.UNKNOWN + ) + ++ def remove_handlers(self): ++ for handler_id in self.handlers: ++ self.nm_active_connection.handler_disconnect(handler_id) ++ self.handlers = set() ++ + + def _is_device_master_type(nmdev): + if nmdev: +diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py +index 35b4f3d4..4c721b6a 100644 +--- a/libnmstate/nm/connection.py ++++ b/libnmstate/nm/connection.py +@@ -259,19 +259,26 @@ def waitfor_active_connection_async(self, ac): + def _waitfor_active_connection_callback( + self, _nm_act_con, _state, _reason, ac + ): +- ac.refresh_state() ++ cur_nm_act_conn = get_device_active_connection(self.nmdevice) ++ if cur_nm_act_conn and cur_nm_act_conn != ac.nm_active_connection: ++ logging.debug( ++ 'Active connection of device {} has been replaced'.format( ++ self.devname ++ ) ++ ) ++ ac.remove_handlers() ++ ac = ActiveConnection(cur_nm_act_conn) ++ self.waitfor_active_connection_async(ac) + if ac.is_active: + logging.debug( + 'Connection activation succeeded: dev=%s, con-state=%s', + ac.devname, + ac.state, + ) +- for handler_id in ac.handlers: +- ac.nm_active_connection.handler_disconnect(handler_id) ++ ac.remove_handlers() + self._mainloop.execute_next_action() + elif not ac.is_activating: +- for handler_id in ac.handlers: +- ac.nm_active_connection.handler_disconnect(handler_id) ++ ac.remove_handlers() + self._mainloop.quit( + 'Connection activation failed on {}: reason={}'.format( + ac.devname, ac.reason diff --git a/SOURCES/BZ_1748825_Change-DHCPv4-client-ID-to-MAC-address.patch b/SOURCES/BZ_1748825_Change-DHCPv4-client-ID-to-MAC-address.patch new file mode 100644 index 0000000..33af9bc --- /dev/null +++ b/SOURCES/BZ_1748825_Change-DHCPv4-client-ID-to-MAC-address.patch @@ -0,0 +1,28 @@ +From 71a9406c8bd1014d19ab82507e7a29d1265c9111 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Thu, 5 Sep 2019 15:53:55 +0800 +Subject: [PATCH 1/2] nm/ipv4: Change DHCPv4 client ID to MAC address. + +Set DHCPv4 client ID to MAC address to make sure interface get the +same IPv4 address always. + +Signed-off-by: Gris Ge +--- + libnmstate/nm/ipv4.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libnmstate/nm/ipv4.py b/libnmstate/nm/ipv4.py +index 282038b..3fea66a 100644 +--- a/libnmstate/nm/ipv4.py ++++ b/libnmstate/nm/ipv4.py +@@ -47,6 +47,7 @@ def create_setting(config, base_con_profile): + if not setting_ipv4: + setting_ipv4 = nmclient.NM.SettingIP4Config.new() + ++ setting_ipv4.props.dhcp_client_id = 'mac' + setting_ipv4.props.method = nmclient.NM.SETTING_IP4_CONFIG_METHOD_DISABLED + if config and config.get(InterfaceIPv4.ENABLED): + if config.get(InterfaceIPv4.DHCP): +-- +2.23.0 + diff --git a/SOURCES/BZ_1748825_Enforce-IPv6-RA-and-DHCPv6-address-base-on-MAC.patch b/SOURCES/BZ_1748825_Enforce-IPv6-RA-and-DHCPv6-address-base-on-MAC.patch new file mode 100644 index 0000000..a7388d8 --- /dev/null +++ b/SOURCES/BZ_1748825_Enforce-IPv6-RA-and-DHCPv6-address-base-on-MAC.patch @@ -0,0 +1,35 @@ +From f4a9b9bce8e59a59218275495993bb17e5d86da0 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Thu, 5 Sep 2019 21:06:51 +0800 +Subject: [PATCH 2/2] nm/ipv6: Enforce IPv6-RA and DHCPv6 address base on MAC + +When enslave interface to linux bridge, to ensure the bridge +get the same IPv6 address from IPv6-RA and DHCPv6, change +the 'dhcp_duid' to LL(Link-layer address, RFC 3315) and +'addr_gen_mode' to EUI64. + +Signed-off-by: Gris Ge +--- + libnmstate/nm/ipv6.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/libnmstate/nm/ipv6.py b/libnmstate/nm/ipv6.py +index 92955ab..9d0bc9b 100644 +--- a/libnmstate/nm/ipv6.py ++++ b/libnmstate/nm/ipv6.py +@@ -112,6 +112,12 @@ def create_setting(config, base_con_profile): + if not setting_ip: + setting_ip = nmclient.NM.SettingIP6Config.new() + ++ # Ensure IPv6 RA and DHCPv6 is based on MAC address only ++ setting_ip.props.addr_gen_mode = ( ++ nmclient.NM.SettingIP6ConfigAddrGenMode.EUI64 ++ ) ++ setting_ip.props.dhcp_duid = 'll' ++ + if not config or not config.get(InterfaceIPv6.ENABLED): + setting_ip.props.method = nmclient.NM.SETTING_IP6_CONFIG_METHOD_IGNORE + return setting_ip +-- +2.23.0 + diff --git a/SOURCES/BZ_1749314_Prioritize-master-interfaces-activaction.patch b/SOURCES/BZ_1749314_Prioritize-master-interfaces-activaction.patch new file mode 100644 index 0000000..0a6de66 --- /dev/null +++ b/SOURCES/BZ_1749314_Prioritize-master-interfaces-activaction.patch @@ -0,0 +1,105 @@ +From fb2f233d5156a3516b309336a310570c368c7a89 Mon Sep 17 00:00:00 2001 +From: Miguel Duarte Barroso +Date: Thu, 22 Aug 2019 10:39:10 +0200 +Subject: [PATCH] nm.applier: Prioritize master interfaces activaction + +The master interfaces - that are not slaves themselves - must be +activated first, otherwise NetworkManager fails with errors such +as: + +""" +NetworkManager[858]: +[1565960861.2223] manager: Activation of requires master +device +""" + +The supplied test fails roughly >= 50% of the times without this +patch - it all comes to the timing. + +Signed-off-by: Miguel Duarte Barroso +Signed-off-by: Gris Ge +--- + libnmstate/nm/applier.py | 25 ++++++++++++++++++++++--- + 1 file changed, 22 insertions(+), 3 deletions(-) + +diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py +index 750ca3d..974460b 100644 +--- a/libnmstate/nm/applier.py ++++ b/libnmstate/nm/applier.py +@@ -41,6 +41,7 @@ from . import wired + + MASTER_METADATA = '_master' + MASTER_TYPE_METADATA = '_master_type' ++MASTER_IFACE_TYPES = ovs.BRIDGE_TYPE, bond.BOND_TYPE, LB.TYPE + + BRPORT_OPTIONS_METADATA = '_brport_options' + +@@ -111,6 +112,7 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + leaving it to choose the correct profile. + + In order to activate correctly the interfaces, the order is significant: ++ - Master-less master interfaces. + - New interfaces (virtual interfaces, but not OVS ones). + - Master interfaces. + - OVS ports. +@@ -123,6 +125,7 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + new_vlan_ifaces_to_activate = set() + new_ovs_interface_to_activate = set() + new_ovs_port_to_activate = set() ++ new_master_not_enslaved_ifaces = set() + master_ifaces_to_edit = set() + ifaces_to_edit = set() + remove_devs_actions = {} +@@ -132,7 +135,13 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + nmdev = device.get_device_by_name(ifname) + if not nmdev: + if ifname in new_ifaces and iface_desired_state['state'] == 'up': +- if iface_desired_state['type'] == ovs.INTERNAL_INTERFACE_TYPE: ++ if _is_master_iface( ++ iface_desired_state ++ ) and not _is_slave_iface(iface_desired_state): ++ new_master_not_enslaved_ifaces.add(ifname) ++ elif ( ++ iface_desired_state['type'] == ovs.INTERNAL_INTERFACE_TYPE ++ ): + new_ovs_interface_to_activate.add(ifname) + elif iface_desired_state['type'] == ovs.PORT_TYPE: + new_ovs_port_to_activate.add(ifname) +@@ -142,8 +151,7 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + new_ifaces_to_activate.add(ifname) + else: + if iface_desired_state['state'] == 'up': +- master_iface_types = ovs.BRIDGE_TYPE, bond.BOND_TYPE, LB.TYPE +- if iface_desired_state['type'] in master_iface_types: ++ if _is_master_iface(iface_desired_state): + master_ifaces_to_edit.add( + (nmdev, con_profiles_by_devname[ifname].profile) + ) +@@ -176,6 +184,9 @@ def set_ifaces_admin_state(ifaces_desired_state, con_profiles=()): + for dev, _ in itertools.chain(master_ifaces_to_edit, ifaces_to_edit): + remove_devs_actions.pop(dev, None) + ++ for ifname in new_master_not_enslaved_ifaces: ++ device.activate(dev=None, connection_id=ifname) ++ + for ifname in new_ifaces_to_activate: + device.activate(dev=None, connection_id=ifname) + +@@ -213,6 +224,14 @@ def _get_new_ifaces(con_profiles): + return ifaces_without_device + + ++def _is_master_iface(iface_state): ++ return iface_state[Interface.TYPE] in MASTER_IFACE_TYPES ++ ++ ++def _is_slave_iface(iface_state): ++ return iface_state.get(MASTER_METADATA) ++ ++ + def _get_affected_devices(iface_state): + nmdev = device.get_device_by_name(iface_state['name']) + devs = [] +-- +2.23.0 + diff --git a/SOURCES/BZ_1749632_Detach-slaves-without-deleting-them.patch b/SOURCES/BZ_1749632_Detach-slaves-without-deleting-them.patch new file mode 100644 index 0000000..87a52c4 --- /dev/null +++ b/SOURCES/BZ_1749632_Detach-slaves-without-deleting-them.patch @@ -0,0 +1,43 @@ +From 385925657ea8871054077164f76d62ae194342cb Mon Sep 17 00:00:00 2001 +From: Edward Haas +Date: Fri, 6 Sep 2019 15:45:58 +0800 +Subject: [PATCH] metadata: Detach slaves without deleting them + +In scenarios where a slave is detached from a master interface (bond, bridge) +and the slave is not mentioned in the desired state, the metadata +processing designates that slave for removal (marking it with `state=down`). + +This behavior works fine with physical interfaces where the profile +removal only clears the interface configuration, but the device is still +present. With virtual interfaces (e.g. bond, vlan) the profile removal +also removed the device itself. + +Detaching the slave should not cause the slave deletion implicitly. +Therefore, this patch avoids setting the slave interface state to +`down`, allowing it to just get detached without deletion. + +Signed-off-by: Edward Haas +Signed-off-by: Gris Ge +--- + libnmstate/metadata.py | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/libnmstate/metadata.py b/libnmstate/metadata.py +index 8274e22..b624abd 100644 +--- a/libnmstate/metadata.py ++++ b/libnmstate/metadata.py +@@ -155,10 +155,7 @@ def _generate_link_master_metadata( + slaves2remove = set(current_slaves) - set(desired_slaves) + for slave in slaves2remove: + if slave not in ifaces_desired_state: +- ifaces_desired_state[slave] = { +- 'name': slave, +- 'state': 'down', +- } ++ ifaces_desired_state[slave] = {'name': slave} + + current_masters = ( + (ifname, ifstate) +-- +2.23.0 + diff --git a/SOURCES/nmstate-0.0.8.tar.gz.asc b/SOURCES/nmstate-0.0.8.tar.gz.asc new file mode 100644 index 0000000..2cd90a5 --- /dev/null +++ b/SOURCES/nmstate-0.0.8.tar.gz.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.22 (GNU/Linux) + +iQIcBAABAgAGBQJdOZnsAAoJEImQFMBGPBK7sxkP/0i1bxLjse1/d/4179tzA/aR +24SxVsg4I1kngB4cxagQf4zO1YDkoiPY7rfw8WCqHxI7bsGVLwQKNGRsj9cCun4a +Dc3Fi9JNi725EWaauQjy8kz/1AVwyiId56hgvA3D6jO/3xocW+XUdE/cwPHi/Fsp +w0wD6AdcISSNi1yvBiur6obEuLrh4GMRAAEtRipzR1h+hNrtxjx85G0YhDtGieIv +OudbdzMp/z/IR+etO8rUK/CknTILqTVIeBKVL319CoIcTkI2RaUSo8RcQrjkh62p +yPj7v4AFPT0u8f3jK3IOeRuLlPjz17P8c6I2aUWRDuvUwzYjKCQsSgrRfWujUe/S +rflapdInAKfT+ibz96JVwmFKDmtIAOmze8iU5Xy1az9gOX7BMS4wc4bGaWG4x125 +0JDNTIG7nZzN/T2YrS3IDfsuIqxPCNVpxdWsl8QLRFSru2NUE+NdwmKxOlM4Qtw0 +87HfND0VCbxkaHs3kBTJYCmvsyqErB1P+0JLcTlVSAYQg/M6QyvrG4WGrzIp1p/n +mrOS/ulW/TEB8wyHhUorlHTlQODkpzOeVJLpMMSOf39YT71jkT4TeooPTJchgSrU +fw5eDJggJSFI/rEI+jiYMnD22WDXGmJXqsTKWjSdkQ0P7ttINyHTPnf0kM1T49QN +CwDYJyGrBmMKAchf9qdr +=/j6K +-----END PGP SIGNATURE----- diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec new file mode 100644 index 0000000..b2f9a3d --- /dev/null +++ b/SPECS/nmstate.spec @@ -0,0 +1,129 @@ +%?python_enable_dependency_generator +%define srcname nmstate +%define libname libnmstate + +Name: nmstate +Version: 0.0.8 +Release: 15%{?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 +Patch1: BZ_1738101-Exclude-ipv6-changes-from-reapply.patch +Patch2: BZ_1740554_rollback_to_ipv6_disabled.patch +Patch3: BZ_1740584-Ignore-not-active-failure-when-deactivate.patch +Patch4: BZ_1741049_vlan_interface_should_activated_after_base.patch +Patch5: BZ_1740125_Increase-main-loop-timeout.patch +Patch6: BZ_1741440_Fix_Activation_Race_of_Slave.patch +Patch7: BZ_1749314_Prioritize-master-interfaces-activaction.patch +Patch8: BZ_1748825_Change-DHCPv4-client-ID-to-MAC-address.patch +Patch9: BZ_1748825_Enforce-IPv6-RA-and-DHCPv6-address-base-on-MAC.patch +Patch10: BZ_1749632_Detach-slaves-without-deleting-them.patch +BuildArch: noarch +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: gnupg2 +Requires: python3-setuptools +Requires: python3-%{libname} = %{?epoch:%{epoch}:}%{version}-%{release} +Requires: NetworkManager-config-server + +%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 +# Use Recommends for NetworkManager because only access to NM DBus is required, +# but NM could be running on a different host +Recommends: NetworkManager +# Use Suggests for NetworkManager-ovs since it is only required for OVS support +Suggests: NetworkManager-ovs + + +%description -n python3-%{libname} +This package contains the Python 3 library for nmstate. + +%prep +gpgv2 --keyring %{SOURCE2} %{SOURCE1} %{SOURCE0} +%autosetup -p1 + +%build +%py3_build + +%install +%py3_install + +%files +%doc README.md +%doc examples/ +%{python3_sitelib}/nmstatectl +%{_bindir}/nmstatectl + +%files -n python3-%{libname} +%license LICENSE +%{python3_sitelib}/%{libname} +%{python3_sitelib}/%{srcname}-*.egg-info/ + +%changelog +* Tue Sep 10 2019 Gris Ge - 0.0.8-15 +- Detach slaves without deleting them: RHBZ #1749632 + +* Fri Sep 06 2019 Gris Ge - 0.0.8-14 +- Preserve (dynamic) IPv6 address base on MAC address: RHBZ #1748825 + +* Fri Sep 06 2019 Gris Ge - 0.0.8-13 +- Prioritize master interfaces activaction: RHBZ #1749314 + +* Mon Sep 02 2019 Gris Ge - 0.0.8-12 +- Fix slave activatoin race: RHBZ #1741440 + +* Mon Sep 02 2019 Gris Ge - 0.0.8-10 +- Fix RHBZ #1740125 + +* Wed Aug 14 2019 Gris Ge - 0.0.8-9 +- Fix RHBZ #1741049 + +* Wed Aug 14 2019 Gris Ge - 0.0.8-8 +- Fix RHBZ #1740584 + +* Tue Aug 13 2019 Gris Ge - 0.0.8-7 +- Fix RHBZ #1740554 + +* Tue Aug 13 2019 Gris Ge - 0.0.8-6 +- Bump release tag as CNV took the -5. + +* Tue Aug 13 2019 Gris Ge - 0.0.8-5 +- Bump release tag as CNV took the -4. + +* Tue Aug 13 2019 Gris Ge - 0.0.8-4 +- Disable reapply on ipv6 to fix bug 1738101. + +* Fri Jul 26 2019 Gris Ge - 0.0.8-3 +- Fix the license to meet Fedora/RHEL guideline. + +* Fri Jul 26 2019 Gris Ge - 0.0.8-2 +- Relicense to LGPL2.1+. + +* Fri Jul 26 2019 Gris Ge - 0.0.8-1 +- Upgrade to 0.0.8. + +* Fri Jun 14 2019 Gris Ge - 0.0.7-1 +- Upgrade to 0.0.7. + +* Mon Apr 22 2019 Gris Ge - 0.0.5-3 +- Add missing runtime dependency. + +* Thu Mar 21 2019 Gris Ge - 0.0.5-2 +- Rebuild to enable CI testing. + +* Mon Mar 18 2019 Gris Ge - 0.0.5-1 +- Initial release