← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~harald-jensas/cloud-init:bug/1806014 into cloud-init:master

 

Harald Jensås has proposed merging ~harald-jensas/cloud-init:bug/1806014 into cloud-init:master with ~harald-jensas/cloud-init:bug/1847517 as a prerequisite.

Requested reviews:
  cloud-init Commiters (cloud-init-dev)
Related bugs:
  Bug #1696476 in cloud-init: "Identification of e24cloud platform as using Ec2 datasource"
  https://bugs.launchpad.net/cloud-init/+bug/1696476
  Bug #1806014 in cloud-init: "[Centos] IPV6_AUTOCONF=no when using dhcp6"
  https://bugs.launchpad.net/cloud-init/+bug/1806014
  Bug #1847517 in cloud-init: "cloudinit/net/sysconfig.py write incorrect config for dhcp-stateless openstack subnets"
  https://bugs.launchpad.net/cloud-init/+bug/1847517

For more details, see:
https://code.launchpad.net/~harald-jensas/cloud-init/+git/cloud-init/+merge/374138
-- 
Your team cloud-init Commiters is requested to review the proposed merge of ~harald-jensas/cloud-init:bug/1806014 into cloud-init:master.
diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py
index e54a34e..afed089 100644
--- a/cloudinit/net/netplan.py
+++ b/cloudinit/net/netplan.py
@@ -52,6 +52,7 @@ def _extract_addresses(config, entry, ifname):
          'netmask': 64,
          'type': 'static'}],
       'type: physical'
+      'accept-ra': 'true'
     }
 
     An entry dictionary looks like:
@@ -84,6 +85,7 @@ def _extract_addresses(config, entry, ifname):
     nameservers = []
     searchdomains = []
     subnets = config.get('subnets', [])
+    accept_ra = config.get('accept-ra', False)
     if subnets is None:
         subnets = []
     for subnet in subnets:
@@ -144,6 +146,8 @@ def _extract_addresses(config, entry, ifname):
         ns = entry.get('nameservers', {})
         ns.update({'search': searchdomains})
         entry.update({'nameservers': ns})
+    if util.is_true(accept_ra):
+        entry.update({'accept-ra': True})
 
 
 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 c0c415d..4123c27 100644
--- a/cloudinit/net/network_state.py
+++ b/cloudinit/net/network_state.py
@@ -22,7 +22,8 @@ NETWORK_STATE_REQUIRED_KEYS = {
 }
 NETWORK_V2_KEY_FILTER = [
     'addresses', 'dhcp4', 'dhcp6', 'gateway4', 'gateway6', 'interfaces',
-    'match', 'mtu', 'nameservers', 'renderer', 'set-name', 'wakeonlan'
+    'match', 'mtu', 'nameservers', 'renderer', 'set-name', 'wakeonlan',
+    'accept-ra'
 ]
 
 NET_CONFIG_TO_V2 = {
@@ -340,7 +341,8 @@ class NetworkStateInterpreter(object):
             'name': 'eth0',
             'subnets': [
                 {'type': 'dhcp4'}
-             ]
+             ],
+            'accept-ra': 'true'
         }
         '''
 
@@ -370,6 +372,7 @@ class NetworkStateInterpreter(object):
             'address': None,
             'gateway': None,
             'subnets': subnets,
+            'accept-ra': command.get('accept-ra')
         })
         self._network_state['interfaces'].update({command.get('name'): iface})
         self.dump_network_state()
@@ -613,6 +616,7 @@ class NetworkStateInterpreter(object):
               driver: ixgbe
             set-name: lom1
             dhcp6: true
+            accept-ra: true
           switchports:
             match:
               name: enp2*
@@ -641,7 +645,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]
 
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 4e65676..39b94b8 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -333,6 +333,8 @@ class Renderer(renderer.Renderer):
                 if old_key == 'mac_address' and iface['type'] != 'physical':
                     continue
                 iface_cfg[new_key] = old_value
+        if iface.get('accept-ra', False):
+            iface_cfg['IPV6_FORCE_ACCEPT_RA'] = True
 
     @classmethod
     def _render_subnets(cls, iface_cfg, subnets, has_default_route):
@@ -343,11 +345,16 @@ class Renderer(renderer.Renderer):
         for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
             mtu_key = 'MTU'
             subnet_type = subnet.get('type')
-            if subnet_type == 'dhcp6' or subnet_type == 'ipv6_dhcpv6-stateful':
+            if subnet_type == 'dhcp6':
                 # TODO need to set BOOTPROTO to dhcp6 on SUSE
                 iface_cfg['IPV6INIT'] = True
                 # Configure network settings using DHCPv6
                 iface_cfg['DHCPV6C'] = True
+            elif subnet_type == 'ipv6_dhcpv6-stateful':
+                iface_cfg['IPV6INIT'] = True
+                # Configure network settings using DHCPv6
+                iface_cfg['DHCPV6C'] = True
+                iface_cfg['IPV6_FORCE_ACCEPT_RA'] = True
             elif subnet_type == 'ipv6_dhcpv6-stateless':
                 iface_cfg['IPV6INIT'] = True
                 # Configure network settings using SLAAC from RAs
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index f5a9cae..b7adb78 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -1034,26 +1034,74 @@ NETWORK_CONFIGS = {
     },
     'dhcpv6_only': {
         'expected_eni': textwrap.dedent("""\
-            auto lo
-            iface lo inet loopback
+        auto lo
+        iface lo inet loopback
 
-            auto iface0
-            iface iface0 inet6 dhcp
+        auto iface0
+        iface iface0 inet6 dhcp
+    """).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': 'dhcp6'}
+    """).rstrip(' '),
+        'expected_sysconfig': {
+            'ifcfg-iface0': textwrap.dedent("""\
+            BOOTPROTO=none
+            DEVICE=iface0
+            DHCPV6C=yes
+            IPV6INIT=yes
+            DEVICE=iface0
+            NM_CONTROLLED=no
+            ONBOOT=yes
+            STARTMODE=auto
+            TYPE=Ethernet
+            USERCTL=no
+            """),
+        },
+    },
+    'dhcpv6_accept_ra': {
+        'expected_eni': textwrap.dedent("""\
+        auto lo
+        iface lo inet loopback
+
+        auto iface0
+        iface iface0 inet6 dhcp
         """).rstrip(' '),
+        # TODO: Validate expected_netplan
         'expected_netplan': textwrap.dedent("""
             network:
                 version: 2
                 ethernets:
                     iface0:
+                        accept-ra: true
                         dhcp6: true
         """).rstrip(' '),
-        'yaml': textwrap.dedent("""\
+        '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("""\
@@ -1061,13 +1109,14 @@ NETWORK_CONFIGS = {
                 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_stateless': {
@@ -1137,6 +1186,7 @@ NETWORK_CONFIGS = {
             DEVICE=iface0
             DHCPV6C=yes
             IPV6INIT=yes
+            IPV6_FORCE_ACCEPT_RA=yes
             DEVICE=iface0
             NM_CONTROLLED=no
             ONBOOT=yes
@@ -2857,6 +2907,20 @@ USERCTL=no
         self._compare_files_to_expected(entry[self.expected_name], found)
         self._assert_headers(found)
 
+    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_stateless_config(self):
         entry = NETWORK_CONFIGS['dhcpv6_stateless']
         found = self._render_and_read(network_config=yaml.load(entry['yaml']))
@@ -3918,6 +3982,14 @@ 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_all(self):
         entry = NETWORK_CONFIGS['all']
         files = self._render_and_read(network_config=yaml.load(entry['yaml']))