From 2cbbea39ea1da7f65cff4bc6688e736518b898e4 Mon Sep 17 00:00:00 2001 From: a-dubs Date: Mon, 21 Oct 2024 09:51:56 -0400 Subject: [PATCH] tried adding _perform_connectivity_check to ephemeral ip --- cloudinit/net/ephemeral.py | 75 +++++++++++++++++---------- cloudinit/sources/DataSourceOracle.py | 36 ++++++------- 2 files changed, 67 insertions(+), 44 deletions(-) diff --git a/cloudinit/net/ephemeral.py b/cloudinit/net/ephemeral.py index a996a825c99..d30fed2cd8d 100644 --- a/cloudinit/net/ephemeral.py +++ b/cloudinit/net/ephemeral.py @@ -3,6 +3,7 @@ """Module for ephemeral network context managers """ import contextlib +import json import logging from functools import partial from typing import Any, Callable, Dict, List, Optional, Tuple @@ -11,6 +12,7 @@ import cloudinit.netinfo as netinfo from cloudinit.net.dhcp import NoDHCPLeaseError, maybe_perform_dhcp_discovery from cloudinit.subp import ProcessExecutionError +from cloudinit.url_helper import UrlError, wait_for_url LOG = logging.getLogger(__name__) @@ -429,12 +431,9 @@ def __init__( self.state_msg: str = "" self.distro = distro self.connectivity_urls_data = connectivity_urls_data - self.ipv6_connectivity_check_callback = ( - ipv6_connectivity_check_callback - ) # will be updated by the context manager - self.ipv6_reached_at_url = None + self.imds_reached_at_url: Optional[str] = None def __enter__(self): if not (self.ipv4 or self.ipv6): @@ -444,27 +443,12 @@ def __enter__(self): exceptions = [] ephemeral_obtained = False - if self.ipv6_connectivity_check_callback is not None: - if not self.ipv6: - raise ValueError( - "ipv6_connectivity_check_callback provided but ipv6 is " - "not enabled" - ) - ephemeral_obtained, exceptions = self._do_ipv6( - ephemeral_obtained, exceptions - ) - self.ipv6_reached_at_url = self.ipv6_connectivity_check_callback() - # if ipv6_connectivity_check_callback is provided, then we want to - # skip ipv4 ephemeral network setup if ipv6 ephemeral network setup - # and imds connectivity check succeeded - if self.ipv4 and not self.ipv6_reached_at_url: - LOG.debug( - "Bringing up ipv4 ephemeral network since ipv6 failed" - ) - ephemeral_obtained, exceptions = self._do_ipv4( - ephemeral_obtained, exceptions - ) + self.imds_reached_at_url = self._perform_connectivity_check() + + if self.imds_reached_at_url: + LOG.debug("We already have connectivity to IMDS, skipping DHCP.") else: + LOG.debug("No connectivity to IMDS, attempting DHCP setup.") if self.ipv4: ephemeral_obtained, exceptions = self._do_ipv4( ephemeral_obtained, exceptions @@ -474,7 +458,7 @@ def __enter__(self): ephemeral_obtained, exceptions ) - if not ephemeral_obtained: + if not self.imds_reached_at_url and not ephemeral_obtained: # Ephemeral network setup failed in linkup for both ipv4 and # ipv6. Raise only the first exception found. LOG.error( @@ -504,7 +488,6 @@ def _do_ipv4( EphemeralDHCPv4( distro=self.distro, iface=self.interface, - connectivity_urls_data=self.connectivity_urls_data, ) ) ephemeral_obtained = True @@ -561,5 +544,45 @@ def _do_ipv6( exceptions.append(e) return ephemeral_obtained, exceptions + def _perform_connectivity_check( + self, + ) -> Optional[str]: + + def headers_cb(url): + headers = [ + url_data.get("headers") for url_data in self.connectivity_urls_data + if url_data["url"] == url + ][0] + return headers + + try: + url_that_worked, url_response = wait_for_url( + urls=[url_data["url"] for url_data in self.connectivity_urls_data], + headers_cb=headers_cb, + timeout=0.5, # keep really short for quick failure path + connect_synchronously=False, + ) + imds_data = json.loads(url_response.decode("utf-8")) + except UrlError as e: + LOG.debug( + "Failed to reach IMDS with error: %s", + e, + ) + except Exception as e: # pylint: disable=broad-except + LOG.debug( + "Unexpected error occurred. Failed to reach IMDS: %s", + e, + ) + else: + if imds_data: + LOG.debug( + "IMDS was successfully reached at %s without ephemeral " + "network setup.", + url_that_worked, + ) + return url_that_worked + LOG.debug("Failed to reach IMDS without ephemeral network setup.") + return None + def __exit__(self, *_args): self.stack.close() diff --git a/cloudinit/sources/DataSourceOracle.py b/cloudinit/sources/DataSourceOracle.py index 520655bc5bb..fff28932c3a 100644 --- a/cloudinit/sources/DataSourceOracle.py +++ b/cloudinit/sources/DataSourceOracle.py @@ -42,14 +42,6 @@ IPV6_METADATA_ROOT = "http://[fd00:c1::a9fe:a9fe]/opc/v{version}/" IPV4_METADATA_PATTERN = IPV4_METADATA_ROOT + "{path}/" IPV6_METADATA_PATTERN = IPV6_METADATA_ROOT + "{path}/" -METADATA_URLS = [ - IPV4_METADATA_ROOT, - IPV6_METADATA_ROOT, -] -METADATA_ROOTS = [ - IPV4_METADATA_ROOT, - IPV6_METADATA_ROOT, -] # https://docs.cloud.oracle.com/iaas/Content/Network/Troubleshoot/connectionhang.htm#Overview, # indicates that an MTU of 9000 is used within OCI @@ -196,23 +188,27 @@ def _get_data(self): version=1, path="instance" ), }, + { + "url": IPV6_METADATA_PATTERN.format( + version=2, path="instance" + ), + "headers": V2_HEADERS, + }, + { + "url": IPV6_METADATA_PATTERN.format( + version=1, path="instance" + ), + }, ] - ipv6_url_that_worked = check_ipv6_connectivity() - if ipv6_url_that_worked: - md_patterns = [IPV6_METADATA_PATTERN] - else: - md_patterns = [IPV4_METADATA_PATTERN] - - # if we have connectivity to imds, then skip ephemeral network setup - if self.perform_dhcp_setup and not ipv6_url_that_worked: + if self.perform_dhcp_setup: nic_name = net.find_fallback_nic() try: network_context = ephemeral.EphemeralIPNetwork( distro=self.distro, interface=nic_name, - ipv6=False, + ipv6=True, ipv4=True, connectivity_urls_data=connectivity_urls_data, ipv6_connectivity_check_callback=None, @@ -232,7 +228,10 @@ def _get_data(self): fetch_vnics_data=fetch_primary_nic or fetch_secondary_nics, max_wait=self.url_max_wait, timeout=self.url_timeout, - metadata_patterns=md_patterns, + metadata_patterns=[ + IPV6_METADATA_PATTERN, + IPV4_METADATA_PATTERN, + ], ) # set the metadata root address that worked to allow for detecting # whether ipv4 or ipv6 was used for getting metadata @@ -557,6 +556,7 @@ def read_opc_metadata( timeout=timeout, headers_cb=_headers_cb, sleep_time=0.1, + connect_synchronously=False, ) if vnics_url: vnics_data = json.loads(vnics_response.decode("utf-8"))