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:
parent
619d9c7bb7
commit
67f5018fe6
4 changed files with 337 additions and 244 deletions
|
@ -12,10 +12,10 @@ index b82852e1..c998b21e 100644
|
|||
LOG = logging.getLogger(__name__)
|
||||
diff --git a/cloudinit/distros/nixos.py b/cloudinit/distros/nixos.py
|
||||
new file mode 100644
|
||||
index 00000000..d53d2a61
|
||||
index 00000000..d53d2a62
|
||||
--- /dev/null
|
||||
+++ b/cloudinit/distros/nixos.py
|
||||
@@ -0,0 +1,103 @@
|
||||
@@ -0,0 +1,109 @@
|
||||
+# vi: ts=4 expandtab
|
||||
+#
|
||||
+# Copyright (C) 2012 Canonical Ltd.
|
||||
|
@ -47,6 +47,7 @@ index 00000000..d53d2a61
|
|||
+from cloudinit import atomic_helper
|
||||
+
|
||||
+from cloudinit.distros.parsers.hostname import HostnameConf
|
||||
+from cloudinit.net import dhcp
|
||||
+
|
||||
+LOG = logging.getLogger(__name__)
|
||||
+
|
||||
|
@ -61,6 +62,11 @@ index 00000000..d53d2a61
|
|||
+ self.usr_lib_exec = os.path.join(os.path.dirname(__file__),
|
||||
+ "../../../../../libexec")
|
||||
+ self.osfamily = 'nixos'
|
||||
+ self.dhcp_client_priority = [
|
||||
+ dhcp.Udhcpc,
|
||||
+ dhcp.IscDhclient,
|
||||
+ dhcp.Dhcpcd,
|
||||
+ ]
|
||||
+
|
||||
+ def _select_hostname(self, hostname, fqdn):
|
||||
+ # Prefer the short hostname over the long
|
||||
|
@ -112,10 +118,10 @@ index 00000000..d53d2a61
|
|||
+ raise NotImplementedError()
|
||||
+
|
||||
+ def package_command(self, command, args=None, pkgs=None):
|
||||
+ raise NotImplementedError()
|
||||
+ pass
|
||||
+
|
||||
+ def set_timezone(self, tz):
|
||||
+ raise NotImplementedError()
|
||||
+ pass
|
||||
+
|
||||
+ def update_package_sources(self):
|
||||
+ raise NotImplementedError()
|
||||
+ pass
|
||||
|
|
|
@ -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
|
||||
index a9a1c980..2d83089b 100644
|
||||
index 6c8c2f54..f5586cea 100644
|
||||
--- a/cloudinit/net/dhcp.py
|
||||
+++ b/cloudinit/net/dhcp.py
|
||||
@@ -14,12 +14,48 @@ from io import StringIO
|
||||
|
||||
import configobj
|
||||
|
||||
-from cloudinit import subp, util
|
||||
+from cloudinit import subp, util, temp_utils
|
||||
from cloudinit.net import find_fallback_nic, get_devicelist
|
||||
|
||||
@@ -21,6 +21,7 @@ from cloudinit import subp, temp_utils, util
|
||||
from cloudinit.net import (
|
||||
find_fallback_nic,
|
||||
get_devicelist,
|
||||
+ get_ib_interface_hwaddr,
|
||||
get_interface_mac,
|
||||
is_ib_interface,
|
||||
)
|
||||
@@ -28,6 +29,37 @@ from cloudinit.net import (
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
NETWORKD_LEASES_DIR = "/run/systemd/netif/leases"
|
||||
|
@ -17,9 +58,7 @@ index a9a1c980..2d83089b 100644
|
|||
+log() {
|
||||
+ echo "udhcpc[$PPID]" "$interface: $2"
|
||||
+}
|
||||
+
|
||||
+[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1
|
||||
+
|
||||
+case $1 in
|
||||
+ bound|renew)
|
||||
+ cat <<JSON > "$LEASE_FILE"
|
||||
|
@ -32,17 +71,14 @@ index a9a1c980..2d83089b 100644
|
|||
+}
|
||||
+JSON
|
||||
+ ;;
|
||||
+
|
||||
+ deconfig)
|
||||
+ log err "Not supported"
|
||||
+ exit 1
|
||||
+ ;;
|
||||
+
|
||||
+ leasefail | nak)
|
||||
+ log err "configuration failed: $1: $message"
|
||||
+ exit 1
|
||||
+ ;;
|
||||
+
|
||||
+ *)
|
||||
+ echo "$0: Unknown udhcpc command: $1" >&2
|
||||
+ exit 1
|
||||
|
@ -52,134 +88,199 @@ index a9a1c980..2d83089b 100644
|
|||
|
||||
|
||||
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):
|
||||
- """Perform dhcp discovery if nic valid and dhclient command exists.
|
||||
+ """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.
|
||||
+class NoDHCPLeaseMissingUdhcpcError(NoDHCPLeaseError):
|
||||
+ """Raised when unable to find udhcpc client."""
|
||||
+
|
||||
+ @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
|
||||
+ dhclient.lease file or empty list.
|
||||
+ """
|
||||
+ LOG.debug("Performing a dhcp discovery on %s", interface)
|
||||
def select_dhcp_client(distro):
|
||||
"""distros set priority list, select based on this order which to use
|
||||
|
||||
@@ -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.
|
||||
+ # Generally dhclient relies on dhclient-script PREINIT action to bring the
|
||||
+ # link up before attempting discovery. Since we are using -sf /bin/true,
|
||||
+ # we need to do that "link up" ourselves first.
|
||||
+ 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 = [
|
||||
+ udhcpc_cmd_path,
|
||||
+ "-O",
|
||||
+ "staticroutes",
|
||||
+ "-i",
|
||||
+class Udhcpc(DhcpClient):
|
||||
+ client_name = "udhcpc"
|
||||
+
|
||||
+ def __init__(self):
|
||||
+ self.udhcpc_path = subp.which("udhcpc")
|
||||
+ if not self.udhcpc_path:
|
||||
+ LOG.debug("Skip udhcpc configuration: No udhcpc command found.")
|
||||
+ raise NoDHCPLeaseMissingUdhcpcError()
|
||||
+
|
||||
+ def dhcp_discovery(
|
||||
+ self,
|
||||
+ interface,
|
||||
+ "-s",
|
||||
+ udhcpc_script,
|
||||
+ "-n", # Exit if lease is not obtained
|
||||
+ "-q", # Exit after obtaining lease
|
||||
+ "-f", # Run in foreground
|
||||
+ "-v",
|
||||
+ ]
|
||||
+ dhcp_log_func=None,
|
||||
+ distro=None,
|
||||
+ ):
|
||||
+ """Run udhcpc on the interface without scripts or filesystem artifacts.
|
||||
+
|
||||
+ out, err = subp.subp(
|
||||
+ cmd, update_env={"LEASE_FILE": lease_file}, capture=True
|
||||
+ )
|
||||
+ @param interface: Name of the network interface on which to run udhcpc.
|
||||
+ @param dhcp_log_func: A callable accepting the udhcpc output and
|
||||
+ error streams.
|
||||
+
|
||||
+ if dhcp_log_func is not None:
|
||||
+ 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: A list of dicts of representing the dhcp leases parsed from
|
||||
+ the udhcpc lease file.
|
||||
+ """
|
||||
+ LOG.debug("Performing a dhcp discovery on %s", interface)
|
||||
+
|
||||
+ 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
|
||||
+ 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):
|
||||
"""Run dhclient on the interface without scripts or filesystem artifacts.
|
||||
|
||||
+ if dhcp_log_func is not None:
|
||||
+ 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
|
||||
index 40340553..8913cf65 100644
|
||||
index 55d4c6e9..9123cd15 100644
|
||||
--- a/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,
|
||||
NoDHCPLeaseInterfaceError,
|
||||
NoDHCPLeaseMissingDhclientError,
|
||||
+ dhcp_udhcpc_discovery,
|
||||
dhcp_discovery,
|
||||
+ NoDHCPLeaseMissingUdhcpcError,
|
||||
+ Udhcpc,
|
||||
maybe_perform_dhcp_discovery,
|
||||
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):
|
||||
+ with_logs = True
|
||||
+ 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.subp.subp")
|
||||
+ @mock.patch("cloudinit.util.load_json")
|
||||
+ @mock.patch("cloudinit.util.load_file")
|
||||
+ @mock.patch("cloudinit.util.write_file")
|
||||
+ 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_loadjson.return_value = {
|
||||
+ "interface": "eth9",
|
||||
|
@ -201,22 +302,120 @@ index 40340553..8913cf65 100644
|
|||
+ "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,
|
||||
+ ),
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+
|
||||
class TestDHCPDiscoveryClean(CiTestCase):
|
||||
with_logs = True
|
||||
|
||||
@@ -372,7 +410,7 @@ class TestDHCPDiscoveryClean(CiTestCase):
|
||||
maybe_perform_dhcp_discovery()
|
||||
|
||||
self.assertIn(
|
||||
- "Skip dhclient configuration: No dhclient command found.",
|
||||
+ "Skip dhclient configuration: No dhclient or 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=True)
|
||||
+ @mock.patch("cloudinit.net.dhcp.get_ib_interface_hwaddr")
|
||||
+ @mock.patch("cloudinit.net.dhcp.subp.which", return_value="/sbin/udhcpc")
|
||||
+ @mock.patch("cloudinit.net.dhcp.os.remove")
|
||||
+ @mock.patch("cloudinit.net.dhcp.subp.subp")
|
||||
+ @mock.patch("cloudinit.util.load_json")
|
||||
+ @mock.patch("cloudinit.util.load_file")
|
||||
+ @mock.patch("cloudinit.util.write_file")
|
||||
+ def test_udhcpc_discovery_ib(
|
||||
+ 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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -16,22 +16,20 @@
|
|||
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "cloud-init";
|
||||
version = "23.1.2";
|
||||
version = "23.2";
|
||||
namePrefix = "";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "canonical";
|
||||
repo = "cloud-init";
|
||||
rev = "refs/tags/${version}";
|
||||
hash = "sha256-tn4flcrf04hVWhqkmK4qDenXcnV93pP+C+8J63b6FXQ=";
|
||||
hash = "sha256-/bhezXS5GunlgID7e/QaTC4UsQ2hDvS5HnG8Wphk64k=";
|
||||
};
|
||||
|
||||
patches = [
|
||||
./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
|
||||
# upstream: https://github.com/canonical/cloud-init/pull/2151
|
||||
./0003-vultr-remove-check_route-check.patch
|
||||
];
|
||||
|
||||
prePatch = ''
|
||||
|
|
Loading…
Reference in a new issue