diff options
Diffstat (limited to 'cloudinit/distros/networking.py')
-rw-r--r-- | cloudinit/distros/networking.py | 79 |
1 files changed, 77 insertions, 2 deletions
diff --git a/cloudinit/distros/networking.py b/cloudinit/distros/networking.py index e421a2ce..75e69df5 100644 --- a/cloudinit/distros/networking.py +++ b/cloudinit/distros/networking.py @@ -1,7 +1,11 @@ import abc +import logging import os -from cloudinit import net +from cloudinit import net, util + + +LOG = logging.getLogger(__name__) # Type aliases (https://docs.python.org/3/library/typing.html#type-aliases), @@ -102,10 +106,72 @@ class Networking(metaclass=abc.ABCMeta): def master_is_bridge_or_bond(self, devname: DeviceName) -> bool: return net.master_is_bridge_or_bond(devname) + @abc.abstractmethod + def settle(self, *, exists=None) -> None: + """Wait for device population in the system to complete. + + :param exists: + An optional optimisation. If given, only perform as much of the + settle process as is required for the given DeviceName to be + present in the system. (This may include skipping the settle + process entirely, if the device already exists.) + :type exists: Optional[DeviceName] + """ + pass + def wait_for_physdevs( self, netcfg: NetworkConfig, *, strict: bool = True ) -> None: - return net.wait_for_physdevs(netcfg, strict=strict) + """Wait for all the physical devices in `netcfg` to exist on the system + + Specifically, this will call `self.settle` 5 times, and check after + each one if the physical devices are now present in the system. + + :param netcfg: + The NetworkConfig from which to extract physical devices to wait + for. + :param strict: + Raise a `RuntimeError` if any physical devices are not present + after waiting. + """ + physdevs = self.extract_physdevs(netcfg) + + # set of expected iface names and mac addrs + expected_ifaces = dict([(iface[0], iface[1]) for iface in physdevs]) + expected_macs = set(expected_ifaces.keys()) + + # set of current macs + present_macs = self.get_interfaces_by_mac().keys() + + # compare the set of expected mac address values to + # the current macs present; we only check MAC as cloud-init + # has not yet renamed interfaces and the netcfg may include + # such renames. + for _ in range(0, 5): + if expected_macs.issubset(present_macs): + LOG.debug("net: all expected physical devices present") + return + + missing = expected_macs.difference(present_macs) + LOG.debug("net: waiting for expected net devices: %s", missing) + for mac in missing: + # trigger a settle, unless this interface exists + devname = expected_ifaces[mac] + msg = "Waiting for settle or {} exists".format(devname) + util.log_time( + LOG.debug, + msg, + func=self.settle, + kwargs={"exists": devname}, + ) + + # update present_macs after settles + present_macs = self.get_interfaces_by_mac().keys() + + msg = "Not all expected physical devices present: %s" % missing + LOG.warning(msg) + if strict: + raise RuntimeError(msg) class BSDNetworking(Networking): @@ -114,6 +180,10 @@ class BSDNetworking(Networking): def is_physical(self, devname: DeviceName) -> bool: raise NotImplementedError() + def settle(self, *, exists=None) -> None: + """BSD has no equivalent to `udevadm settle`; noop.""" + pass + class LinuxNetworking(Networking): """Implementation of networking functionality common to Linux distros.""" @@ -138,3 +208,8 @@ class LinuxNetworking(Networking): def is_physical(self, devname: DeviceName) -> bool: return os.path.exists(net.sys_dev_path(devname, "device")) + + def settle(self, *, exists=None) -> None: + if exists is not None: + exists = net.sys_dev_path(exists) + util.udevadm_settle(exists=exists) |