|
|
69219a |
From f14ed869e5784b1d5a3dfbabc1484eb266e8c3ab Mon Sep 17 00:00:00 2001
|
|
|
69219a |
From: Eduardo Otubo <otubo@redhat.com>
|
|
|
69219a |
Date: Mon, 4 May 2020 12:40:13 +0200
|
|
|
69219a |
Subject: [PATCH 6/6] net: IPv6, accept_ra, slaac, stateless (#51)
|
|
|
69219a |
|
|
|
69219a |
RH-Author: Eduardo Otubo <otubo@redhat.com>
|
|
|
69219a |
Message-id: <20200327152826.13343-7-otubo@redhat.com>
|
|
|
69219a |
Patchwork-id: 94458
|
|
|
69219a |
O-Subject: [RHEL-8.1.z/RHEL-8.2.z cloud-init PATCHv2 6/6] net: IPv6, accept_ra, slaac, stateless (#51)
|
|
|
69219a |
Bugzilla: 1811753
|
|
|
69219a |
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
|
|
69219a |
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
|
|
69219a |
|
|
|
69219a |
commit 62bbc262c3c7f633eac1d09ec78c055eef05166a
|
|
|
69219a |
Author: Harald <hjensas@redhat.com>
|
|
|
69219a |
Date: Wed Nov 20 18:55:27 2019 +0100
|
|
|
69219a |
|
|
|
69219a |
net: IPv6, accept_ra, slaac, stateless (#51)
|
|
|
69219a |
|
|
|
69219a |
Router advertisements are required for the default route
|
|
|
69219a |
to be set up, thus accept_ra should be enabled for
|
|
|
69219a |
dhcpv6-stateful.
|
|
|
69219a |
|
|
|
69219a |
sysconf: IPV6_FORCE_ACCEPT_RA controls accept_ra sysctl.
|
|
|
69219a |
eni: mode static and mode dhcp 'accept_ra' controls sysctl.
|
|
|
69219a |
|
|
|
69219a |
Add 'accept-ra: true|false' parameter to config v1 and
|
|
|
69219a |
v2. When True: accept_ra is set to '1'. When False:
|
|
|
69219a |
accept_ra is set to '0'. When not defined in config the
|
|
|
69219a |
value is left to the operating system default.
|
|
|
69219a |
|
|
|
69219a |
This change also extend the IPv6 support to distinguish
|
|
|
69219a |
between slaac and dhcpv6-stateless. SLAAC is autoconfig
|
|
|
69219a |
without any options from DHCP, while stateless auto-configures
|
|
|
69219a |
the address and the uses DHCP for other options.
|
|
|
69219a |
|
|
|
69219a |
LP: #1806014
|
|
|
69219a |
LP: #1808647
|
|
|
69219a |
|
|
|
69219a |
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
|
|
|
69219a |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
69219a |
---
|
|
|
69219a |
cloudinit/net/eni.py | 15 ++
|
|
|
69219a |
cloudinit/net/netplan.py | 9 +-
|
|
|
69219a |
cloudinit/net/network_state.py | 21 +-
|
|
|
69219a |
cloudinit/net/sysconfig.py | 34 ++-
|
|
|
69219a |
cloudinit/sources/helpers/openstack.py | 21 +-
|
|
|
69219a |
.../unittests/test_datasource/test_configdrive.py | 3 +-
|
|
|
69219a |
tests/unittests/test_net.py | 249 +++++++++++++++++++++
|
|
|
69219a |
7 files changed, 334 insertions(+), 18 deletions(-)
|
|
|
69219a |
|
|
|
69219a |
diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
|
|
|
69219a |
index 896a39b..c70435a 100644
|
|
|
69219a |
--- a/cloudinit/net/eni.py
|
|
|
69219a |
+++ b/cloudinit/net/eni.py
|
|
|
69219a |
@@ -393,6 +393,7 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
def _render_iface(self, iface, render_hwaddress=False):
|
|
|
69219a |
sections = []
|
|
|
69219a |
subnets = iface.get('subnets', {})
|
|
|
69219a |
+ accept_ra = iface.pop('accept-ra', None)
|
|
|
69219a |
if subnets:
|
|
|
69219a |
for index, subnet in enumerate(subnets):
|
|
|
69219a |
ipv4_subnet_mtu = None
|
|
|
69219a |
@@ -409,9 +410,23 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
subnet['type'] == 'ipv6_dhcpv6-stateful'):
|
|
|
69219a |
# Configure network settings using DHCP or DHCPv6
|
|
|
69219a |
iface['mode'] = 'dhcp'
|
|
|
69219a |
+ if accept_ra is not None:
|
|
|
69219a |
+ # Accept router advertisements (0=off, 1=on)
|
|
|
69219a |
+ iface['accept_ra'] = '1' if accept_ra else '0'
|
|
|
69219a |
elif subnet['type'] == 'ipv6_dhcpv6-stateless':
|
|
|
69219a |
# Configure network settings using SLAAC from RAs
|
|
|
69219a |
iface['mode'] = 'auto'
|
|
|
69219a |
+ # Use stateless DHCPv6 (0=off, 1=on)
|
|
|
69219a |
+ iface['dhcp'] = '1'
|
|
|
69219a |
+ elif subnet['type'] == 'ipv6_slaac':
|
|
|
69219a |
+ # Configure network settings using SLAAC from RAs
|
|
|
69219a |
+ iface['mode'] = 'auto'
|
|
|
69219a |
+ # Use stateless DHCPv6 (0=off, 1=on)
|
|
|
69219a |
+ iface['dhcp'] = '0'
|
|
|
69219a |
+ elif subnet_is_ipv6(subnet) and subnet['type'] == 'static':
|
|
|
69219a |
+ if accept_ra is not None:
|
|
|
69219a |
+ # Accept router advertisements (0=off, 1=on)
|
|
|
69219a |
+ iface['accept_ra'] = '1' if accept_ra else '0'
|
|
|
69219a |
|
|
|
69219a |
# do not emit multiple 'auto $IFACE' lines as older (precise)
|
|
|
69219a |
# ifupdown complains
|
|
|
69219a |
diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py
|
|
|
69219a |
index 21517fd..78ec38c 100644
|
|
|
69219a |
--- a/cloudinit/net/netplan.py
|
|
|
69219a |
+++ b/cloudinit/net/netplan.py
|
|
|
69219a |
@@ -4,7 +4,7 @@ import copy
|
|
|
69219a |
import os
|
|
|
69219a |
|
|
|
69219a |
from . import renderer
|
|
|
69219a |
-from .network_state import subnet_is_ipv6, NET_CONFIG_TO_V2
|
|
|
69219a |
+from .network_state import subnet_is_ipv6, NET_CONFIG_TO_V2, IPV6_DYNAMIC_TYPES
|
|
|
69219a |
|
|
|
69219a |
from cloudinit import log as logging
|
|
|
69219a |
from cloudinit import util
|
|
|
69219a |
@@ -51,7 +51,8 @@ def _extract_addresses(config, entry, ifname):
|
|
|
69219a |
'mtu': 1480,
|
|
|
69219a |
'netmask': 64,
|
|
|
69219a |
'type': 'static'}],
|
|
|
69219a |
- 'type: physical'
|
|
|
69219a |
+ 'type: physical',
|
|
|
69219a |
+ 'accept-ra': 'true'
|
|
|
69219a |
}
|
|
|
69219a |
|
|
|
69219a |
An entry dictionary looks like:
|
|
|
69219a |
@@ -92,6 +93,8 @@ def _extract_addresses(config, entry, ifname):
|
|
|
69219a |
if sn_type == 'dhcp':
|
|
|
69219a |
sn_type += '4'
|
|
|
69219a |
entry.update({sn_type: True})
|
|
|
69219a |
+ elif sn_type in IPV6_DYNAMIC_TYPES:
|
|
|
69219a |
+ entry.update({'dhcp6': True})
|
|
|
69219a |
elif sn_type in ['static']:
|
|
|
69219a |
addr = "%s" % subnet.get('address')
|
|
|
69219a |
if 'prefix' in subnet:
|
|
|
69219a |
@@ -144,6 +147,8 @@ def _extract_addresses(config, entry, ifname):
|
|
|
69219a |
ns = entry.get('nameservers', {})
|
|
|
69219a |
ns.update({'search': searchdomains})
|
|
|
69219a |
entry.update({'nameservers': ns})
|
|
|
69219a |
+ if 'accept-ra' in config and config['accept-ra'] is not None:
|
|
|
69219a |
+ entry.update({'accept-ra': util.is_true(config.get('accept-ra'))})
|
|
|
69219a |
|
|
|
69219a |
|
|
|
69219a |
def _extract_bond_slaves_by_name(interfaces, entry, bond_master):
|
|
|
69219a |
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
|
|
|
69219a |
index 571eb57..82cfa42 100644
|
|
|
69219a |
--- a/cloudinit/net/network_state.py
|
|
|
69219a |
+++ b/cloudinit/net/network_state.py
|
|
|
69219a |
@@ -17,13 +17,17 @@ from cloudinit import util
|
|
|
69219a |
LOG = logging.getLogger(__name__)
|
|
|
69219a |
|
|
|
69219a |
NETWORK_STATE_VERSION = 1
|
|
|
69219a |
+IPV6_DYNAMIC_TYPES = ['dhcp6',
|
|
|
69219a |
+ 'ipv6_slaac',
|
|
|
69219a |
+ 'ipv6_dhcpv6-stateless',
|
|
|
69219a |
+ 'ipv6_dhcpv6-stateful']
|
|
|
69219a |
NETWORK_STATE_REQUIRED_KEYS = {
|
|
|
69219a |
1: ['version', 'config', 'network_state'],
|
|
|
69219a |
}
|
|
|
69219a |
NETWORK_V2_KEY_FILTER = [
|
|
|
69219a |
'addresses', 'dhcp4', 'dhcp4-overrides', 'dhcp6', 'dhcp6-overrides',
|
|
|
69219a |
'gateway4', 'gateway6', 'interfaces', 'match', 'mtu', 'nameservers',
|
|
|
69219a |
- 'renderer', 'set-name', 'wakeonlan'
|
|
|
69219a |
+ 'renderer', 'set-name', 'wakeonlan', 'accept-ra'
|
|
|
69219a |
]
|
|
|
69219a |
|
|
|
69219a |
NET_CONFIG_TO_V2 = {
|
|
|
69219a |
@@ -341,7 +345,8 @@ class NetworkStateInterpreter(object):
|
|
|
69219a |
'name': 'eth0',
|
|
|
69219a |
'subnets': [
|
|
|
69219a |
{'type': 'dhcp4'}
|
|
|
69219a |
- ]
|
|
|
69219a |
+ ],
|
|
|
69219a |
+ 'accept-ra': 'true'
|
|
|
69219a |
}
|
|
|
69219a |
'''
|
|
|
69219a |
|
|
|
69219a |
@@ -361,6 +366,9 @@ class NetworkStateInterpreter(object):
|
|
|
69219a |
self.use_ipv6 = True
|
|
|
69219a |
break
|
|
|
69219a |
|
|
|
69219a |
+ accept_ra = command.get('accept-ra', None)
|
|
|
69219a |
+ if accept_ra is not None:
|
|
|
69219a |
+ accept_ra = util.is_true(accept_ra)
|
|
|
69219a |
iface.update({
|
|
|
69219a |
'name': command.get('name'),
|
|
|
69219a |
'type': command.get('type'),
|
|
|
69219a |
@@ -371,6 +379,7 @@ class NetworkStateInterpreter(object):
|
|
|
69219a |
'address': None,
|
|
|
69219a |
'gateway': None,
|
|
|
69219a |
'subnets': subnets,
|
|
|
69219a |
+ 'accept-ra': accept_ra
|
|
|
69219a |
})
|
|
|
69219a |
self._network_state['interfaces'].update({command.get('name'): iface})
|
|
|
69219a |
self.dump_network_state()
|
|
|
69219a |
@@ -614,6 +623,7 @@ class NetworkStateInterpreter(object):
|
|
|
69219a |
driver: ixgbe
|
|
|
69219a |
set-name: lom1
|
|
|
69219a |
dhcp6: true
|
|
|
69219a |
+ accept-ra: true
|
|
|
69219a |
switchports:
|
|
|
69219a |
match:
|
|
|
69219a |
name: enp2*
|
|
|
69219a |
@@ -642,7 +652,7 @@ class NetworkStateInterpreter(object):
|
|
|
69219a |
driver = match.get('driver', None)
|
|
|
69219a |
if driver:
|
|
|
69219a |
phy_cmd['params'] = {'driver': driver}
|
|
|
69219a |
- for key in ['mtu', 'match', 'wakeonlan']:
|
|
|
69219a |
+ for key in ['mtu', 'match', 'wakeonlan', 'accept-ra']:
|
|
|
69219a |
if key in cfg:
|
|
|
69219a |
phy_cmd[key] = cfg[key]
|
|
|
69219a |
|
|
|
69219a |
@@ -915,8 +925,9 @@ def is_ipv6_addr(address):
|
|
|
69219a |
|
|
|
69219a |
def subnet_is_ipv6(subnet):
|
|
|
69219a |
"""Common helper for checking network_state subnets for ipv6."""
|
|
|
69219a |
- # 'static6' or 'dhcp6'
|
|
|
69219a |
- if subnet['type'].endswith('6'):
|
|
|
69219a |
+ # 'static6', 'dhcp6', 'ipv6_dhcpv6-stateful', 'ipv6_dhcpv6-stateless' or
|
|
|
69219a |
+ # 'ipv6_slaac'
|
|
|
69219a |
+ if subnet['type'].endswith('6') or subnet['type'] in IPV6_DYNAMIC_TYPES:
|
|
|
69219a |
# This is a request for DHCPv6.
|
|
|
69219a |
return True
|
|
|
69219a |
elif subnet['type'] == 'static' and is_ipv6_addr(subnet.get('address')):
|
|
|
69219a |
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
|
|
|
69219a |
index 8b11dbb..13c0a65 100644
|
|
|
69219a |
--- a/cloudinit/net/sysconfig.py
|
|
|
69219a |
+++ b/cloudinit/net/sysconfig.py
|
|
|
69219a |
@@ -14,7 +14,7 @@ from configobj import ConfigObj
|
|
|
69219a |
|
|
|
69219a |
from . import renderer
|
|
|
69219a |
from .network_state import (
|
|
|
69219a |
- is_ipv6_addr, net_prefix_to_ipv4_mask, subnet_is_ipv6)
|
|
|
69219a |
+ is_ipv6_addr, net_prefix_to_ipv4_mask, subnet_is_ipv6, IPV6_DYNAMIC_TYPES)
|
|
|
69219a |
|
|
|
69219a |
LOG = logging.getLogger(__name__)
|
|
|
69219a |
NM_CFG_FILE = "/etc/NetworkManager/NetworkManager.conf"
|
|
|
69219a |
@@ -319,6 +319,9 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
continue
|
|
|
69219a |
iface_cfg[new_key] = old_value
|
|
|
69219a |
|
|
|
69219a |
+ if iface['accept-ra'] is not None:
|
|
|
69219a |
+ iface_cfg['IPV6_FORCE_ACCEPT_RA'] = iface['accept-ra']
|
|
|
69219a |
+
|
|
|
69219a |
@classmethod
|
|
|
69219a |
def _render_subnets(cls, iface_cfg, subnets, has_default_route):
|
|
|
69219a |
# setting base values
|
|
|
69219a |
@@ -335,6 +338,15 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
iface_cfg['DHCPV6C'] = True
|
|
|
69219a |
elif subnet_type == 'ipv6_dhcpv6-stateless':
|
|
|
69219a |
iface_cfg['IPV6INIT'] = True
|
|
|
69219a |
+ # Configure network settings using SLAAC from RAs and optional
|
|
|
69219a |
+ # info from dhcp server using DHCPv6
|
|
|
69219a |
+ iface_cfg['IPV6_AUTOCONF'] = True
|
|
|
69219a |
+ iface_cfg['DHCPV6C'] = True
|
|
|
69219a |
+ # Use Information-request to get only stateless configuration
|
|
|
69219a |
+ # parameters (i.e., without address).
|
|
|
69219a |
+ iface_cfg['DHCPV6C_OPTIONS'] = '-S'
|
|
|
69219a |
+ elif subnet_type == 'ipv6_slaac':
|
|
|
69219a |
+ iface_cfg['IPV6INIT'] = True
|
|
|
69219a |
# Configure network settings using SLAAC from RAs
|
|
|
69219a |
iface_cfg['IPV6_AUTOCONF'] = True
|
|
|
69219a |
elif subnet_type in ['dhcp4', 'dhcp']:
|
|
|
69219a |
@@ -381,10 +393,15 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
# metric may apply to both dhcp and static config
|
|
|
69219a |
if 'metric' in subnet:
|
|
|
69219a |
iface_cfg['METRIC'] = subnet['metric']
|
|
|
69219a |
+ # TODO(hjensas): Including dhcp6 here is likely incorrect. DHCPv6
|
|
|
69219a |
+ # does not ever provide a default gateway, the default gateway
|
|
|
69219a |
+ # come from RA's. (https://github.com/openSUSE/wicked/issues/570)
|
|
|
69219a |
if subnet_type in ['dhcp', 'dhcp4', 'dhcp6']:
|
|
|
69219a |
if has_default_route and iface_cfg['BOOTPROTO'] != 'none':
|
|
|
69219a |
iface_cfg['DHCLIENT_SET_DEFAULT_ROUTE'] = False
|
|
|
69219a |
continue
|
|
|
69219a |
+ elif subnet_type in IPV6_DYNAMIC_TYPES:
|
|
|
69219a |
+ continue
|
|
|
69219a |
elif subnet_type == 'static':
|
|
|
69219a |
if subnet_is_ipv6(subnet):
|
|
|
69219a |
ipv6_index = ipv6_index + 1
|
|
|
69219a |
@@ -424,10 +441,14 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
@classmethod
|
|
|
69219a |
def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
|
|
|
69219a |
for _, subnet in enumerate(subnets, start=len(iface_cfg.children)):
|
|
|
69219a |
+ subnet_type = subnet.get('type')
|
|
|
69219a |
for route in subnet.get('routes', []):
|
|
|
69219a |
is_ipv6 = subnet.get('ipv6') or is_ipv6_addr(route['gateway'])
|
|
|
69219a |
|
|
|
69219a |
- if _is_default_route(route):
|
|
|
69219a |
+ # Any dynamic configuration method, slaac, dhcpv6-stateful/
|
|
|
69219a |
+ # stateless should get router information from router RA's.
|
|
|
69219a |
+ if (_is_default_route(route) and subnet_type not in
|
|
|
69219a |
+ IPV6_DYNAMIC_TYPES):
|
|
|
69219a |
if (
|
|
|
69219a |
(subnet.get('ipv4') and
|
|
|
69219a |
route_cfg.has_set_default_ipv4) or
|
|
|
69219a |
@@ -446,10 +467,17 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
# TODO(harlowja): add validation that no other iface has
|
|
|
69219a |
# also provided the default route?
|
|
|
69219a |
iface_cfg['DEFROUTE'] = True
|
|
|
69219a |
+ # TODO(hjensas): Including dhcp6 here is likely incorrect.
|
|
|
69219a |
+ # DHCPv6 does not ever provide a default gateway, the
|
|
|
69219a |
+ # default gateway come from RA's.
|
|
|
69219a |
+ # (https://github.com/openSUSE/wicked/issues/570)
|
|
|
69219a |
if iface_cfg['BOOTPROTO'] in ('dhcp', 'dhcp4', 'dhcp6'):
|
|
|
69219a |
+ # NOTE(hjensas): DHCLIENT_SET_DEFAULT_ROUTE is SuSE
|
|
|
69219a |
+ # only. RHEL, CentOS, Fedora does not implement this
|
|
|
69219a |
+ # option.
|
|
|
69219a |
iface_cfg['DHCLIENT_SET_DEFAULT_ROUTE'] = True
|
|
|
69219a |
if 'gateway' in route:
|
|
|
69219a |
- if is_ipv6 or is_ipv6_addr(route['gateway']):
|
|
|
69219a |
+ if is_ipv6:
|
|
|
69219a |
iface_cfg['IPV6_DEFAULTGW'] = route['gateway']
|
|
|
69219a |
route_cfg.has_set_default_ipv6 = True
|
|
|
69219a |
else:
|
|
|
69219a |
diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
|
|
|
69219a |
index 9f2fd2d..77fcdd3 100644
|
|
|
69219a |
--- a/cloudinit/sources/helpers/openstack.py
|
|
|
69219a |
+++ b/cloudinit/sources/helpers/openstack.py
|
|
|
69219a |
@@ -584,17 +584,24 @@ def convert_net_json(network_json=None, known_macs=None):
|
|
|
69219a |
if n['link'] == link['id']]:
|
|
|
69219a |
subnet = dict((k, v) for k, v in network.items()
|
|
|
69219a |
if k in valid_keys['subnet'])
|
|
|
69219a |
- if 'dhcp' in network['type']:
|
|
|
69219a |
- t = (network['type'] if network['type'].startswith('ipv6')
|
|
|
69219a |
- else 'dhcp4')
|
|
|
69219a |
- subnet.update({
|
|
|
69219a |
- 'type': t,
|
|
|
69219a |
- })
|
|
|
69219a |
- else:
|
|
|
69219a |
+
|
|
|
69219a |
+ if network['type'] == 'ipv4_dhcp':
|
|
|
69219a |
+ subnet.update({'type': 'dhcp4'})
|
|
|
69219a |
+ elif network['type'] == 'ipv6_dhcp':
|
|
|
69219a |
+ subnet.update({'type': 'dhcp6'})
|
|
|
69219a |
+ elif network['type'] in ['ipv6_slaac', 'ipv6_dhcpv6-stateless',
|
|
|
69219a |
+ 'ipv6_dhcpv6-stateful']:
|
|
|
69219a |
+ subnet.update({'type': network['type']})
|
|
|
69219a |
+ elif network['type'] in ['ipv4', 'ipv6']:
|
|
|
69219a |
subnet.update({
|
|
|
69219a |
'type': 'static',
|
|
|
69219a |
'address': network.get('ip_address'),
|
|
|
69219a |
})
|
|
|
69219a |
+
|
|
|
69219a |
+ # Enable accept_ra for stateful and legacy ipv6_dhcp types
|
|
|
69219a |
+ if network['type'] in ['ipv6_dhcpv6-stateful', 'ipv6_dhcp']:
|
|
|
69219a |
+ cfg.update({'accept-ra': True})
|
|
|
69219a |
+
|
|
|
69219a |
if network['type'] == 'ipv4':
|
|
|
69219a |
subnet['ipv4'] = True
|
|
|
69219a |
if network['type'] == 'ipv6':
|
|
|
69219a |
diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py
|
|
|
69219a |
index ed4e9d5..bd4c310 100644
|
|
|
69219a |
--- a/tests/unittests/test_datasource/test_configdrive.py
|
|
|
69219a |
+++ b/tests/unittests/test_datasource/test_configdrive.py
|
|
|
69219a |
@@ -536,7 +536,8 @@ class TestNetJson(CiTestCase):
|
|
|
69219a |
'mtu': None,
|
|
|
69219a |
'name': 'enp0s2',
|
|
|
69219a |
'subnets': [{'type': 'ipv6_dhcpv6-stateful'}],
|
|
|
69219a |
- 'type': 'physical'}
|
|
|
69219a |
+ 'type': 'physical',
|
|
|
69219a |
+ 'accept-ra': True}
|
|
|
69219a |
],
|
|
|
69219a |
}
|
|
|
69219a |
conv_data = openstack.convert_net_json(in_data, known_macs=KNOWN_MACS)
|
|
|
69219a |
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
|
|
69219a |
index 70d13f3..21a3f0e 100644
|
|
|
69219a |
--- a/tests/unittests/test_net.py
|
|
|
69219a |
+++ b/tests/unittests/test_net.py
|
|
|
69219a |
@@ -712,6 +712,143 @@ NETWORK_CONFIGS = {
|
|
|
69219a |
"""),
|
|
|
69219a |
},
|
|
|
69219a |
},
|
|
|
69219a |
+ 'dhcpv6_accept_ra': {
|
|
|
69219a |
+ 'expected_eni': textwrap.dedent("""\
|
|
|
69219a |
+ auto lo
|
|
|
69219a |
+ iface lo inet loopback
|
|
|
69219a |
+
|
|
|
69219a |
+ auto iface0
|
|
|
69219a |
+ iface iface0 inet6 dhcp
|
|
|
69219a |
+ accept_ra 1
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'expected_netplan': textwrap.dedent("""
|
|
|
69219a |
+ network:
|
|
|
69219a |
+ version: 2
|
|
|
69219a |
+ ethernets:
|
|
|
69219a |
+ iface0:
|
|
|
69219a |
+ accept-ra: true
|
|
|
69219a |
+ dhcp6: true
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'yaml_v1': textwrap.dedent("""\
|
|
|
69219a |
+ version: 1
|
|
|
69219a |
+ config:
|
|
|
69219a |
+ - type: 'physical'
|
|
|
69219a |
+ name: 'iface0'
|
|
|
69219a |
+ subnets:
|
|
|
69219a |
+ - {'type': 'dhcp6'}
|
|
|
69219a |
+ accept-ra: true
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'yaml_v2': textwrap.dedent("""\
|
|
|
69219a |
+ version: 2
|
|
|
69219a |
+ ethernets:
|
|
|
69219a |
+ iface0:
|
|
|
69219a |
+ dhcp6: true
|
|
|
69219a |
+ accept-ra: true
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'expected_sysconfig': {
|
|
|
69219a |
+ 'ifcfg-iface0': textwrap.dedent("""\
|
|
|
69219a |
+ BOOTPROTO=none
|
|
|
69219a |
+ DEVICE=iface0
|
|
|
69219a |
+ DHCPV6C=yes
|
|
|
69219a |
+ IPV6INIT=yes
|
|
|
69219a |
+ IPV6_FORCE_ACCEPT_RA=yes
|
|
|
69219a |
+ DEVICE=iface0
|
|
|
69219a |
+ NM_CONTROLLED=no
|
|
|
69219a |
+ ONBOOT=yes
|
|
|
69219a |
+ STARTMODE=auto
|
|
|
69219a |
+ TYPE=Ethernet
|
|
|
69219a |
+ USERCTL=no
|
|
|
69219a |
+ """),
|
|
|
69219a |
+ },
|
|
|
69219a |
+ },
|
|
|
69219a |
+ 'dhcpv6_reject_ra': {
|
|
|
69219a |
+ 'expected_eni': textwrap.dedent("""\
|
|
|
69219a |
+ auto lo
|
|
|
69219a |
+ iface lo inet loopback
|
|
|
69219a |
+
|
|
|
69219a |
+ auto iface0
|
|
|
69219a |
+ iface iface0 inet6 dhcp
|
|
|
69219a |
+ accept_ra 0
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'expected_netplan': textwrap.dedent("""
|
|
|
69219a |
+ network:
|
|
|
69219a |
+ version: 2
|
|
|
69219a |
+ ethernets:
|
|
|
69219a |
+ iface0:
|
|
|
69219a |
+ accept-ra: false
|
|
|
69219a |
+ dhcp6: true
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'yaml_v1': textwrap.dedent("""\
|
|
|
69219a |
+ version: 1
|
|
|
69219a |
+ config:
|
|
|
69219a |
+ - type: 'physical'
|
|
|
69219a |
+ name: 'iface0'
|
|
|
69219a |
+ subnets:
|
|
|
69219a |
+ - {'type': 'dhcp6'}
|
|
|
69219a |
+ accept-ra: false
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'yaml_v2': textwrap.dedent("""\
|
|
|
69219a |
+ version: 2
|
|
|
69219a |
+ ethernets:
|
|
|
69219a |
+ iface0:
|
|
|
69219a |
+ dhcp6: true
|
|
|
69219a |
+ accept-ra: false
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'expected_sysconfig': {
|
|
|
69219a |
+ 'ifcfg-iface0': textwrap.dedent("""\
|
|
|
69219a |
+ BOOTPROTO=none
|
|
|
69219a |
+ DEVICE=iface0
|
|
|
69219a |
+ DHCPV6C=yes
|
|
|
69219a |
+ IPV6INIT=yes
|
|
|
69219a |
+ IPV6_FORCE_ACCEPT_RA=no
|
|
|
69219a |
+ DEVICE=iface0
|
|
|
69219a |
+ NM_CONTROLLED=no
|
|
|
69219a |
+ ONBOOT=yes
|
|
|
69219a |
+ STARTMODE=auto
|
|
|
69219a |
+ TYPE=Ethernet
|
|
|
69219a |
+ USERCTL=no
|
|
|
69219a |
+ """),
|
|
|
69219a |
+ },
|
|
|
69219a |
+ },
|
|
|
69219a |
+ 'ipv6_slaac': {
|
|
|
69219a |
+ 'expected_eni': textwrap.dedent("""\
|
|
|
69219a |
+ auto lo
|
|
|
69219a |
+ iface lo inet loopback
|
|
|
69219a |
+
|
|
|
69219a |
+ auto iface0
|
|
|
69219a |
+ iface iface0 inet6 auto
|
|
|
69219a |
+ dhcp 0
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'expected_netplan': textwrap.dedent("""
|
|
|
69219a |
+ network:
|
|
|
69219a |
+ version: 2
|
|
|
69219a |
+ ethernets:
|
|
|
69219a |
+ iface0:
|
|
|
69219a |
+ dhcp6: true
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'yaml': textwrap.dedent("""\
|
|
|
69219a |
+ version: 1
|
|
|
69219a |
+ config:
|
|
|
69219a |
+ - type: 'physical'
|
|
|
69219a |
+ name: 'iface0'
|
|
|
69219a |
+ subnets:
|
|
|
69219a |
+ - {'type': 'ipv6_slaac'}
|
|
|
69219a |
+ """).rstrip(' '),
|
|
|
69219a |
+ 'expected_sysconfig': {
|
|
|
69219a |
+ 'ifcfg-iface0': textwrap.dedent("""\
|
|
|
69219a |
+ BOOTPROTO=none
|
|
|
69219a |
+ DEVICE=iface0
|
|
|
69219a |
+ IPV6_AUTOCONF=yes
|
|
|
69219a |
+ IPV6INIT=yes
|
|
|
69219a |
+ DEVICE=iface0
|
|
|
69219a |
+ NM_CONTROLLED=no
|
|
|
69219a |
+ ONBOOT=yes
|
|
|
69219a |
+ STARTMODE=auto
|
|
|
69219a |
+ TYPE=Ethernet
|
|
|
69219a |
+ USERCTL=no
|
|
|
69219a |
+ """),
|
|
|
69219a |
+ },
|
|
|
69219a |
+ },
|
|
|
69219a |
'dhcpv6_stateless': {
|
|
|
69219a |
'expected_eni': textwrap.dedent("""\
|
|
|
69219a |
auto lo
|
|
|
69219a |
@@ -719,6 +856,7 @@ NETWORK_CONFIGS = {
|
|
|
69219a |
|
|
|
69219a |
auto iface0
|
|
|
69219a |
iface iface0 inet6 auto
|
|
|
69219a |
+ dhcp 1
|
|
|
69219a |
""").rstrip(' '),
|
|
|
69219a |
'expected_netplan': textwrap.dedent("""
|
|
|
69219a |
network:
|
|
|
69219a |
@@ -739,6 +877,8 @@ NETWORK_CONFIGS = {
|
|
|
69219a |
'ifcfg-iface0': textwrap.dedent("""\
|
|
|
69219a |
BOOTPROTO=none
|
|
|
69219a |
DEVICE=iface0
|
|
|
69219a |
+ DHCPV6C=yes
|
|
|
69219a |
+ DHCPV6C_OPTIONS=-S
|
|
|
69219a |
IPV6_AUTOCONF=yes
|
|
|
69219a |
IPV6INIT=yes
|
|
|
69219a |
DEVICE=iface0
|
|
|
69219a |
@@ -763,6 +903,7 @@ NETWORK_CONFIGS = {
|
|
|
69219a |
version: 2
|
|
|
69219a |
ethernets:
|
|
|
69219a |
iface0:
|
|
|
69219a |
+ accept-ra: true
|
|
|
69219a |
dhcp6: true
|
|
|
69219a |
""").rstrip(' '),
|
|
|
69219a |
'yaml': textwrap.dedent("""\
|
|
|
69219a |
@@ -772,6 +913,7 @@ NETWORK_CONFIGS = {
|
|
|
69219a |
name: 'iface0'
|
|
|
69219a |
subnets:
|
|
|
69219a |
- {'type': 'ipv6_dhcpv6-stateful'}
|
|
|
69219a |
+ accept-ra: true
|
|
|
69219a |
""").rstrip(' '),
|
|
|
69219a |
'expected_sysconfig': {
|
|
|
69219a |
'ifcfg-iface0': textwrap.dedent("""\
|
|
|
69219a |
@@ -779,6 +921,7 @@ NETWORK_CONFIGS = {
|
|
|
69219a |
DEVICE=iface0
|
|
|
69219a |
DHCPV6C=yes
|
|
|
69219a |
IPV6INIT=yes
|
|
|
69219a |
+ IPV6_FORCE_ACCEPT_RA=yes
|
|
|
69219a |
DEVICE=iface0
|
|
|
69219a |
NM_CONTROLLED=no
|
|
|
69219a |
ONBOOT=yes
|
|
|
69219a |
@@ -2376,6 +2519,33 @@ USERCTL=no
|
|
|
69219a |
}
|
|
|
69219a |
}
|
|
|
69219a |
|
|
|
69219a |
+ def test_dhcpv6_accept_ra_config_v1(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_accept_ra']
|
|
|
69219a |
+ found = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml_v1']))
|
|
|
69219a |
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
|
69219a |
+ self._assert_headers(found)
|
|
|
69219a |
+
|
|
|
69219a |
+ def test_dhcpv6_accept_ra_config_v2(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_accept_ra']
|
|
|
69219a |
+ found = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml_v2']))
|
|
|
69219a |
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
|
69219a |
+ self._assert_headers(found)
|
|
|
69219a |
+
|
|
|
69219a |
+ def test_dhcpv6_reject_ra_config_v1(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_reject_ra']
|
|
|
69219a |
+ found = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml_v1']))
|
|
|
69219a |
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
|
69219a |
+ self._assert_headers(found)
|
|
|
69219a |
+
|
|
|
69219a |
+ def test_dhcpv6_reject_ra_config_v2(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_reject_ra']
|
|
|
69219a |
+ found = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml_v2']))
|
|
|
69219a |
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
|
69219a |
+ self._assert_headers(found)
|
|
|
69219a |
|
|
|
69219a |
def test_dhcpv6_stateless_config(self):
|
|
|
69219a |
entry = NETWORK_CONFIGS['dhcpv6_stateless']
|
|
|
69219a |
@@ -3267,6 +3437,46 @@ class TestNetplanRoundTrip(CiTestCase):
|
|
|
69219a |
entry['expected_netplan'].splitlines(),
|
|
|
69219a |
files['/etc/netplan/50-cloud-init.yaml'].splitlines())
|
|
|
69219a |
|
|
|
69219a |
+ def testsimple_render_dhcpv6_accept_ra(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_accept_ra']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml_v1']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_netplan'].splitlines(),
|
|
|
69219a |
+ files['/etc/netplan/50-cloud-init.yaml'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
+ def testsimple_render_dhcpv6_reject_ra(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_reject_ra']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml_v1']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_netplan'].splitlines(),
|
|
|
69219a |
+ files['/etc/netplan/50-cloud-init.yaml'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
+ def testsimple_render_ipv6_slaac(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['ipv6_slaac']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_netplan'].splitlines(),
|
|
|
69219a |
+ files['/etc/netplan/50-cloud-init.yaml'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
+ def testsimple_render_dhcpv6_stateless(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_stateless']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_netplan'].splitlines(),
|
|
|
69219a |
+ files['/etc/netplan/50-cloud-init.yaml'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
+ def testsimple_render_dhcpv6_stateful(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_stateful']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_netplan'].splitlines(),
|
|
|
69219a |
+ files['/etc/netplan/50-cloud-init.yaml'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
def testsimple_render_all(self):
|
|
|
69219a |
entry = NETWORK_CONFIGS['all']
|
|
|
69219a |
files = self._render_and_read(network_config=yaml.load(entry['yaml']))
|
|
|
69219a |
@@ -3350,6 +3560,45 @@ class TestEniRoundTrip(CiTestCase):
|
|
|
69219a |
entry['expected_eni'].splitlines(),
|
|
|
69219a |
files['/etc/network/interfaces'].splitlines())
|
|
|
69219a |
|
|
|
69219a |
+ def testsimple_render_dhcpv6_stateless(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_stateless']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml']))
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(entry['yaml']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_eni'].splitlines(),
|
|
|
69219a |
+ files['/etc/network/interfaces'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
+ def testsimple_render_ipv6_slaac(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['ipv6_slaac']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(entry['yaml']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_eni'].splitlines(),
|
|
|
69219a |
+ files['/etc/network/interfaces'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
+ def testsimple_render_dhcpv6_stateful(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_stateless']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(entry['yaml']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_eni'].splitlines(),
|
|
|
69219a |
+ files['/etc/network/interfaces'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
+ def testsimple_render_dhcpv6_accept_ra(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_accept_ra']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml_v1']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_eni'].splitlines(),
|
|
|
69219a |
+ files['/etc/network/interfaces'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
+ def testsimple_render_dhcpv6_reject_ra(self):
|
|
|
69219a |
+ entry = NETWORK_CONFIGS['dhcpv6_reject_ra']
|
|
|
69219a |
+ files = self._render_and_read(network_config=yaml.load(
|
|
|
69219a |
+ entry['yaml_v1']))
|
|
|
69219a |
+ self.assertEqual(
|
|
|
69219a |
+ entry['expected_eni'].splitlines(),
|
|
|
69219a |
+ files['/etc/network/interfaces'].splitlines())
|
|
|
69219a |
+
|
|
|
69219a |
def testsimple_render_manual(self):
|
|
|
69219a |
"""Test rendering of 'manual' for 'type' and 'control'.
|
|
|
69219a |
|
|
|
69219a |
--
|
|
|
69219a |
1.8.3.1
|
|
|
69219a |
|