diff options
-rwxr-xr-x | bin/cloud-init | 7 | ||||
-rw-r--r-- | cloudinit/distros/debian.py | 34 | ||||
-rw-r--r-- | cloudinit/net/__init__.py | 53 | ||||
-rw-r--r-- | cloudinit/stages.py | 4 | ||||
-rw-r--r-- | tests/unittests/test_net.py | 4 |
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']}], |