From 794aa4f8ebdcf96525d021b4a2196465cf936688 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Tue, 16 Jan 2024 13:25:12 -0500 Subject: [PATCH] * Tue Jan 16 2024 Jon Maloy - 23.4-2 - ci-net-network_manager-do-not-set-may-fail-to-False-for.patch [RHEL-7277] - ci-net-allow-dhcp6-configuration-from-generate_fallback.patch [RHEL-7277] - ci-net-nm-check-for-presence-of-ifcfg-files-when-nm-con.patch [RHEL-17610] - Resolves: RHEL-7277 ([RFE] [Azure][RHEL8][Network][cloud-init] Can not acquire IPv6 address) - Resolves: RHEL-17610 ([RHEL-8] NetworkManagerActivator brings up interface failed when using sysconfig renderer) --- ...configuration-from-generate_fallback.patch | 182 ++++++++++++++++++ ...ger-do-not-set-may-fail-to-False-for.patch | 150 +++++++++++++++ ...-presence-of-ifcfg-files-when-nm-con.patch | 123 ++++++++++++ cloud-init.spec | 17 +- 4 files changed, 471 insertions(+), 1 deletion(-) create mode 100644 ci-net-allow-dhcp6-configuration-from-generate_fallback.patch create mode 100644 ci-net-network_manager-do-not-set-may-fail-to-False-for.patch create mode 100644 ci-net-nm-check-for-presence-of-ifcfg-files-when-nm-con.patch diff --git a/ci-net-allow-dhcp6-configuration-from-generate_fallback.patch b/ci-net-allow-dhcp6-configuration-from-generate_fallback.patch new file mode 100644 index 0000000..3cb9acf --- /dev/null +++ b/ci-net-allow-dhcp6-configuration-from-generate_fallback.patch @@ -0,0 +1,182 @@ +From 2942fb776cd1fc765089ebd0004e01dc2b3a5920 Mon Sep 17 00:00:00 2001 +From: Ani Sinha +Date: Tue, 21 Nov 2023 13:57:15 +0530 +Subject: [PATCH 2/3] net: allow dhcp6 configuration from + generate_fallback_configuration() + +RH-Author: Ani Sinha +RH-MergeRequest: 119: net: allow dhcp6 configuration from generate_fallback_configuration() +RH-Jira: RHEL-7277 +RH-Acked-by: Jon Maloy +RH-Acked-by: Cathy Avery +RH-Commit: [2/2] b067c813488dfddc79d8ebd5bb51894ff040c356 + +This will make sure on Azure we can use both dhcp4 and dhcp6 when IMDS is not +used. This is useful in situations where only ipv6 network is available and +there is no dhcp4 running. + +This change is mostly a reversal of commit 29ed5f5b646ee and therefore, +re-application of the commit 518047aea9 with some small changes. + +The issue that caused the reversal of 518047aea9 is fixed by the earlier commit: +cab0eaf290af7 ("net/network_manager: do not set "may-fail" to False for both ipv4 and ipv6 dhcp") + +Fixes GH-4439 + +Signed-off-by: Ani Sinha +(cherry picked from commit 0264e969166846b2f5cf87ccdb051a3a795eca15) +--- + cloudinit/net/__init__.py | 7 ++++++- + tests/unittests/net/test_init.py | 4 ++++ + tests/unittests/test_net.py | 24 +++++++++++++++++++++--- + 3 files changed, 31 insertions(+), 4 deletions(-) + +diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py +index bf21633b..c0888f52 100644 +--- a/cloudinit/net/__init__.py ++++ b/cloudinit/net/__init__.py +@@ -571,7 +571,12 @@ def generate_fallback_config(config_driver=None): + match = { + "macaddress": read_sys_net_safe(target_name, "address").lower() + } +- cfg = {"dhcp4": True, "set-name": target_name, "match": match} ++ cfg = { ++ "dhcp4": True, ++ "dhcp6": True, ++ "set-name": target_name, ++ "match": match, ++ } + if config_driver: + driver = device_driver(target_name) + if driver: +diff --git a/tests/unittests/net/test_init.py b/tests/unittests/net/test_init.py +index 561d5151..60a44186 100644 +--- a/tests/unittests/net/test_init.py ++++ b/tests/unittests/net/test_init.py +@@ -261,6 +261,7 @@ class TestGenerateFallbackConfig(CiTestCase): + "eth1": { + "match": {"macaddress": mac}, + "dhcp4": True, ++ "dhcp6": True, + "set-name": "eth1", + } + }, +@@ -278,6 +279,7 @@ class TestGenerateFallbackConfig(CiTestCase): + "eth0": { + "match": {"macaddress": mac}, + "dhcp4": True, ++ "dhcp6": True, + "set-name": "eth0", + } + }, +@@ -293,6 +295,7 @@ class TestGenerateFallbackConfig(CiTestCase): + "ethernets": { + "eth0": { + "dhcp4": True, ++ "dhcp6": True, + "match": {"macaddress": mac}, + "set-name": "eth0", + } +@@ -359,6 +362,7 @@ class TestGenerateFallbackConfig(CiTestCase): + "ethernets": { + "ens3": { + "dhcp4": True, ++ "dhcp6": True, + "match": {"name": "ens3"}, + "set-name": "ens3", + } +diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py +index 54d053f3..e52c2497 100644 +--- a/tests/unittests/test_net.py ++++ b/tests/unittests/test_net.py +@@ -4299,6 +4299,7 @@ class TestGenerateFallbackConfig(CiTestCase): + "ethernets": { + "eth0": { + "dhcp4": True, ++ "dhcp6": True, + "set-name": "eth0", + "match": { + "macaddress": "00:11:22:33:44:55", +@@ -4383,6 +4384,9 @@ iface lo inet loopback + + auto eth0 + iface eth0 inet dhcp ++ ++# control-alias eth0 ++iface eth0 inet6 dhcp + """ + self.assertEqual(expected.lstrip(), contents.lstrip()) + +@@ -4472,6 +4476,9 @@ iface lo inet loopback + + auto eth1 + iface eth1 inet dhcp ++ ++# control-alias eth1 ++iface eth1 inet6 dhcp + """ + self.assertEqual(expected.lstrip(), contents.lstrip()) + +@@ -4695,7 +4702,9 @@ class TestRhelSysConfigRendering(CiTestCase): + # + BOOTPROTO=dhcp + DEVICE=eth1000 ++DHCPV6C=yes + HWADDR=07-1c-c6-75-a4-be ++IPV6INIT=yes + ONBOOT=yes + TYPE=Ethernet + USERCTL=no +@@ -5593,7 +5602,8 @@ class TestOpenSuseSysConfigRendering(CiTestCase): + expected_content = """ + # Created by cloud-init automatically, do not edit. + # +-BOOTPROTO=dhcp4 ++BOOTPROTO=dhcp ++DHCLIENT6_MODE=managed + LLADDR=07-1c-c6-75-a4-be + STARTMODE=auto + """.lstrip() +@@ -5979,7 +5989,11 @@ class TestNetworkManagerRendering(CiTestCase): + + [ipv4] + method=auto +- may-fail=false ++ may-fail=true ++ ++ [ipv6] ++ method=auto ++ may-fail=true + + """ + ), +@@ -6245,6 +6259,9 @@ iface lo inet loopback + + auto eth1000 + iface eth1000 inet dhcp ++ ++# control-alias eth1000 ++iface eth1000 inet6 dhcp + """ + self.assertEqual(expected.lstrip(), contents.lstrip()) + +@@ -6304,6 +6321,7 @@ class TestNetplanNetRendering: + ethernets: + eth1000: + dhcp4: true ++ dhcp6: true + match: + macaddress: 07-1c-c6-75-a4-be + set-name: eth1000 +@@ -7803,7 +7821,7 @@ class TestNetworkdNetRendering(CiTestCase): + Name=eth1000 + MACAddress=07-1c-c6-75-a4-be + [Network] +- DHCP=ipv4""" ++ DHCP=yes""" + ).rstrip(" ") + + expected = self.create_conf_dict(expected.splitlines()) +-- +2.41.0 + diff --git a/ci-net-network_manager-do-not-set-may-fail-to-False-for.patch b/ci-net-network_manager-do-not-set-may-fail-to-False-for.patch new file mode 100644 index 0000000..adc3246 --- /dev/null +++ b/ci-net-network_manager-do-not-set-may-fail-to-False-for.patch @@ -0,0 +1,150 @@ +From 010cd58942c82e902bc02cb5a34074f6083fc890 Mon Sep 17 00:00:00 2001 +From: Ani Sinha +Date: Thu, 23 Nov 2023 12:27:51 +0530 +Subject: [PATCH 1/3] net/network_manager: do not set "may-fail" to False for + both ipv4 and ipv6 dhcp + +RH-Author: Ani Sinha +RH-MergeRequest: 119: net: allow dhcp6 configuration from generate_fallback_configuration() +RH-Jira: RHEL-7277 +RH-Acked-by: Jon Maloy +RH-Acked-by: Cathy Avery +RH-Commit: [1/2] be07418f69a4c461e2fa02a72b7b985053af9660 + +If "may-fail" is set to False in the Network Manager keyfile for both ipv4 +and ipv6 for dhcp configuration, it essentially means both ipv4 and ipv6 network +initialization using dhcp must succeed for the overall network configuration to +succeed. This means, for environments where only ipv4 or ipv6 is available but +not both and we need to configure both ipv4 and ipv6 dhcp, the overall +network configuration will fail. This is not what we want. When both ipv4 +and ipv6 dhcp are configured, it is enough for the overall configuration to +succeed if any one succeeds. +Therefore, set "may-fail" to True for both ipv4 and ipv6 if and only if both +ipv4 and ipv6 are configured as dhcp in the Network Manager keyfile and +"may-fail" is set to False for both. If both ipv4 and ipv6 are configured +in the keyfile and if for any of them "may-fail" is already set to True,then +do nothing. +All other cases remain same as before. + +Please see discussions in PR #4474. + +Co-authored-by: James Falcon +Signed-off-by: Ani Sinha +(cherry picked from commit 29dd5ace73ad60c7452c39b840045fb47fe0711f) +--- + cloudinit/net/network_manager.py | 59 ++++++++++++++++++++++++++++++++ + tests/unittests/test_net.py | 8 ++--- + 2 files changed, 63 insertions(+), 4 deletions(-) + +diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py +index 8374cfcc..8a99eb3a 100644 +--- a/cloudinit/net/network_manager.py ++++ b/cloudinit/net/network_manager.py +@@ -71,6 +71,57 @@ class NMConnection: + if not self.config.has_option(section, option): + self.config[section][option] = value + ++ def _config_option_is_set(self, section, option): ++ """ ++ Checks if a config option is set. Returns True if it is, ++ else returns False. ++ """ ++ return self.config.has_section(section) and self.config.has_option( ++ section, option ++ ) ++ ++ def _get_config_option(self, section, option): ++ """ ++ Returns the value of a config option if its set, ++ else returns None. ++ """ ++ if self._config_option_is_set(section, option): ++ return self.config[section][option] ++ else: ++ return None ++ ++ def _change_set_config_option(self, section, option, value): ++ """ ++ Overrides the value of a config option if its already set. ++ Else, if the config option is not set, it does nothing. ++ """ ++ if self._config_option_is_set(section, option): ++ self.config[section][option] = value ++ ++ def _set_mayfail_true_if_both_false_dhcp(self): ++ """ ++ If for both ipv4 and ipv6, 'may-fail' is set to be False, ++ set it to True for both of them. ++ """ ++ for family in ["ipv4", "ipv6"]: ++ if self._get_config_option(family, "may-fail") != "false": ++ # if either ipv4 or ipv6 sections are not set/configured, ++ # or if both are configured but for either ipv4 or ipv6, ++ # 'may-fail' is not 'false', do not do anything. ++ return ++ if self._get_config_option(family, "method") not in [ ++ "dhcp", ++ "auto", ++ ]: ++ # if both v4 and v6 are not dhcp, do not do anything. ++ return ++ ++ # If we landed here, it means both ipv4 and ipv6 are configured ++ # with dhcp/auto and both have 'may-fail' set to 'false'. So set ++ # both to 'true'. ++ for family in ["ipv4", "ipv6"]: ++ self._change_set_config_option(family, "may-fail", "true") ++ + def _set_ip_method(self, family, subnet_type): + """ + Ensures there's appropriate [ipv4]/[ipv6] for given family +@@ -271,6 +322,14 @@ class NMConnection: + if family == "ipv4" and "mtu" in subnet: + ipv4_mtu = subnet["mtu"] + ++ # we do not want to set may-fail to false for both ipv4 and ipv6 dhcp ++ # at the at the same time. This will make the network configuration ++ # work only when both ipv4 and ipv6 dhcp succeeds. This may not be ++ # what we want. If we have configured both ipv4 and ipv6 dhcp, any one ++ # succeeding should be enough. Therefore, if "may-fail" is set to ++ # False for both ipv4 and ipv6 dhcp, set them both to True. ++ self._set_mayfail_true_if_both_false_dhcp() ++ + if ipv4_mtu is None: + ipv4_mtu = device_mtu + if not ipv4_mtu == device_mtu: +diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py +index 052b0674..54d053f3 100644 +--- a/tests/unittests/test_net.py ++++ b/tests/unittests/test_net.py +@@ -1470,11 +1470,11 @@ NETWORK_CONFIGS = { + + [ipv4] + method=auto +- may-fail=false ++ may-fail=true + + [ipv6] + method=auto +- may-fail=false ++ may-fail=true + + """ + ), +@@ -1642,11 +1642,11 @@ NETWORK_CONFIGS = { + + [ipv6] + method=auto +- may-fail=false ++ may-fail=true + + [ipv4] + method=auto +- may-fail=false ++ may-fail=true + + """ + ), +-- +2.41.0 + diff --git a/ci-net-nm-check-for-presence-of-ifcfg-files-when-nm-con.patch b/ci-net-nm-check-for-presence-of-ifcfg-files-when-nm-con.patch new file mode 100644 index 0000000..f0a5e83 --- /dev/null +++ b/ci-net-nm-check-for-presence-of-ifcfg-files-when-nm-con.patch @@ -0,0 +1,123 @@ +From ffc8f3fbb4c8c14a4ef2b6a99a9ea61da4bedde7 Mon Sep 17 00:00:00 2001 +From: Ani Sinha +Date: Thu, 7 Dec 2023 02:39:51 +0530 +Subject: [PATCH 3/3] net/nm: check for presence of ifcfg files when nm + connection files are absent (#4645) + +RH-Author: Ani Sinha +RH-MergeRequest: 120: net/nm: check for presence of ifcfg files when nm connection files are absent (#4645) +RH-Jira: RHEL-17610 +RH-Acked-by: Emanuele Giuseppe Esposito +RH-Acked-by: Jon Maloy +RH-Commit: [1/1] e0647418de8b70724a32500f26f544650d701404 + +On systems that use network manager to manage connections and activate network +interfaces, they may also use ifcfg files for configuring +interfaces using ifcfg-rh network manager plugin. When network manager is used +as the activator, we need to also check for the presence of ifcfg interface +config file when the network manager connection file is absent and if ifcfg-rh +plugin is present. +Hence, with this change, network manager activator first tries to use network +manager connection files to bring up or bring down the interface. If the +connection files are not present and if ifcfg-rh plugin is present, it tries to +use ifcfg files for the interface. If the plugin or the ifcfg files are not +present, the activator fails to activate or deactivate the interface and it +bails out with warning log. + +Fixes: GH-4640 + +Signed-off-by: Ani Sinha +(cherry picked from commit d1d5166895da471cff3606c70d4e8ab6eec1c006) +--- + cloudinit/net/activators.py | 7 +++++++ + cloudinit/net/network_manager.py | 33 ++++++++++++++++++++++++++++++-- + 2 files changed, 38 insertions(+), 2 deletions(-) + +diff --git a/cloudinit/net/activators.py b/cloudinit/net/activators.py +index e69da40d..dd858862 100644 +--- a/cloudinit/net/activators.py ++++ b/cloudinit/net/activators.py +@@ -117,6 +117,13 @@ class NetworkManagerActivator(NetworkActivator): + from cloudinit.net.network_manager import conn_filename + + filename = conn_filename(device_name) ++ if filename is None: ++ LOG.warning( ++ "Unable to find an interface config file. " ++ "Unable to bring up interface." ++ ) ++ return False ++ + cmd = ["nmcli", "connection", "load", filename] + if _alter_interface(cmd, device_name): + cmd = ["nmcli", "connection", "up", "filename", filename] +diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py +index 8a99eb3a..76a0ac15 100644 +--- a/cloudinit/net/network_manager.py ++++ b/cloudinit/net/network_manager.py +@@ -17,10 +17,12 @@ from typing import Optional + from cloudinit import subp, util + from cloudinit.net import is_ipv6_address, renderer, subnet_is_ipv6 + from cloudinit.net.network_state import NetworkState ++from cloudinit.net.sysconfig import available_nm_ifcfg_rh + + NM_RUN_DIR = "/etc/NetworkManager" + NM_LIB_DIR = "/usr/lib/NetworkManager" + NM_CFG_FILE = "/etc/NetworkManager/NetworkManager.conf" ++IFCFG_CFG_FILE = "/etc/sysconfig/network-scripts" + NM_IPV6_ADDR_GEN_CONF = """# This is generated by cloud-init. Do not edit. + # + [.config] +@@ -442,7 +444,7 @@ class Renderer(renderer.Renderer): + for con_id, conn in self.connections.items(): + if not conn.valid(): + continue +- name = conn_filename(con_id, target) ++ name = nm_conn_filename(con_id, target) + util.write_file(name, conn.dump(), 0o600) + + # Select EUI64 to be used by default by NM for creating the address +@@ -452,12 +454,39 @@ class Renderer(renderer.Renderer): + ) + + +-def conn_filename(con_id, target=None): ++def nm_conn_filename(con_id, target=None): + target_con_dir = subp.target_path(target, NM_RUN_DIR) + con_file = f"cloud-init-{con_id}.nmconnection" + return f"{target_con_dir}/system-connections/{con_file}" + + ++def sysconfig_conn_filename(devname, target=None): ++ target_con_dir = subp.target_path(target, IFCFG_CFG_FILE) ++ con_file = f"ifcfg-{devname}" ++ return f"{target_con_dir}/{con_file}" ++ ++ ++def conn_filename(devname): ++ """ ++ This function returns the name of the interface config file. ++ It first checks for presence of network manager connection file. ++ If absent and ifcfg-rh plugin for network manager is available, ++ it returns the name of the ifcfg file if it is present. If the ++ plugin is not present or the plugin is present but ifcfg file is ++ not, it returns None. ++ This function is called from NetworkManagerActivator class in ++ activators.py. ++ """ ++ conn_file = nm_conn_filename(devname) ++ # If the network manager connection file is absent, also check for ++ # presence of ifcfg files for the same interface (if nm-ifcfg-rh plugin is ++ # present, network manager can handle ifcfg files). If both network manager ++ # connection file and ifcfg files are absent, return None. ++ if not os.path.isfile(conn_file) and available_nm_ifcfg_rh(): ++ conn_file = sysconfig_conn_filename(devname) ++ return conn_file if os.path.isfile(conn_file) else None ++ ++ + def cloud_init_nm_conf_filename(target=None): + target_con_dir = subp.target_path(target, NM_RUN_DIR) + conf_file = "30-cloud-init-ip6-addr-gen-mode.conf" +-- +2.41.0 + diff --git a/cloud-init.spec b/cloud-init.spec index 0aa1e42..bd2c2b1 100644 --- a/cloud-init.spec +++ b/cloud-init.spec @@ -6,7 +6,7 @@ Name: cloud-init Version: 23.4 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Cloud instance init scripts Group: System Environment/Base @@ -19,6 +19,12 @@ Patch0003: 0003-Do-not-write-NM_CONTROLLED-no-in-generated-interface.patch Patch0004: 0004-include-NOZEROCONF-yes-in-etc-sysconfig-network.patch Patch0005: 0005-settings.py-update-settings-for-rhel.patch Patch0013: 0013-rhel-cloud.cfg-remove-ssh_genkeytypes-in-settings.py.patch +# For RHEL-7277 - [RFE] [Azure][RHEL8][Network][cloud-init] Can not acquire IPv6 address +Patch14: ci-net-network_manager-do-not-set-may-fail-to-False-for.patch +# For RHEL-7277 - [RFE] [Azure][RHEL8][Network][cloud-init] Can not acquire IPv6 address +Patch15: ci-net-allow-dhcp6-configuration-from-generate_fallback.patch +# For RHEL-17610 - [RHEL-8] NetworkManagerActivator brings up interface failed when using sysconfig renderer +Patch16: ci-net-nm-check-for-presence-of-ifcfg-files-when-nm-con.patch BuildArch: noarch @@ -235,6 +241,15 @@ fi %config(noreplace) %{_sysconfdir}/rsyslog.d/21-cloudinit.conf %changelog +* Tue Jan 16 2024 Jon Maloy - 23.4-2 +- ci-net-network_manager-do-not-set-may-fail-to-False-for.patch [RHEL-7277] +- ci-net-allow-dhcp6-configuration-from-generate_fallback.patch [RHEL-7277] +- ci-net-nm-check-for-presence-of-ifcfg-files-when-nm-con.patch [RHEL-17610] +- Resolves: RHEL-7277 + ([RFE] [Azure][RHEL8][Network][cloud-init] Can not acquire IPv6 address) +- Resolves: RHEL-17610 + ([RHEL-8] NetworkManagerActivator brings up interface failed when using sysconfig renderer) + * Mon Jan 8 2024 Jon Maloy - 23.4-1 - Rebase to 23.4.1 [RHEL-18314] - Resolves: RHEL-18314