69219a
From 28e77dc7d641233b5793a159befc225fd3a8726b Mon Sep 17 00:00:00 2001
69219a
From: Eduardo Otubo <otubo@redhat.com>
69219a
Date: Mon, 4 May 2020 12:40:08 +0200
69219a
Subject: [PATCH 5/6] net: handle openstack dhcpv6-stateless configuration
69219a
MIME-Version: 1.0
69219a
Content-Type: text/plain; charset=UTF-8
69219a
Content-Transfer-Encoding: 8bit
69219a
69219a
RH-Author: Eduardo Otubo <otubo@redhat.com>
69219a
Message-id: <20200327152826.13343-6-otubo@redhat.com>
69219a
Patchwork-id: 94461
69219a
O-Subject: [RHEL-8.1.z/RHEL-8.2.z cloud-init PATCHv2 5/6] net: handle openstack dhcpv6-stateless configuration
69219a
Bugzilla: 1811753
69219a
RH-Acked-by: Cathy Avery <cavery@redhat.com>
69219a
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
69219a
69219a
commit fac98983187c0984aa79c569c4b76cab90fd6f47
69219a
Author: Harald Jensås <hjensas@redhat.com>
69219a
Date:   Wed Oct 16 15:30:28 2019 +0000
69219a
69219a
    net: handle openstack dhcpv6-stateless configuration
69219a
69219a
    Openstack subnets can be configured to use SLAAC by setting
69219a
    ipv6_address_mode=dhcpv6-stateless. When this is the case
69219a
    the sysconfig interface configuration should use
69219a
    IPV6_AUTOCONF=yes and not set DHCPV6C=yes.
69219a
69219a
    This change sets the subnets type property to the full
69219a
    network['type'] from openstack metadata.
69219a
69219a
    cloudinit/net/sysconfig.py and cloudinit/net/eni.py
69219a
    are updated to support new subnet types:
69219a
      - 'ipv6_dhcpv6-stateless' => IPV6_AUTOCONF=yes
69219a
      - 'ipv6_dhcpv6-stateful' => DHCPV6C=yes
69219a
69219a
    Type 'dhcp6' in sysconfig is kept for backward compatibility
69219a
    with any implementations that set subnet_type == 'dhcp6'.
69219a
69219a
    LP: #1847517
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                               |  7 +-
69219a
 cloudinit/net/sysconfig.py                         |  7 +-
69219a
 cloudinit/sources/helpers/openstack.py             |  3 +-
69219a
 .../unittests/test_datasource/test_configdrive.py  | 39 ++++++++++
69219a
 tests/unittests/test_net.py                        | 87 +++++++++++++++++++++-
69219a
 5 files changed, 139 insertions(+), 4 deletions(-)
69219a
69219a
diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
69219a
index 6423632..896a39b 100644
69219a
--- a/cloudinit/net/eni.py
69219a
+++ b/cloudinit/net/eni.py
69219a
@@ -405,8 +405,13 @@ class Renderer(renderer.Renderer):
69219a
                 else:
69219a
                     ipv4_subnet_mtu = subnet.get('mtu')
69219a
                 iface['inet'] = subnet_inet
69219a
-                if subnet['type'].startswith('dhcp'):
69219a
+                if (subnet['type'] == 'dhcp4' or subnet['type'] == 'dhcp6' or
69219a
+                        subnet['type'] == 'ipv6_dhcpv6-stateful'):
69219a
+                    # Configure network settings using DHCP or DHCPv6
69219a
                     iface['mode'] = 'dhcp'
69219a
+                elif subnet['type'] == 'ipv6_dhcpv6-stateless':
69219a
+                    # Configure network settings using SLAAC from RAs
69219a
+                    iface['mode'] = 'auto'
69219a
 
69219a
                 # do not emit multiple 'auto $IFACE' lines as older (precise)
69219a
                 # ifupdown complains
69219a
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
69219a
index a4c7660..8b11dbb 100644
69219a
--- a/cloudinit/net/sysconfig.py
69219a
+++ b/cloudinit/net/sysconfig.py
69219a
@@ -328,10 +328,15 @@ class Renderer(renderer.Renderer):
69219a
         for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
69219a
             mtu_key = 'MTU'
69219a
             subnet_type = subnet.get('type')
69219a
-            if subnet_type == 'dhcp6':
69219a
+            if subnet_type == 'dhcp6' or subnet_type == 'ipv6_dhcpv6-stateful':
69219a
                 # TODO need to set BOOTPROTO to dhcp6 on SUSE
69219a
                 iface_cfg['IPV6INIT'] = True
69219a
+                # Configure network settings using DHCPv6
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
69219a
+                iface_cfg['IPV6_AUTOCONF'] = True
69219a
             elif subnet_type in ['dhcp4', 'dhcp']:
69219a
                 iface_cfg['BOOTPROTO'] = 'dhcp'
69219a
             elif subnet_type == 'static':
69219a
diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
69219a
index 9c29cea..9f2fd2d 100644
69219a
--- a/cloudinit/sources/helpers/openstack.py
69219a
+++ b/cloudinit/sources/helpers/openstack.py
69219a
@@ -585,7 +585,8 @@ def convert_net_json(network_json=None, known_macs=None):
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 = 'dhcp6' if network['type'].startswith('ipv6') else 'dhcp4'
69219a
+                t = (network['type'] if network['type'].startswith('ipv6')
69219a
+                     else 'dhcp4')
69219a
                 subnet.update({
69219a
                     'type': t,
69219a
                 })
69219a
diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py
69219a
index dcdabea..ed4e9d5 100644
69219a
--- a/tests/unittests/test_datasource/test_configdrive.py
69219a
+++ b/tests/unittests/test_datasource/test_configdrive.py
69219a
@@ -503,6 +503,45 @@ class TestNetJson(CiTestCase):
69219a
                                                     known_macs=KNOWN_MACS)
69219a
         self.assertEqual(myds.network_config, network_config)
69219a
 
69219a
+    def test_network_config_conversion_dhcp6(self):
69219a
+        """Test some ipv6 input network json and check the expected
69219a
+           conversions."""
69219a
+        in_data = {
69219a
+            'links': [
69219a
+                {'vif_id': '2ecc7709-b3f7-4448-9580-e1ec32d75bbd',
69219a
+                 'ethernet_mac_address': 'fa:16:3e:69:b0:58',
69219a
+                 'type': 'ovs', 'mtu': None, 'id': 'tap2ecc7709-b3'},
69219a
+                {'vif_id': '2f88d109-5b57-40e6-af32-2472df09dc33',
69219a
+                 'ethernet_mac_address': 'fa:16:3e:d4:57:ad',
69219a
+                 'type': 'ovs', 'mtu': None, 'id': 'tap2f88d109-5b'},
69219a
+            ],
69219a
+            'networks': [
69219a
+                {'link': 'tap2ecc7709-b3', 'type': 'ipv6_dhcpv6-stateless',
69219a
+                 'network_id': '6d6357ac-0f70-4afa-8bd7-c274cc4ea235',
69219a
+                 'id': 'network0'},
69219a
+                {'link': 'tap2f88d109-5b', 'type': 'ipv6_dhcpv6-stateful',
69219a
+                 'network_id': 'd227a9b3-6960-4d94-8976-ee5788b44f54',
69219a
+                 'id': 'network1'},
69219a
+            ]
69219a
+        }
69219a
+        out_data = {
69219a
+            'version': 1,
69219a
+            'config': [
69219a
+                {'mac_address': 'fa:16:3e:69:b0:58',
69219a
+                 'mtu': None,
69219a
+                 'name': 'enp0s1',
69219a
+                 'subnets': [{'type': 'ipv6_dhcpv6-stateless'}],
69219a
+                 'type': 'physical'},
69219a
+                {'mac_address': 'fa:16:3e:d4:57:ad',
69219a
+                 'mtu': None,
69219a
+                 'name': 'enp0s2',
69219a
+                 'subnets': [{'type': 'ipv6_dhcpv6-stateful'}],
69219a
+                 'type': 'physical'}
69219a
+            ],
69219a
+        }
69219a
+        conv_data = openstack.convert_net_json(in_data, known_macs=KNOWN_MACS)
69219a
+        self.assertEqual(out_data, conv_data)
69219a
+
69219a
     def test_network_config_conversions(self):
69219a
         """Tests a bunch of input network json and checks the
69219a
            expected conversions."""
69219a
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
69219a
index df5658d..70d13f3 100644
69219a
--- a/tests/unittests/test_net.py
69219a
+++ b/tests/unittests/test_net.py
69219a
@@ -712,6 +712,82 @@ NETWORK_CONFIGS = {
69219a
                 """),
69219a
         },
69219a
     },
69219a
+    'dhcpv6_stateless': {
69219a
+        'expected_eni': textwrap.dedent("""\
69219a
+        auto lo
69219a
+        iface lo inet loopback
69219a
+
69219a
+        auto iface0
69219a
+        iface iface0 inet6 auto
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_dhcpv6-stateless'}
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_stateful': {
69219a
+        'expected_eni': textwrap.dedent("""\
69219a
+        auto lo
69219a
+        iface lo inet loopback
69219a
+
69219a
+        auto iface0
69219a
+        iface iface0 inet6 dhcp
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_dhcpv6-stateful'}
69219a
+    """).rstrip(' '),
69219a
+        'expected_sysconfig': {
69219a
+            'ifcfg-iface0': textwrap.dedent("""\
69219a
+            BOOTPROTO=none
69219a
+            DEVICE=iface0
69219a
+            DHCPV6C=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
     'all': {
69219a
         'expected_eni': ("""\
69219a
 auto lo
69219a
@@ -2300,8 +2376,11 @@ USERCTL=no
69219a
             }
69219a
         }
69219a
 
69219a
+
69219a
+    def test_dhcpv6_stateless_config(self):
69219a
+        entry = NETWORK_CONFIGS['dhcpv6_stateless']
69219a
         found = self._render_and_read(network_config=yaml.load(entry['yaml']))
69219a
-        self._compare_files_to_expected(entry['expected_sysconfig'], found)
69219a
+        self._compare_files_to_expected(entry[self.expected_name], found)
69219a
         self._assert_headers(found)
69219a
 
69219a
     def test_from_v2_route_metric(self):
69219a
@@ -2334,6 +2413,12 @@ USERCTL=no
69219a
             self._compare_files_to_expected(
69219a
                 expected, self._render_and_read(network_config=v2data))
69219a
 
69219a
+    def test_dhcpv6_stateful_config(self):
69219a
+        entry = NETWORK_CONFIGS['dhcpv6_stateful']
69219a
+        found = self._render_and_read(network_config=yaml.load(entry['yaml']))
69219a
+        self._compare_files_to_expected(entry[self.expected_name], found)
69219a
+        self._assert_headers(found)
69219a
+
69219a
 
69219a
 class TestOpenSuseSysConfigRendering(CiTestCase):
69219a
 
69219a
-- 
69219a
1.8.3.1
69219a