summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/cloud-init7
-rw-r--r--cloudinit/distros/debian.py34
-rw-r--r--cloudinit/net/__init__.py53
-rw-r--r--cloudinit/stages.py4
-rw-r--r--tests/unittests/test_net.py4
5 files changed, 81 insertions, 21 deletions
diff --git a/bin/cloud-init b/bin/cloud-init
index 715be4b5..5857af32 100755
--- a/bin/cloud-init
+++ b/bin/cloud-init
@@ -266,7 +266,12 @@ def main_init(name, args):
return (None, ["No instance datasource found."])
if args.local:
- init.apply_network_config()
+ if not init.ds_restored:
+ # if local mode and the datasource was not restored from cache
+ # (this is not first boot) then apply networking.
+ init.apply_network_config()
+ else:
+ LOG.debug("skipping networking config from restored datasource.")
# Stage 6
iid = init.instancify()
diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py
index 5d7e6cfc..75ab340f 100644
--- a/cloudinit/distros/debian.py
+++ b/cloudinit/distros/debian.py
@@ -84,7 +84,8 @@ class Distro(distros.Distro):
eni=self.network_conf_fn,
links_prefix=self.links_prefix,
netrules=None)
- util.del_file("/etc/network/interfaces.d/eth0.cfg")
+ _maybe_remove_legacy_eth0()
+
return []
def _bring_up_interfaces(self, device_names):
@@ -193,3 +194,34 @@ def _get_wrapper_prefix(cmd, mode):
return cmd
else:
return []
+
+
+def _maybe_remove_legacy_eth0(path="/etc/network/interfaces.d/eth0.cfg"):
+ """Ubuntu cloud images previously included a 'eth0.cfg' that had
+ hard coded content. That file would interfere with the rendered
+ configuration if it was present.
+
+ if the file does not exist do nothing.
+ If the file exists:
+ - with known content, remove it and warn
+ - with unknown content, leave it and warn
+ """
+
+ if not os.path.exists(path):
+ return
+
+ bmsg = "Dynamic networking config may not apply."
+ try:
+ contents = util.load_file(path)
+ known_contents = ["auto eth0", "iface eth0 inet dhcp"]
+ lines = [f.strip() for f in contents.splitlines()
+ if not f.startswith("#")]
+ if lines == known_contents:
+ util.del_file(path)
+ msg = "removed %s with known contents" % path
+ else:
+ msg = (bmsg + " '%s' exists with user configured content." % path)
+ except:
+ msg = bmsg + " %s exists, but could not be read." % path
+
+ LOG.warn(msg)
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
index a765b75a..31544fd8 100644
--- a/cloudinit/net/__init__.py
+++ b/cloudinit/net/__init__.py
@@ -350,7 +350,7 @@ def _klibc_to_config_entry(content, mac_addrs=None):
# if no IPV4ADDR or IPV6ADDR, then go on.
if pre + "ADDR" not in data:
continue
- subnet = {'type': proto}
+ subnet = {'type': proto, 'control': 'manual'}
# these fields go right on the subnet
for key in ('NETMASK', 'BROADCAST', 'GATEWAY'):
@@ -444,12 +444,13 @@ def iface_add_subnet(iface, subnet):
def iface_add_attrs(iface):
content = ""
ignore_map = [
- 'type',
- 'name',
+ 'control',
+ 'index',
'inet',
'mode',
- 'index',
+ 'name',
'subnets',
+ 'type',
]
if iface['type'] not in ['bond', 'bridge', 'vlan']:
ignore_map.append('mac_address')
@@ -508,6 +509,26 @@ def render_route(route, indent=""):
return content
+def iface_start_entry(iface, index):
+ fullname = iface['name']
+ if index != 0:
+ fullname += ":%s" % index
+
+ control = iface['control']
+ if control == "auto":
+ cverb = "auto"
+ elif control in ("hotplug",):
+ cverb = "allow-" + control
+ else:
+ cverb = "# control-" + control
+
+ subst = iface.copy()
+ subst.update({'fullname': fullname, 'cverb': cverb})
+
+ return ("{cverb} {fullname}\n"
+ "iface {fullname} {inet} {mode}\n").format(**subst)
+
+
def render_interfaces(network_state):
''' Given state, emit etc/network/interfaces content '''
@@ -528,16 +549,19 @@ def render_interfaces(network_state):
if len(value):
content += " dns-{} {}\n".format(dnskey, " ".join(value))
- content += "\n"
for iface in sorted(interfaces.values(),
key=lambda k: (order[k['type']], k['name'])):
- content += "auto {name}\n".format(**iface)
+ if content[-2:] != "\n\n":
+ content += "\n"
subnets = iface.get('subnets', {})
if subnets:
for index, subnet in zip(range(0, len(subnets)), subnets):
+ if content[-2:] != "\n\n":
+ content += "\n"
iface['index'] = index
iface['mode'] = subnet['type']
+ iface['control'] = subnet.get('control', 'auto')
if iface['mode'].endswith('6'):
iface['inet'] += '6'
elif iface['mode'] == 'static' and ":" in subnet['address']:
@@ -545,28 +569,21 @@ def render_interfaces(network_state):
if iface['mode'].startswith('dhcp'):
iface['mode'] = 'dhcp'
- if index == 0:
- content += "iface {name} {inet} {mode}\n".format(**iface)
- else:
- content += "auto {name}:{index}\n".format(**iface)
- content += \
- "iface {name}:{index} {inet} {mode}\n".format(**iface)
-
+ content += iface_start_entry(iface, index)
content += iface_add_subnet(iface, subnet)
content += iface_add_attrs(iface)
- for route in subnet.get('routes', []):
- content += render_route(route, indent=" ")
- content += "\n"
else:
+ # ifenslave docs say to auto the slave devices
+ if 'bond-master' in iface:
+ content += "auto {name}\n".format(**iface)
content += "iface {name} {inet} {mode}\n".format(**iface)
content += iface_add_attrs(iface)
- content += "\n"
for route in network_state.get('routes'):
content += render_route(route)
# global replacements until v2 format
- content = content.replace('mac_address', 'hwaddress ether')
+ content = content.replace('mac_address', 'hwaddress')
return content
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 3fbb4443..ffb15165 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -66,6 +66,7 @@ class Init(object):
self._distro = None
# Changed only when a fetch occurs
self.datasource = NULL_DATA_SOURCE
+ self.ds_restored = False
if reporter is None:
reporter = events.ReportEventStack(
@@ -80,6 +81,7 @@ class Init(object):
self._distro = None
if reset_ds:
self.datasource = NULL_DATA_SOURCE
+ self.ds_restored = False
@property
def distro(self):
@@ -231,6 +233,8 @@ class Init(object):
ds = None
else:
myrep.description = "no cache found"
+
+ self.ds_restored = bool(ds)
LOG.debug(myrep.description)
if not ds:
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index dfb31710..09235c4d 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -33,6 +33,7 @@ DHCP_EXPECTED_1 = {
'name': 'eth0',
'type': 'physical',
'subnets': [{'broadcast': '192.168.122.255',
+ 'control': 'manual',
'gateway': '192.168.122.1',
'dns_search': ['foo.com'],
'type': 'dhcp',
@@ -59,7 +60,8 @@ DOMAINSEARCH='foo.com'
STATIC_EXPECTED_1 = {
'name': 'eth1',
'type': 'physical',
- 'subnets': [{'broadcast': '10.0.0.255', 'gateway': '10.0.0.1',
+ 'subnets': [{'broadcast': '10.0.0.255', 'control': 'manual',
+ 'gateway': '10.0.0.1',
'dns_search': ['foo.com'], 'type': 'static',
'netmask': '255.255.255.0',
'dns_nameservers': ['10.0.1.1']}],