From 7c6d07fc2d5f6b72a1818508ba8c3d6eee1bcbc8 Mon Sep 17 00:00:00 2001 From: Brett Holman Date: Tue, 24 Oct 2023 09:31:09 -0600 Subject: [PATCH] ephemeral: Handle link up failure for both ipv4 and ipv6 EphemeralIPv{4,6} failure is not always an error, therefore do not log this event as an error in the context manager. Allow callsites to determine log level. Fixes GH-4540 --- cloudinit/net/ephemeral.py | 72 ++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/cloudinit/net/ephemeral.py b/cloudinit/net/ephemeral.py index 3b5f9b96a2ef..de21536eb7e6 100644 --- a/cloudinit/net/ephemeral.py +++ b/cloudinit/net/ephemeral.py @@ -107,10 +107,6 @@ def __enter__(self): elif self.router: self._bringup_router() except subp.ProcessExecutionError: - LOG.error( - "Error bringing up EphemeralIPv4Network. " - "Datasource setup cannot continue" - ) self.__exit__(None, None, None) raise @@ -274,11 +270,9 @@ def __exit__(self, excp_type, excp_value, excp_traceback): def clean_network(self): """Exit _ephipv4 context to teardown of ip configuration performed.""" - if self.lease: - self.lease = None - if not self._ephipv4: - return - self._ephipv4.__exit__(None, None, None) + self.lease = None + if self._ephipv4: + self._ephipv4.__exit__(None, None, None) def obtain_lease(self): """Perform dhcp discovery in a sandboxed environment if possible. @@ -350,7 +344,13 @@ def get_first_option_value( class EphemeralIPNetwork: - """Marries together IPv4 and IPv6 ephemeral context managers""" + """Combined ephemeral context manager for IPv4 and IPv6 + + Either ipv4 or ipv6 ephemeral network may fail to initialize, but if either + succeeds, then this context manager will not raise exception. This allows + either ipv4 or ipv6 ephemeral network to succeed, but requires that error + handling for networks unavailable be done within the context. + """ def __init__( self, @@ -367,21 +367,55 @@ def __init__( self.distro = distro def __enter__(self): - # ipv6 dualstack might succeed when dhcp4 fails - # therefore catch exception unless only v4 is used + if not (self.ipv4 or self.ipv6): + # no ephemeral network requested, but this object still needs to + # function as a context manager + return self try: + exceptions = [] + ephemeral_obtained = False if self.ipv4: - self.stack.enter_context( - EphemeralDHCPv4(self.distro, self.interface) - ) + try: + self.stack.enter_context( + EphemeralDHCPv4( + self.distro, + self.interface, + ) + ) + ephemeral_obtained = True + except subp.ProcessExecutionError as e: + LOG.info( + "Failed to bring up EphemeralIPv4Network." + ) + exceptions.append(e) + if self.ipv6: - self.stack.enter_context( - EphemeralIPv6Network(self.distro, self.interface) + try: + self.stack.enter_context( + EphemeralIPv6Network( + self.distro, + self.interface, + ) + ) + ephemeral_obtained = True + except subp.ProcessExecutionError as e: + LOG.info( + "Failed to bring up EphemeralIPv6Network." + ) + exceptions.append(e) + + if not ephemeral_obtained: + # Ephemeral network setup failed in linkup for both ipv4 and + # ipv6. Raise only the first exception found. + LOG.error( + "Failed to bring up EphemeralIPNetwork. " + "Datasource setup cannot continue" ) - # v6 link local might be usable - # caller may want to log network state + raise exceptions[0] except NoDHCPLeaseError as e: if self.ipv6: + # ipv6 dualstack might succeed when dhcp4 fails, so catch + # NoDHCPLeaseError unless only v4 is used self.state_msg = "using link-local ipv6" else: raise e