diff options
Diffstat (limited to 'cloudinit')
-rw-r--r-- | cloudinit/distros/__init__.py | 12 | ||||
-rw-r--r-- | cloudinit/distros/debian.py | 9 | ||||
-rw-r--r-- | cloudinit/net/__init__.py | 83 | ||||
-rw-r--r-- | cloudinit/stages.py | 11 | ||||
-rw-r--r-- | cloudinit/util.py | 9 |
5 files changed, 124 insertions, 0 deletions
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 74b484a7..e32ddd57 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -78,6 +78,10 @@ class Distro(object): def _write_network_config(self, settings): raise NotImplementedError() + @abc.abstractmethod + def _write_network_fallback(self): + raise NotImplementedError() + def _find_tz_file(self, tz): tz_file = os.path.join(self.tz_zone_dir, str(tz)) if not os.path.isfile(tz_file): @@ -143,6 +147,14 @@ class Distro(object): return self._bring_up_interfaces(dev_names) return False + def apply_fallback_network(self, bring_up=True): + # Write it out + dev_names = self._write_network_fallback() + # Now try to bring them up + if bring_up: + return self._bring_up_interfaces(dev_names) + return False + @abc.abstractmethod def apply_locale(self, locale, out_fn=None): raise NotImplementedError() diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py index 909d6deb..de8c4c6c 100644 --- a/cloudinit/distros/debian.py +++ b/cloudinit/distros/debian.py @@ -82,6 +82,15 @@ class Distro(distros.Distro): net.render_network_state(network_state=ns, target="/") return [] + def _write_network_fallback(self): + # old fallback configuration is obsolete, disable it + util.disable_conf_file('/etc/network/interfaces.d/eth0.cfg') + nconf = net.generate_fallback_config() + if nconf is not None: + ns = nconf['config'] + net.render_network_state(network_state=ns, target="/") + return [] + def _bring_up_interfaces(self, device_names): use_all = False for d in device_names: diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py index 3cf99604..36f07a02 100644 --- a/cloudinit/net/__init__.py +++ b/cloudinit/net/__init__.py @@ -20,6 +20,7 @@ import errno import glob import os import re +import string from cloudinit import log as logging from cloudinit import util @@ -46,6 +47,8 @@ NET_CONFIG_BRIDGE_OPTIONS = [ "bridge_hello", "bridge_maxage", "bridge_maxwait", "bridge_stp", ] +DEFAULT_PRIMARY_INTERFACE = 'eth0' + def sys_dev_path(devname, path=""): return SYS_CLASS_NET + devname + "/" + path @@ -280,6 +283,86 @@ def parse_net_config(path): return ns +def generate_fallback_config(): + """Determine which attached net dev is most likely to have a connection and + generate network state to run dhcp on that interface""" + # by default use eth0 as primary interface + nconf = {'config': {'interfaces': {}, + 'dns': {'search': [], 'nameservers': []}, 'routes': [] + }, + 'version': 1 + } + + # get list of interfaces that could have connections + invalid_interfaces = set(['lo']) + potential_interfaces = set(os.listdir(SYS_CLASS_NET)) + potential_interfaces = potential_interfaces.difference(invalid_interfaces) + # sort into interfaces with carrier, interfaces which could have carrier, + # and ignore interfaces that are definitely disconnected + connected = [] + possibly_connected = [] + for interface in potential_interfaces: + try: + sysfs_carrier = os.path.join(SYS_CLASS_NET, interface, 'carrier') + carrier = int(util.load_file(sysfs_carrier).strip()) + if carrier: + connected.append(interface) + continue + except OSError: + pass + # check if nic is dormant or down, as this may make a nick appear to + # not have a carrier even though it could acquire one when brought + # online by dhclient + try: + sysfs_dormant = os.path.join(SYS_CLASS_NET, interface, 'dormant') + dormant = int(util.load_file(sysfs_dormant).strip()) + if dormant: + possibly_connected.append(interface) + continue + except OSError: + pass + try: + sysfs_operstate = os.path.join(SYS_CLASS_NET, interface, + 'operstate') + operstate = util.load_file(sysfs_operstate).strip() + if operstate in ['dormant', 'down', 'lowerlayerdown', 'unknown']: + possibly_connected.append(interface) + continue + except OSError: + pass + + # don't bother with interfaces that might not be connected if there are + # some that definitely are + if connected: + potential_interfaces = connected + else: + potential_interfaces = possibly_connected + # if there are no interfaces, give up + if not potential_interfaces: + return + # if eth0 exists use it above anything else, otherwise get the interface + # that looks 'first' + if DEFAULT_PRIMARY_INTERFACE in potential_interfaces: + name = DEFAULT_PRIMARY_INTERFACE + else: + potential_interfaces.sort( + key=lambda x: int(''.join(i for i in x if i in string.digits))) + name = potential_interfaces[0] + + sysfs_mac = os.path.join(SYS_CLASS_NET, name, 'address') + mac = util.load_file(sysfs_mac).strip() + target_name = name + + # generate net config for interface + nconf['config']['interfaces'][target_name] = { + 'mac_address': mac, 'name': target_name, 'type': 'physical', + 'mode': 'manual', 'inet': 'inet', + 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}] + } + + return nconf + + def render_persistent_net(network_state): ''' Given state, emit udev rules to map mac to ifname diff --git a/cloudinit/stages.py b/cloudinit/stages.py index c230ec0d..64da3b5b 100644 --- a/cloudinit/stages.py +++ b/cloudinit/stages.py @@ -567,6 +567,17 @@ class Init(object): # Run the handlers self._do_handlers(user_data_msg, c_handlers_list, frequency) + def apply_networking(self): + """Attempt to apply network configuration, either using network + configuration from datasource or fallback configuration if that is + not available""" + if self.datasource and self.datasource.network_config: + ds_net_conf = self.datasource.network_config + res = self.distro.apply_network_config(ds_net_conf, bring_up=True) + else: + res = self.distro.apply_fallback_network(bring_up=True) + return res + class Modules(object): def __init__(self, init, cfg_files=None, reporter=None): diff --git a/cloudinit/util.py b/cloudinit/util.py index 20916e53..58ab3c75 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -849,6 +849,15 @@ def read_seeded(base="", ext="", timeout=5, retries=10, file_retries=0): return (md, ud) +def disable_conf_file(conf): + # disable .cfg file by renaming it if it exists + if not os.path.exists(conf): + return None + target_path = conf + '.disabled' + rename(conf, target_path) + return target_path + + def read_conf_d(confd): # Get reverse sorted list (later trumps newer) confs = sorted(os.listdir(confd), reverse=True) |