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