summaryrefslogtreecommitdiff
path: root/cloudinit/distros/networking.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/distros/networking.py')
-rw-r--r--cloudinit/distros/networking.py79
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)