cloud-init: 23.1.2 -> 23.2

Keep support for udhcpc (waiting for upstream PR:
https://github.com/canonical/cloud-init/pull/4190).

Vultr patch has been merged upstream
(https://github.com/canonical/cloud-init/pull/2151).
This commit is contained in:
Jean-François Roche 2023-06-19 17:18:36 +02:00
parent 619d9c7bb7
commit 67f5018fe6
4 changed files with 337 additions and 244 deletions

View file

@ -12,10 +12,10 @@ index b82852e1..c998b21e 100644
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
diff --git a/cloudinit/distros/nixos.py b/cloudinit/distros/nixos.py diff --git a/cloudinit/distros/nixos.py b/cloudinit/distros/nixos.py
new file mode 100644 new file mode 100644
index 00000000..d53d2a61 index 00000000..d53d2a62
--- /dev/null --- /dev/null
+++ b/cloudinit/distros/nixos.py +++ b/cloudinit/distros/nixos.py
@@ -0,0 +1,103 @@ @@ -0,0 +1,109 @@
+# vi: ts=4 expandtab +# vi: ts=4 expandtab
+# +#
+# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012 Canonical Ltd.
@ -47,6 +47,7 @@ index 00000000..d53d2a61
+from cloudinit import atomic_helper +from cloudinit import atomic_helper
+ +
+from cloudinit.distros.parsers.hostname import HostnameConf +from cloudinit.distros.parsers.hostname import HostnameConf
+from cloudinit.net import dhcp
+ +
+LOG = logging.getLogger(__name__) +LOG = logging.getLogger(__name__)
+ +
@ -61,6 +62,11 @@ index 00000000..d53d2a61
+ self.usr_lib_exec = os.path.join(os.path.dirname(__file__), + self.usr_lib_exec = os.path.join(os.path.dirname(__file__),
+ "../../../../../libexec") + "../../../../../libexec")
+ self.osfamily = 'nixos' + self.osfamily = 'nixos'
+ self.dhcp_client_priority = [
+ dhcp.Udhcpc,
+ dhcp.IscDhclient,
+ dhcp.Dhcpcd,
+ ]
+ +
+ def _select_hostname(self, hostname, fqdn): + def _select_hostname(self, hostname, fqdn):
+ # Prefer the short hostname over the long + # Prefer the short hostname over the long
@ -112,10 +118,10 @@ index 00000000..d53d2a61
+ raise NotImplementedError() + raise NotImplementedError()
+ +
+ def package_command(self, command, args=None, pkgs=None): + def package_command(self, command, args=None, pkgs=None):
+ raise NotImplementedError() + pass
+ +
+ def set_timezone(self, tz): + def set_timezone(self, tz):
+ raise NotImplementedError() + pass
+ +
+ def update_package_sources(self): + def update_package_sources(self):
+ raise NotImplementedError() + pass

View file

@ -1,15 +1,56 @@
From 53260ce3bd70a0852d3e0d5569474214cea0ec0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= <jfroche@pyxel.be>
Date: Mon, 19 Jun 2023 15:56:46 +0200
Subject: [PATCH] net/dhcp: add udhcpc support
The currently used dhcp client, dhclient, is coming from the unmaintained package, isc-dhcp-client (refer https://www.isc.org/dhcp/) which ended support in 2022.
This change introduce support for the dhcp client, udhcpc, from the busybox project. Busybox advantages are that it is available across many distributions and comes with lightweight executables.
---
cloudinit/distros/__init__.py | 8 +-
cloudinit/net/dhcp.py | 129 ++++++++++++++++++++++-
tests/unittests/net/test_dhcp.py | 175 ++++++++++++++++++++++++++++++-
tools/.github-cla-signers | 1 +
4 files changed, 309 insertions(+), 4 deletions(-)
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index ec148939..0fab8945 100644
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -110,14 +110,18 @@ class Distro(persistence.CloudInitPickleMixin, metaclass=abc.ABCMeta):
resolve_conf_fn = "/etc/resolv.conf"
osfamily: str
- dhcp_client_priority = [dhcp.IscDhclient, dhcp.Dhcpcd]
+ dhcp_client_priority = [dhcp.IscDhclient, dhcp.Dhcpcd, dhcp.Udhcpc]
def __init__(self, name, cfg, paths):
self._paths = paths
self._cfg = cfg
self.name = name
self.networking: Networking = self.networking_cls()
- self.dhcp_client_priority = [dhcp.IscDhclient, dhcp.Dhcpcd]
+ self.dhcp_client_priority = [
+ dhcp.IscDhclient,
+ dhcp.Dhcpcd,
+ dhcp.Udhcpc,
+ ]
def _unpickle(self, ci_pkl_version: int) -> None:
"""Perform deserialization fixes for Distro."""
diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py
index a9a1c980..2d83089b 100644 index 6c8c2f54..f5586cea 100644
--- a/cloudinit/net/dhcp.py --- a/cloudinit/net/dhcp.py
+++ b/cloudinit/net/dhcp.py +++ b/cloudinit/net/dhcp.py
@@ -14,12 +14,48 @@ from io import StringIO @@ -21,6 +21,7 @@ from cloudinit import subp, temp_utils, util
from cloudinit.net import (
import configobj find_fallback_nic,
get_devicelist,
-from cloudinit import subp, util + get_ib_interface_hwaddr,
+from cloudinit import subp, util, temp_utils get_interface_mac,
from cloudinit.net import find_fallback_nic, get_devicelist is_ib_interface,
)
@@ -28,6 +29,37 @@ from cloudinit.net import (
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
NETWORKD_LEASES_DIR = "/run/systemd/netif/leases" NETWORKD_LEASES_DIR = "/run/systemd/netif/leases"
@ -17,9 +58,7 @@ index a9a1c980..2d83089b 100644
+log() { +log() {
+ echo "udhcpc[$PPID]" "$interface: $2" + echo "udhcpc[$PPID]" "$interface: $2"
+} +}
+
+[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1 +[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1
+
+case $1 in +case $1 in
+ bound|renew) + bound|renew)
+ cat <<JSON > "$LEASE_FILE" + cat <<JSON > "$LEASE_FILE"
@ -32,17 +71,14 @@ index a9a1c980..2d83089b 100644
+} +}
+JSON +JSON
+ ;; + ;;
+
+ deconfig) + deconfig)
+ log err "Not supported" + log err "Not supported"
+ exit 1 + exit 1
+ ;; + ;;
+
+ leasefail | nak) + leasefail | nak)
+ log err "configuration failed: $1: $message" + log err "configuration failed: $1: $message"
+ exit 1 + exit 1
+ ;; + ;;
+
+ *) + *)
+ echo "$0: Unknown udhcpc command: $1" >&2 + echo "$0: Unknown udhcpc command: $1" >&2
+ exit 1 + exit 1
@ -52,134 +88,199 @@ index a9a1c980..2d83089b 100644
class NoDHCPLeaseError(Exception): class NoDHCPLeaseError(Exception):
@@ -43,12 +79,14 @@ class NoDHCPLeaseMissingDhclientError(NoDHCPLeaseError): @@ -50,6 +82,10 @@ class NoDHCPLeaseMissingDhclientError(NoDHCPLeaseError):
"""Raised when unable to find dhclient."""
def maybe_perform_dhcp_discovery(nic=None, dhcp_log_func=None, tmp_dir=None): +class NoDHCPLeaseMissingUdhcpcError(NoDHCPLeaseError):
- """Perform dhcp discovery if nic valid and dhclient command exists. + """Raised when unable to find udhcpc client."""
+ """Perform dhcp discovery if nic valid and dhclient or udhcpc command
+ exists.
If the nic is invalid or undiscoverable or dhclient command is not found,
skip dhcp_discovery and return an empty dict.
- @param nic: Name of the network interface we want to run dhclient on.
+ @param nic: Name of the network interface we want to run the dhcp client
+ on.
@param dhcp_log_func: A callable accepting the dhclient output and error
streams.
@param tmp_dir: Tmp dir with exec permissions.
@@ -66,11 +104,16 @@ def maybe_perform_dhcp_discovery(nic=None, dhcp_log_func=None, tmp_dir=None):
"Skip dhcp_discovery: nic %s not found in get_devicelist.", nic
)
raise NoDHCPLeaseInterfaceError()
+ udhcpc_path = subp.which("udhcpc")
+ if udhcpc_path:
+ return dhcp_udhcpc_discovery(udhcpc_path, nic, dhcp_log_func)
dhclient_path = subp.which("dhclient")
- if not dhclient_path:
- LOG.debug("Skip dhclient configuration: No dhclient command found.")
- raise NoDHCPLeaseMissingDhclientError()
- return dhcp_discovery(dhclient_path, nic, dhcp_log_func)
+ if dhclient_path:
+ return dhcp_discovery(dhclient_path, nic, dhcp_log_func)
+ LOG.debug(
+ "Skip dhclient configuration: No dhclient or udhcpc command found."
+ )
+ raise NoDHCPLeaseMissingDhclientError()
def parse_dhcp_lease_file(lease_file):
@@ -107,6 +150,61 @@ def parse_dhcp_lease_file(lease_file):
return dhcp_leases
+def dhcp_udhcpc_discovery(udhcpc_cmd_path, interface, dhcp_log_func=None):
+ """Run udhcpc on the interface without scripts or filesystem artifacts.
+ +
+ @param udhcpc_cmd_path: Full path to the udhcpc used.
+ @param interface: Name of the network interface on which to dhclient.
+ @param dhcp_log_func: A callable accepting the dhclient output and error
+ streams.
+ +
+ @return: A list of dicts of representing the dhcp leases parsed from the def select_dhcp_client(distro):
+ dhclient.lease file or empty list. """distros set priority list, select based on this order which to use
+ """
+ LOG.debug("Performing a dhcp discovery on %s", interface) @@ -60,7 +96,10 @@ def select_dhcp_client(distro):
dhcp_client = client()
LOG.debug("DHCP client selected: %s", client.client_name)
return dhcp_client
- except NoDHCPLeaseMissingDhclientError:
+ except (
+ NoDHCPLeaseMissingDhclientError,
+ NoDHCPLeaseMissingUdhcpcError,
+ ):
LOG.warning("DHCP client not found: %s", client.client_name)
raise NoDHCPLeaseMissingDhclientError()
@@ -497,3 +536,91 @@ class Dhcpcd:
def __init__(self):
raise NoDHCPLeaseMissingDhclientError("Dhcpcd not yet implemented")
+ +
+ tmp_dir = temp_utils.get_tmp_ancestor(needs_exe=True)
+ lease_file = os.path.join(tmp_dir, interface + ".lease.json")
+ with contextlib.suppress(FileNotFoundError):
+ os.remove(lease_file)
+ +
+ # udhcpc needs the interface up to send initial discovery packets. +class Udhcpc(DhcpClient):
+ # Generally dhclient relies on dhclient-script PREINIT action to bring the + client_name = "udhcpc"
+ # link up before attempting discovery. Since we are using -sf /bin/true, +
+ # we need to do that "link up" ourselves first. + def __init__(self):
+ subp.subp(["ip", "link", "set", "dev", interface, "up"], capture=True) + self.udhcpc_path = subp.which("udhcpc")
+ udhcpc_script = os.path.join(tmp_dir, "udhcpc_script") + if not self.udhcpc_path:
+ util.write_file(udhcpc_script, UDHCPC_SCRIPT, 0o755) + LOG.debug("Skip udhcpc configuration: No udhcpc command found.")
+ cmd = [ + raise NoDHCPLeaseMissingUdhcpcError()
+ udhcpc_cmd_path, +
+ "-O", + def dhcp_discovery(
+ "staticroutes", + self,
+ "-i",
+ interface, + interface,
+ "-s", + dhcp_log_func=None,
+ udhcpc_script, + distro=None,
+ "-n", # Exit if lease is not obtained + ):
+ "-q", # Exit after obtaining lease + """Run udhcpc on the interface without scripts or filesystem artifacts.
+ "-f", # Run in foreground
+ "-v",
+ ]
+ +
+ out, err = subp.subp( + @param interface: Name of the network interface on which to run udhcpc.
+ cmd, update_env={"LEASE_FILE": lease_file}, capture=True + @param dhcp_log_func: A callable accepting the udhcpc output and
+ ) + error streams.
+ +
+ if dhcp_log_func is not None: + @return: A list of dicts of representing the dhcp leases parsed from
+ dhcp_log_func(out, err) + the udhcpc lease file.
+ lease_json = util.load_json(util.load_file(lease_file)) + """
+ static_routes = lease_json["static_routes"].split() + LOG.debug("Performing a dhcp discovery on %s", interface)
+ if static_routes: +
+ # format: dest1/mask gw1 ... destn/mask gwn + tmp_dir = temp_utils.get_tmp_ancestor(needs_exe=True)
+ lease_json["static_routes"] = [ + lease_file = os.path.join(tmp_dir, interface + ".lease.json")
+ i for i in zip(static_routes[::2], static_routes[1::2]) + with contextlib.suppress(FileNotFoundError):
+ os.remove(lease_file)
+
+ # udhcpc needs the interface up to send initial discovery packets
+ subp.subp(["ip", "link", "set", "dev", interface, "up"], capture=True)
+
+ udhcpc_script = os.path.join(tmp_dir, "udhcpc_script")
+ util.write_file(udhcpc_script, UDHCPC_SCRIPT, 0o755)
+
+ cmd = [
+ self.udhcpc_path,
+ "-O",
+ "staticroutes",
+ "-i",
+ interface,
+ "-s",
+ udhcpc_script,
+ "-n", # Exit if lease is not obtained
+ "-q", # Exit after obtaining lease
+ "-f", # Run in foreground
+ "-v",
+ ] + ]
+ return [lease_json]
+ +
+ # For INFINIBAND port the dhcpc must be running with
+ # client id option. So here we are checking if the interface is
+ # INFINIBAND or not. If yes, we are generating the the client-id to be
+ # used with the udhcpc
+ if is_ib_interface(interface):
+ dhcp_client_identifier = get_ib_interface_hwaddr(
+ interface, ethernet_format=True
+ )
+ cmd.extend(
+ ["-x", "0x3d:%s" % dhcp_client_identifier.replace(":", "")]
+ )
+ try:
+ out, err = subp.subp(
+ cmd, update_env={"LEASE_FILE": lease_file}, capture=True
+ )
+ except subp.ProcessExecutionError as error:
+ LOG.debug(
+ "udhcpc exited with code: %s stderr: %r stdout: %r",
+ error.exit_code,
+ error.stderr,
+ error.stdout,
+ )
+ raise NoDHCPLeaseError from error
+ +
def dhcp_discovery(dhclient_cmd_path, interface, dhcp_log_func=None): + if dhcp_log_func is not None:
"""Run dhclient on the interface without scripts or filesystem artifacts. + dhcp_log_func(out, err)
+
+ lease_json = util.load_json(util.load_file(lease_file))
+ static_routes = lease_json["static_routes"].split()
+ if static_routes:
+ # format: dest1/mask gw1 ... destn/mask gwn
+ lease_json["static_routes"] = [
+ i for i in zip(static_routes[::2], static_routes[1::2])
+ ]
+ return [lease_json]
diff --git a/tests/unittests/net/test_dhcp.py b/tests/unittests/net/test_dhcp.py diff --git a/tests/unittests/net/test_dhcp.py b/tests/unittests/net/test_dhcp.py
index 40340553..8913cf65 100644 index 55d4c6e9..9123cd15 100644
--- a/tests/unittests/net/test_dhcp.py --- a/tests/unittests/net/test_dhcp.py
+++ b/tests/unittests/net/test_dhcp.py +++ b/tests/unittests/net/test_dhcp.py
@@ -12,6 +12,7 @@ from cloudinit.net.dhcp import ( @@ -13,6 +13,8 @@ from cloudinit.net.dhcp import (
NoDHCPLeaseError, NoDHCPLeaseError,
NoDHCPLeaseInterfaceError, NoDHCPLeaseInterfaceError,
NoDHCPLeaseMissingDhclientError, NoDHCPLeaseMissingDhclientError,
+ dhcp_udhcpc_discovery, + NoDHCPLeaseMissingUdhcpcError,
dhcp_discovery, + Udhcpc,
maybe_perform_dhcp_discovery, maybe_perform_dhcp_discovery,
networkd_load_leases, networkd_load_leases,
@@ -334,6 +335,43 @@ class TestDHCPParseStaticRoutes(CiTestCase): )
@@ -388,11 +390,13 @@ class TestDHCPDiscoveryClean(CiTestCase):
self.logs.getvalue(),
) )
+ @mock.patch("cloudinit.temp_utils.get_tmp_ancestor", return_value="/tmp")
@mock.patch("cloudinit.net.dhcp.find_fallback_nic", return_value="eth9")
@mock.patch("cloudinit.net.dhcp.os.remove")
@mock.patch("cloudinit.net.dhcp.subp.subp")
@mock.patch("cloudinit.net.dhcp.subp.which")
- def test_dhcp_client_failover(self, m_which, m_subp, m_remove, m_fallback):
+ def test_dhcp_client_failover(self, m_which, m_subp, m_remove, m_fallback,
+ m_get_tmp_ancestor):
"""Log and do nothing when nic is absent and no fallback is found."""
m_subp.side_effect = [
("", ""),
@@ -928,3 +932,172 @@ class TestEphemeralDhcpLeaseErrors:
pass
assert len(m_dhcp.mock_calls) == 1
+
+
+class TestUDHCPCDiscoveryClean(CiTestCase): +class TestUDHCPCDiscoveryClean(CiTestCase):
+ with_logs = True
+ maxDiff = None + maxDiff = None
+ +
+ @mock.patch("cloudinit.temp_utils.get_tmp_ancestor", return_value="/tmp")
+ @mock.patch("cloudinit.net.dhcp.subp.which")
+ @mock.patch("cloudinit.net.dhcp.find_fallback_nic")
+ def test_absent_udhcpc_command(self, m_fallback, m_which,
+ m_get_tmp_ancestor):
+ """When dhclient doesn't exist in the OS, log the issue and no-op."""
+ m_fallback.return_value = "eth9"
+ m_which.return_value = None # udhcpc isn't found
+
+ distro = MockDistro()
+ distro.dhcp_client_priority = [Udhcpc]
+
+ with pytest.raises(NoDHCPLeaseMissingDhclientError):
+ maybe_perform_dhcp_discovery(distro)
+
+ self.assertIn(
+ "Skip udhcpc configuration: No udhcpc command found.",
+ self.logs.getvalue(),
+ )
+
+ @mock.patch("cloudinit.temp_utils.get_tmp_ancestor", return_value="/tmp")
+ @mock.patch("cloudinit.net.dhcp.is_ib_interface", return_value=False)
+ @mock.patch("cloudinit.net.dhcp.subp.which", return_value="/sbin/udhcpc")
+ @mock.patch("cloudinit.net.dhcp.os.remove") + @mock.patch("cloudinit.net.dhcp.os.remove")
+ @mock.patch("cloudinit.net.dhcp.subp.subp") + @mock.patch("cloudinit.net.dhcp.subp.subp")
+ @mock.patch("cloudinit.util.load_json") + @mock.patch("cloudinit.util.load_json")
+ @mock.patch("cloudinit.util.load_file") + @mock.patch("cloudinit.util.load_file")
+ @mock.patch("cloudinit.util.write_file") + @mock.patch("cloudinit.util.write_file")
+ def test_udhcpc_discovery( + def test_udhcpc_discovery(
+ self, m_write_file, m_load_file, m_loadjson, m_subp, m_remove + self,
+ m_write_file,
+ m_load_file,
+ m_loadjson,
+ m_subp,
+ m_remove,
+ m_which,
+ mocked_is_ib_interface,
+ m_get_tmp_ancestor,
+ ): + ):
+ """dhcp_discovery waits for the presence of pidfile and dhcp.leases.""" + """dhcp_discovery runs udcpc and parse the dhcp leases."""
+ m_subp.return_value = ("", "") + m_subp.return_value = ("", "")
+ m_loadjson.return_value = { + m_loadjson.return_value = {
+ "interface": "eth9", + "interface": "eth9",
@ -201,22 +302,120 @@ index 40340553..8913cf65 100644
+ "subnet-mask": "255.255.255.0", + "subnet-mask": "255.255.255.0",
+ } + }
+ ], + ],
+ dhcp_udhcpc_discovery("/sbin/udhcpc", "eth9"), + Udhcpc().dhcp_discovery("eth9", distro=MockDistro()),
+ )
+ # Interface was brought up before dhclient called
+ m_subp.assert_has_calls(
+ [
+ mock.call(
+ ["ip", "link", "set", "dev", "eth9", "up"],
+ capture=True,
+ ),
+ mock.call(
+ [
+ "/sbin/udhcpc",
+ "-O",
+ "staticroutes",
+ "-i",
+ "eth9",
+ "-s",
+ "/tmp/udhcpc_script",
+ "-n",
+ "-q",
+ "-f",
+ "-v",
+ ],
+ update_env={"LEASE_FILE": "/tmp/eth9.lease.json"},
+ capture=True,
+ ),
+ ]
+ ) + )
+ +
+ + @mock.patch("cloudinit.temp_utils.get_tmp_ancestor", return_value="/tmp")
class TestDHCPDiscoveryClean(CiTestCase): + @mock.patch("cloudinit.net.dhcp.is_ib_interface", return_value=True)
with_logs = True + @mock.patch("cloudinit.net.dhcp.get_ib_interface_hwaddr")
+ @mock.patch("cloudinit.net.dhcp.subp.which", return_value="/sbin/udhcpc")
@@ -372,7 +410,7 @@ class TestDHCPDiscoveryClean(CiTestCase): + @mock.patch("cloudinit.net.dhcp.os.remove")
maybe_perform_dhcp_discovery() + @mock.patch("cloudinit.net.dhcp.subp.subp")
+ @mock.patch("cloudinit.util.load_json")
self.assertIn( + @mock.patch("cloudinit.util.load_file")
- "Skip dhclient configuration: No dhclient command found.", + @mock.patch("cloudinit.util.write_file")
+ "Skip dhclient configuration: No dhclient or udhcpc command found.", + def test_udhcpc_discovery_ib(
self.logs.getvalue(), + self,
) + m_write_file,
+ m_load_file,
+ m_loadjson,
+ m_subp,
+ m_remove,
+ m_which,
+ m_get_ib_interface_hwaddr,
+ m_is_ib_interface,
+ m_get_tmp_ancestor,
+ ):
+ """dhcp_discovery runs udcpc and parse the dhcp leases."""
+ m_subp.return_value = ("", "")
+ m_loadjson.return_value = {
+ "interface": "ib0",
+ "fixed-address": "192.168.2.74",
+ "subnet-mask": "255.255.255.0",
+ "routers": "192.168.2.1",
+ "static_routes": "10.240.0.1/32 0.0.0.0 0.0.0.0/0 10.240.0.1",
+ }
+ m_get_ib_interface_hwaddr.return_value = "00:21:28:00:01:cf:4b:01"
+ self.assertEqual(
+ [
+ {
+ "fixed-address": "192.168.2.74",
+ "interface": "ib0",
+ "routers": "192.168.2.1",
+ "static_routes": [
+ ("10.240.0.1/32", "0.0.0.0"),
+ ("0.0.0.0/0", "10.240.0.1"),
+ ],
+ "subnet-mask": "255.255.255.0",
+ }
+ ],
+ Udhcpc().dhcp_discovery("ib0", distro=MockDistro()),
+ )
+ # Interface was brought up before dhclient called
+ m_subp.assert_has_calls(
+ [
+ mock.call(
+ ["ip", "link", "set", "dev", "ib0", "up"], capture=True
+ ),
+ mock.call(
+ [
+ "/sbin/udhcpc",
+ "-O",
+ "staticroutes",
+ "-i",
+ "ib0",
+ "-s",
+ "/tmp/udhcpc_script",
+ "-n",
+ "-q",
+ "-f",
+ "-v",
+ "-x",
+ "0x3d:0021280001cf4b01",
+ ],
+ update_env={"LEASE_FILE": "/tmp/ib0.lease.json"},
+ capture=True,
+ ),
+ ]
+ )
diff --git a/tools/.github-cla-signers b/tools/.github-cla-signers
index b4a9326e..4d82a055 100644
--- a/tools/.github-cla-signers
+++ b/tools/.github-cla-signers
@@ -65,6 +65,7 @@ jacobsalmela
jamesottinger
Jehops
jf
+jfroche
Jille
JohnKepplers
johnsonshi
-- --
2.38.4 2.40.1

View file

@ -1,110 +0,0 @@
From 6df2a198013ebed9aeff119ee0d15cb2d616474c Mon Sep 17 00:00:00 2001
From: zimbatm <zimbatm@zimbatm.com>
Date: Sun, 30 Apr 2023 12:13:54 +0200
Subject: [PATCH] vultr: remove check_route check
The heuristic is assuming that the URL will contain an IP, and that the
route explicitly lists that IP (eg: 0.0.0.0/0 should match but doesn't).
In order for the heuristic to be 100% reliable, it would have to
replicate exactly what the system is doing both in terms of DNS and
route resolution.
Because the HTTP request below is already exercising the python nd
system resolution, it is simpler to just remove this check and lean on
the HTTP request to provide the answer if the network is up or not.
---
cloudinit/sources/helpers/vultr.py | 22 ----------------------
tests/unittests/sources/test_vultr.py | 12 ------------
2 files changed, 34 deletions(-)
diff --git a/cloudinit/sources/helpers/vultr.py b/cloudinit/sources/helpers/vultr.py
index 71676bb1..aac2a610 100644
--- a/cloudinit/sources/helpers/vultr.py
+++ b/cloudinit/sources/helpers/vultr.py
@@ -32,10 +32,6 @@ def get_metadata(
iface=iface,
connectivity_url_data={"url": url},
):
- # Check for the metadata route, skip if not there
- if not check_route(url):
- continue
-
# Fetch the metadata
v1 = read_metadata(url, timeout, retries, sec_between, agent)
@@ -75,24 +71,6 @@ def get_interface_list():
return ifaces
-# Check for /32 route that our dhcp servers inject
-# in order to determine if this a customer-run dhcp server
-def check_route(url):
- # Get routes, confirm entry exists
- routes = netinfo.route_info()
-
- # If no tools exist and empty dict is returned
- if "ipv4" not in routes:
- return False
-
- # Parse each route into a more searchable format
- for route in routes["ipv4"]:
- if route.get("destination", None) in url:
- return True
-
- return False
-
-
# Read the system information from SMBIOS
def get_sysinfo():
return {
diff --git a/tests/unittests/sources/test_vultr.py b/tests/unittests/sources/test_vultr.py
index ba21ae24..7fa02b1c 100644
--- a/tests/unittests/sources/test_vultr.py
+++ b/tests/unittests/sources/test_vultr.py
@@ -274,14 +274,6 @@ INTERFACE_MAP = {
FINAL_INTERFACE_USED = ""
-# Static override, pylint doesnt like this in
-# classes without self
-def check_route(url):
- if FINAL_INTERFACE_USED == "eth0":
- return True
- return False
-
-
class TestDataSourceVultr(CiTestCase):
def setUp(self):
global VULTR_V1_3
@@ -431,7 +423,6 @@ class TestDataSourceVultr(CiTestCase):
@mock.patch(
"cloudinit.net.ephemeral.EphemeralDHCPv4.__exit__", override_exit
)
- @mock.patch("cloudinit.sources.helpers.vultr.check_route")
@mock.patch("cloudinit.sources.helpers.vultr.is_vultr")
@mock.patch("cloudinit.sources.helpers.vultr.read_metadata")
@mock.patch("cloudinit.sources.helpers.vultr.get_interface_list")
@@ -440,12 +431,10 @@ class TestDataSourceVultr(CiTestCase):
mock_interface_list,
mock_read_metadata,
mock_isvultr,
- mock_check_route,
):
mock_read_metadata.return_value = {}
mock_isvultr.return_value = True
mock_interface_list.return_value = FILTERED_INTERFACES
- mock_check_route.return_value = True
distro = mock.MagicMock()
distro.get_tmp_exec_path = self.tmp_dir
@@ -461,7 +450,6 @@ class TestDataSourceVultr(CiTestCase):
self.assertEqual(FINAL_INTERFACE_USED, INTERFACES[3])
# Test route checking sucessful DHCPs
- @mock.patch("cloudinit.sources.helpers.vultr.check_route", check_route)
@mock.patch(
"cloudinit.net.ephemeral.EphemeralDHCPv4.__init__",
ephemeral_init_always,
--
2.40.0

View file

@ -16,22 +16,20 @@
python3.pkgs.buildPythonApplication rec { python3.pkgs.buildPythonApplication rec {
pname = "cloud-init"; pname = "cloud-init";
version = "23.1.2"; version = "23.2";
namePrefix = ""; namePrefix = "";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "canonical"; owner = "canonical";
repo = "cloud-init"; repo = "cloud-init";
rev = "refs/tags/${version}"; rev = "refs/tags/${version}";
hash = "sha256-tn4flcrf04hVWhqkmK4qDenXcnV93pP+C+8J63b6FXQ="; hash = "sha256-/bhezXS5GunlgID7e/QaTC4UsQ2hDvS5HnG8Wphk64k=";
}; };
patches = [ patches = [
./0001-add-nixos-support.patch ./0001-add-nixos-support.patch
# upstream: https://github.com/canonical/cloud-init/pull/2125 # upstream: https://github.com/canonical/cloud-init/pull/4190
./0002-Add-Udhcpc-support.patch ./0002-Add-Udhcpc-support.patch
# upstream: https://github.com/canonical/cloud-init/pull/2151
./0003-vultr-remove-check_route-check.patch
]; ];
prePatch = '' prePatch = ''