summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit')
-rw-r--r--cloudinit/distros/__init__.py12
-rw-r--r--cloudinit/distros/debian.py9
-rw-r--r--cloudinit/net/__init__.py83
-rw-r--r--cloudinit/stages.py11
-rw-r--r--cloudinit/util.py9
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)