summaryrefslogtreecommitdiff
path: root/cloudinit/net/sysconfig.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/net/sysconfig.py')
-rw-r--r--cloudinit/net/sysconfig.py155
1 files changed, 113 insertions, 42 deletions
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 58c5713f..a550f97c 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -5,11 +5,13 @@ import re
import six
+from cloudinit.distros.parsers import networkmanager_conf
from cloudinit.distros.parsers import resolv_conf
from cloudinit import util
from . import renderer
-from .network_state import subnet_is_ipv6
+from .network_state import (
+ is_ipv6_addr, net_prefix_to_ipv4_mask, subnet_is_ipv6)
def _make_header(sep='#'):
@@ -26,11 +28,8 @@ def _make_header(sep='#'):
def _is_default_route(route):
- if route['network'] == '::' and route['netmask'] == 0:
- return True
- if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0':
- return True
- return False
+ default_nets = ('::', '0.0.0.0')
+ return route['prefix'] == 0 and route['network'] in default_nets
def _quote_value(value):
@@ -62,6 +61,9 @@ class ConfigMap(object):
def __getitem__(self, key):
return self._conf[key]
+ def __contains__(self, key):
+ return key in self._conf
+
def drop(self, key):
self._conf.pop(key, None)
@@ -153,9 +155,10 @@ class Route(ConfigMap):
elif proto == "ipv6" and self.is_ipv6_route(address_value):
netmask_value = str(self._conf['NETMASK' + index])
gateway_value = str(self._conf['GATEWAY' + index])
- buf.write("%s/%s via %s\n" % (address_value,
- netmask_value,
- gateway_value))
+ buf.write("%s/%s via %s dev %s\n" % (address_value,
+ netmask_value,
+ gateway_value,
+ self._route_name))
return buf.getvalue()
@@ -252,6 +255,9 @@ class Renderer(renderer.Renderer):
self.netrules_path = config.get(
'netrules_path', 'etc/udev/rules.d/70-persistent-net.rules')
self.dns_path = config.get('dns_path', 'etc/resolv.conf')
+ nm_conf_path = 'etc/NetworkManager/conf.d/99-cloud-init.conf'
+ self.networkmanager_conf_path = config.get('networkmanager_conf_path',
+ nm_conf_path)
@classmethod
def _render_iface_shared(cls, iface, iface_cfg):
@@ -261,6 +267,9 @@ class Renderer(renderer.Renderer):
for (old_key, new_key) in [('mac_address', 'HWADDR'), ('mtu', 'MTU')]:
old_value = iface.get(old_key)
if old_value is not None:
+ # only set HWADDR on physical interfaces
+ if old_key == 'mac_address' and iface['type'] != 'physical':
+ continue
iface_cfg[new_key] = old_value
@classmethod
@@ -270,6 +279,7 @@ class Renderer(renderer.Renderer):
# modifying base values according to subnets
for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
+ mtu_key = 'MTU'
subnet_type = subnet.get('type')
if subnet_type == 'dhcp6':
iface_cfg['IPV6INIT'] = True
@@ -289,11 +299,20 @@ class Renderer(renderer.Renderer):
# if iface_cfg['BOOTPROTO'] == 'none':
# iface_cfg['BOOTPROTO'] = 'static'
if subnet_is_ipv6(subnet):
+ mtu_key = 'IPV6_MTU'
iface_cfg['IPV6INIT'] = True
+ if 'mtu' in subnet:
+ iface_cfg[mtu_key] = subnet['mtu']
+ elif subnet_type == 'manual':
+ # If the subnet has an MTU setting, then ONBOOT=True
+ # to apply the setting
+ iface_cfg['ONBOOT'] = mtu_key in iface_cfg
else:
raise ValueError("Unknown subnet type '%s' found"
" for interface '%s'" % (subnet_type,
iface_cfg.name))
+ if subnet.get('control') == 'manual':
+ iface_cfg['ONBOOT'] = False
# set IPv4 and IPv6 static addresses
ipv4_index = -1
@@ -307,38 +326,32 @@ class Renderer(renderer.Renderer):
elif subnet_type == 'static':
if subnet_is_ipv6(subnet):
ipv6_index = ipv6_index + 1
- if 'netmask' in subnet and str(subnet['netmask']) != "":
- ipv6_cidr = (subnet['address'] +
- '/' +
- str(subnet['netmask']))
- else:
- ipv6_cidr = subnet['address']
+ ipv6_cidr = "%s/%s" % (subnet['address'], subnet['prefix'])
if ipv6_index == 0:
iface_cfg['IPV6ADDR'] = ipv6_cidr
elif ipv6_index == 1:
iface_cfg['IPV6ADDR_SECONDARIES'] = ipv6_cidr
else:
- iface_cfg['IPV6ADDR_SECONDARIES'] = (
- iface_cfg['IPV6ADDR_SECONDARIES'] +
- " " + ipv6_cidr)
+ iface_cfg['IPV6ADDR_SECONDARIES'] += " " + ipv6_cidr
else:
ipv4_index = ipv4_index + 1
- if ipv4_index == 0:
- iface_cfg['IPADDR'] = subnet['address']
- if 'netmask' in subnet:
- iface_cfg['NETMASK'] = subnet['netmask']
+ suff = "" if ipv4_index == 0 else str(ipv4_index)
+ iface_cfg['IPADDR' + suff] = subnet['address']
+ iface_cfg['NETMASK' + suff] = \
+ net_prefix_to_ipv4_mask(subnet['prefix'])
+
+ if 'gateway' in subnet:
+ iface_cfg['DEFROUTE'] = True
+ if is_ipv6_addr(subnet['gateway']):
+ iface_cfg['IPV6_DEFAULTGW'] = subnet['gateway']
else:
- iface_cfg['IPADDR' + str(ipv4_index)] = \
- subnet['address']
- if 'netmask' in subnet:
- iface_cfg['NETMASK' + str(ipv4_index)] = \
- subnet['netmask']
+ iface_cfg['GATEWAY'] = subnet['gateway']
@classmethod
def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
for route in subnet.get('routes', []):
- is_ipv6 = subnet.get('ipv6')
+ is_ipv6 = subnet.get('ipv6') or is_ipv6_addr(route['gateway'])
if _is_default_route(route):
if (
@@ -360,7 +373,7 @@ class Renderer(renderer.Renderer):
# also provided the default route?
iface_cfg['DEFROUTE'] = True
if 'gateway' in route:
- if is_ipv6:
+ if is_ipv6 or is_ipv6_addr(route['gateway']):
iface_cfg['IPV6_DEFAULTGW'] = route['gateway']
route_cfg.has_set_default_ipv6 = True
else:
@@ -372,11 +385,13 @@ class Renderer(renderer.Renderer):
nm_key = 'NETMASK%s' % route_cfg.last_idx
addr_key = 'ADDRESS%s' % route_cfg.last_idx
route_cfg.last_idx += 1
- for (old_key, new_key) in [('gateway', gw_key),
- ('netmask', nm_key),
- ('network', addr_key)]:
- if old_key in route:
- route_cfg[new_key] = route[old_key]
+ # add default routes only to ifcfg files, not
+ # to route-* or route6-*
+ for (old_key, new_key) in [('gateway', gw_key),
+ ('netmask', nm_key),
+ ('network', addr_key)]:
+ if old_key in route:
+ route_cfg[new_key] = route[old_key]
@classmethod
def _render_bonding_opts(cls, iface_cfg, iface):
@@ -409,24 +424,45 @@ class Renderer(renderer.Renderer):
@classmethod
def _render_bond_interfaces(cls, network_state, iface_contents):
bond_filter = renderer.filter_by_type('bond')
+ slave_filter = renderer.filter_by_attr('bond-master')
for iface in network_state.iter_interfaces(bond_filter):
iface_name = iface['name']
iface_cfg = iface_contents[iface_name]
cls._render_bonding_opts(iface_cfg, iface)
- iface_master_name = iface['bond-master']
- iface_cfg['MASTER'] = iface_master_name
- iface_cfg['SLAVE'] = True
+
# Ensure that the master interface (and any of its children)
# are actually marked as being bond types...
- master_cfg = iface_contents[iface_master_name]
- master_cfgs = [master_cfg]
- master_cfgs.extend(master_cfg.children)
+ master_cfgs = [iface_cfg]
+ master_cfgs.extend(iface_cfg.children)
for master_cfg in master_cfgs:
master_cfg['BONDING_MASTER'] = True
master_cfg.kind = 'bond'
- @staticmethod
- def _render_vlan_interfaces(network_state, iface_contents):
+ if iface.get('mac_address'):
+ iface_cfg['MACADDR'] = iface.get('mac_address')
+
+ iface_subnets = iface.get("subnets", [])
+ route_cfg = iface_cfg.routes
+ cls._render_subnets(iface_cfg, iface_subnets)
+ cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
+
+ # iter_interfaces on network-state is not sorted to produce
+ # consistent numbers we need to sort.
+ bond_slaves = sorted(
+ [slave_iface['name'] for slave_iface in
+ network_state.iter_interfaces(slave_filter)
+ if slave_iface['bond-master'] == iface_name])
+
+ for index, bond_slave in enumerate(bond_slaves):
+ slavestr = 'BONDING_SLAVE%s' % index
+ iface_cfg[slavestr] = bond_slave
+
+ slave_cfg = iface_contents[bond_slave]
+ slave_cfg['MASTER'] = iface_name
+ slave_cfg['SLAVE'] = True
+
+ @classmethod
+ def _render_vlan_interfaces(cls, network_state, iface_contents):
vlan_filter = renderer.filter_by_type('vlan')
for iface in network_state.iter_interfaces(vlan_filter):
iface_name = iface['name']
@@ -434,6 +470,11 @@ class Renderer(renderer.Renderer):
iface_cfg['VLAN'] = True
iface_cfg['PHYSDEV'] = iface_name[:iface_name.rfind('.')]
+ iface_subnets = iface.get("subnets", [])
+ route_cfg = iface_cfg.routes
+ cls._render_subnets(iface_cfg, iface_subnets)
+ cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
+
@staticmethod
def _render_dns(network_state, existing_dns_path=None):
content = resolv_conf.ResolvConf("")
@@ -445,6 +486,21 @@ class Renderer(renderer.Renderer):
content.add_search_domain(searchdomain)
return "\n".join([_make_header(';'), str(content)])
+ @staticmethod
+ def _render_networkmanager_conf(network_state):
+ content = networkmanager_conf.NetworkManagerConf("")
+
+ # If DNS server information is provided, configure
+ # NetworkManager to not manage dns, so that /etc/resolv.conf
+ # does not get clobbered.
+ if network_state.dns_nameservers:
+ content.set_section_keypair('main', 'dns', 'none')
+
+ if len(content) == 0:
+ return None
+ out = "".join([_make_header(), "\n", "\n".join(content.write()), "\n"])
+ return out
+
@classmethod
def _render_bridge_interfaces(cls, network_state, iface_contents):
bridge_filter = renderer.filter_by_type('bridge')
@@ -455,6 +511,10 @@ class Renderer(renderer.Renderer):
for old_key, new_key in cls.bridge_opts_keys:
if old_key in iface:
iface_cfg[new_key] = iface[old_key]
+
+ if iface.get('mac_address'):
+ iface_cfg['MACADDR'] = iface.get('mac_address')
+
# Is this the right key to get all the connected interfaces?
for bridged_iface_name in iface.get('bridge_ports', []):
# Ensure all bridged interfaces are correctly tagged
@@ -465,6 +525,11 @@ class Renderer(renderer.Renderer):
for bridge_cfg in bridged_cfgs:
bridge_cfg['BRIDGE'] = iface_name
+ iface_subnets = iface.get("subnets", [])
+ route_cfg = iface_cfg.routes
+ cls._render_subnets(iface_cfg, iface_subnets)
+ cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
+
@classmethod
def _render_sysconfig(cls, base_sysconf_dir, network_state):
'''Given state, return /etc/sysconfig files + contents'''
@@ -505,6 +570,12 @@ class Renderer(renderer.Renderer):
resolv_content = self._render_dns(network_state,
existing_dns_path=dns_path)
util.write_file(dns_path, resolv_content, file_mode)
+ if self.networkmanager_conf_path:
+ nm_conf_path = util.target_path(target,
+ self.networkmanager_conf_path)
+ nm_conf_content = self._render_networkmanager_conf(network_state)
+ if nm_conf_content:
+ util.write_file(nm_conf_path, nm_conf_content, file_mode)
if self.netrules_path:
netrules_content = self._render_persistent_net(network_state)
netrules_path = util.target_path(target, self.netrules_path)