summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit')
-rw-r--r--cloudinit/sources/__init__.py16
-rw-r--r--cloudinit/stages.py37
-rw-r--r--cloudinit/tests/test_stages.py71
3 files changed, 105 insertions, 19 deletions
diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py
index e6966b31..9d249366 100644
--- a/cloudinit/sources/__init__.py
+++ b/cloudinit/sources/__init__.py
@@ -66,6 +66,13 @@ CLOUD_ID_REGION_PREFIX_MAP = {
'china': ('azure-china', lambda c: c == 'azure'), # only change azure
}
+# NetworkConfigSource represents the canonical list of network config sources
+# that cloud-init knows about. (Python 2.7 lacks PEP 435, so use a singleton
+# namedtuple as an enum; see https://stackoverflow.com/a/6971002)
+_NETCFG_SOURCE_NAMES = ('cmdline', 'ds', 'system_cfg', 'fallback')
+NetworkConfigSource = namedtuple('NetworkConfigSource',
+ _NETCFG_SOURCE_NAMES)(*_NETCFG_SOURCE_NAMES)
+
class DataSourceNotFoundException(Exception):
pass
@@ -153,6 +160,15 @@ class DataSource(object):
# Track the discovered fallback nic for use in configuration generation.
_fallback_interface = None
+ # The network configuration sources that should be considered for this data
+ # source. (The first source in this list that provides network
+ # configuration will be used without considering any that follow.) This
+ # should always be a subset of the members of NetworkConfigSource with no
+ # duplicate entries.
+ network_config_sources = (NetworkConfigSource.cmdline,
+ NetworkConfigSource.system_cfg,
+ NetworkConfigSource.ds)
+
# read_url_params
url_max_wait = -1 # max_wait < 0 means do not wait
url_timeout = 10 # timeout for each metadata url read attempt
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 5f9d47b9..6bcda2d1 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -24,6 +24,7 @@ from cloudinit.handlers.shell_script import ShellScriptPartHandler
from cloudinit.handlers.upstart_job import UpstartJobPartHandler
from cloudinit.event import EventType
+from cloudinit.sources import NetworkConfigSource
from cloudinit import cloud
from cloudinit import config
@@ -630,19 +631,37 @@ class Init(object):
if os.path.exists(disable_file):
return (None, disable_file)
- cmdline_cfg = ('cmdline', cmdline.read_kernel_cmdline_config())
- dscfg = ('ds', None)
+ available_cfgs = {
+ NetworkConfigSource.cmdline: cmdline.read_kernel_cmdline_config(),
+ NetworkConfigSource.ds: None,
+ NetworkConfigSource.system_cfg: self.cfg.get('network'),
+ }
+
if self.datasource and hasattr(self.datasource, 'network_config'):
- dscfg = ('ds', self.datasource.network_config)
- sys_cfg = ('system_cfg', self.cfg.get('network'))
+ available_cfgs[NetworkConfigSource.ds] = (
+ self.datasource.network_config)
- for loc, ncfg in (cmdline_cfg, sys_cfg, dscfg):
+ if self.datasource:
+ order = self.datasource.network_config_sources
+ else:
+ order = sources.DataSource.network_config_sources
+ for cfg_source in order:
+ if not hasattr(NetworkConfigSource, cfg_source):
+ LOG.warning('data source specifies an invalid network'
+ ' cfg_source: %s', cfg_source)
+ continue
+ if cfg_source not in available_cfgs:
+ LOG.warning('data source specifies an unavailable network'
+ ' cfg_source: %s', cfg_source)
+ continue
+ ncfg = available_cfgs[cfg_source]
if net.is_disabled_cfg(ncfg):
- LOG.debug("network config disabled by %s", loc)
- return (None, loc)
+ LOG.debug("network config disabled by %s", cfg_source)
+ return (None, cfg_source)
if ncfg:
- return (ncfg, loc)
- return (self.distro.generate_fallback_config(), "fallback")
+ return (ncfg, cfg_source)
+ return (self.distro.generate_fallback_config(),
+ NetworkConfigSource.fallback)
def _apply_netcfg_names(self, netcfg):
try:
diff --git a/cloudinit/tests/test_stages.py b/cloudinit/tests/test_stages.py
index 9b483121..7e13e29d 100644
--- a/cloudinit/tests/test_stages.py
+++ b/cloudinit/tests/test_stages.py
@@ -6,6 +6,7 @@ import os
from cloudinit import stages
from cloudinit import sources
+from cloudinit.sources import NetworkConfigSource
from cloudinit.event import EventType
from cloudinit.util import write_file
@@ -63,7 +64,7 @@ class TestInit(CiTestCase):
"""find_networking_config returns when disabled by kernel cmdline."""
m_cmdline.return_value = {'config': 'disabled'}
self.assertEqual(
- (None, 'cmdline'),
+ (None, NetworkConfigSource.cmdline),
self.init._find_networking_config())
self.assertEqual('DEBUG: network config disabled by cmdline\n',
self.logs.getvalue())
@@ -78,7 +79,7 @@ class TestInit(CiTestCase):
self.init.datasource = FakeDataSource(
network_config={'config': 'disabled'})
self.assertEqual(
- (None, 'ds'),
+ (None, NetworkConfigSource.ds),
self.init._find_networking_config())
self.assertEqual('DEBUG: network config disabled by ds\n',
self.logs.getvalue())
@@ -90,12 +91,62 @@ class TestInit(CiTestCase):
self.init._cfg = {'system_info': {'paths': {'cloud_dir': self.tmpdir}},
'network': {'config': 'disabled'}}
self.assertEqual(
- (None, 'system_cfg'),
+ (None, NetworkConfigSource.system_cfg),
self.init._find_networking_config())
self.assertEqual('DEBUG: network config disabled by system_cfg\n',
self.logs.getvalue())
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test__find_networking_config_uses_datasrc_order(self, m_cmdline):
+ """find_networking_config should check sources in DS defined order"""
+ # cmdline, which would normally be preferred over other sources,
+ # disables networking; in this case, though, the DS moves cmdline later
+ # so its own config is preferred
+ m_cmdline.return_value = {'config': 'disabled'}
+
+ ds_net_cfg = {'config': {'needle': True}}
+ self.init.datasource = FakeDataSource(network_config=ds_net_cfg)
+ self.init.datasource.network_config_sources = [
+ NetworkConfigSource.ds, NetworkConfigSource.system_cfg,
+ NetworkConfigSource.cmdline]
+
+ self.assertEqual(
+ (ds_net_cfg, NetworkConfigSource.ds),
+ self.init._find_networking_config())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test__find_networking_config_warns_if_datasrc_uses_invalid_src(
+ self, m_cmdline):
+ """find_networking_config should check sources in DS defined order"""
+ ds_net_cfg = {'config': {'needle': True}}
+ self.init.datasource = FakeDataSource(network_config=ds_net_cfg)
+ self.init.datasource.network_config_sources = [
+ 'invalid_src', NetworkConfigSource.ds]
+
+ self.assertEqual(
+ (ds_net_cfg, NetworkConfigSource.ds),
+ self.init._find_networking_config())
+ self.assertIn('WARNING: data source specifies an invalid network'
+ ' cfg_source: invalid_src',
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test__find_networking_config_warns_if_datasrc_uses_unavailable_src(
+ self, m_cmdline):
+ """find_networking_config should check sources in DS defined order"""
+ ds_net_cfg = {'config': {'needle': True}}
+ self.init.datasource = FakeDataSource(network_config=ds_net_cfg)
+ self.init.datasource.network_config_sources = [
+ NetworkConfigSource.fallback, NetworkConfigSource.ds]
+
+ self.assertEqual(
+ (ds_net_cfg, NetworkConfigSource.ds),
+ self.init._find_networking_config())
+ self.assertIn('WARNING: data source specifies an unavailable network'
+ ' cfg_source: fallback',
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
def test_wb__find_networking_config_returns_kernel(self, m_cmdline):
"""find_networking_config returns kernel cmdline config if present."""
expected_cfg = {'config': ['fakekernel']}
@@ -105,7 +156,7 @@ class TestInit(CiTestCase):
self.init.datasource = FakeDataSource(
network_config={'config': ['fakedatasource']})
self.assertEqual(
- (expected_cfg, 'cmdline'),
+ (expected_cfg, NetworkConfigSource.cmdline),
self.init._find_networking_config())
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
@@ -118,7 +169,7 @@ class TestInit(CiTestCase):
self.init.datasource = FakeDataSource(
network_config={'config': ['fakedatasource']})
self.assertEqual(
- (expected_cfg, 'system_cfg'),
+ (expected_cfg, NetworkConfigSource.system_cfg),
self.init._find_networking_config())
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
@@ -129,7 +180,7 @@ class TestInit(CiTestCase):
expected_cfg = {'config': ['fakedatasource']}
self.init.datasource = FakeDataSource(network_config=expected_cfg)
self.assertEqual(
- (expected_cfg, 'ds'),
+ (expected_cfg, NetworkConfigSource.ds),
self.init._find_networking_config())
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
@@ -148,7 +199,7 @@ class TestInit(CiTestCase):
distro = self.init.distro
distro.generate_fallback_config = fake_generate_fallback
self.assertEqual(
- (fake_cfg, 'fallback'),
+ (fake_cfg, NetworkConfigSource.fallback),
self.init._find_networking_config())
self.assertNotIn('network config disabled', self.logs.getvalue())
@@ -177,7 +228,7 @@ class TestInit(CiTestCase):
'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
def fake_network_config():
- return net_cfg, 'fallback'
+ return net_cfg, NetworkConfigSource.fallback
m_macs.return_value = {'42:42:42:42:42:42': 'eth9'}
@@ -199,7 +250,7 @@ class TestInit(CiTestCase):
'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
def fake_network_config():
- return net_cfg, 'fallback'
+ return net_cfg, NetworkConfigSource.fallback
self.init._find_networking_config = fake_network_config
self.init.apply_network_config(True)
@@ -223,7 +274,7 @@ class TestInit(CiTestCase):
'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
def fake_network_config():
- return net_cfg, 'fallback'
+ return net_cfg, NetworkConfigSource.fallback
m_macs.return_value = {'42:42:42:42:42:42': 'eth9'}