ea00c4
From 5ded09d5acf4d653fe2cbd54814f53063d265489 Mon Sep 17 00:00:00 2001
ea00c4
From: Eduardo Otubo <otubo@redhat.com>
ea00c4
Date: Thu, 29 Oct 2020 15:05:42 +0100
ea00c4
Subject: [PATCH 1/3] Explicit set IPV6_AUTOCONF and IPV6_FORCE_ACCEPT_RA on
ea00c4
 static6 (#634)
ea00c4
ea00c4
RH-Author: Eduardo Terrell Ferrari Otubo (eterrell)
ea00c4
RH-MergeRequest: 13: [RHEL-8.4.0] Add support for ipv6_autoconf on cloud-init-20.3
ea00c4
RH-Commit: [1/1] 41e61c35893f4487981a1ad31f9f97a9a740b397 (eterrell/cloud-init)
ea00c4
RH-Bugzilla: 1889635
ea00c4
ea00c4
commit b46e4a8cff667c8441622089cf7d57aeb88220cd
ea00c4
Author: Eduardo Otubo <otubo@redhat.com>
ea00c4
Date:   Thu Oct 29 15:05:42 2020 +0100
ea00c4
ea00c4
    Explicit set IPV6_AUTOCONF and IPV6_FORCE_ACCEPT_RA on static6 (#634)
ea00c4
ea00c4
    The static and static6 subnet types for network_data.json were
ea00c4
    being ignored by the Openstack handler, this would cause the code to
ea00c4
    break and not function properly.
ea00c4
ea00c4
    As of today, if a static6 configuration is chosen, the interface will
ea00c4
    still eventually be available to receive router advertisements or be set
ea00c4
    from NetworkManager to wait for them and cycle the interface in negative
ea00c4
    case.
ea00c4
ea00c4
    It is safe to assume that if the interface is manually configured to use
ea00c4
    static ipv6 address, there's no need to wait for router advertisements.
ea00c4
    This patch will set automatically IPV6_AUTOCONF and IPV6_FORCE_ACCEPT_RA
ea00c4
    both to "no" in this case.
ea00c4
ea00c4
    This patch fixes the specific behavior only for RHEL flavor and
ea00c4
    sysconfig renderer. It also introduces new unit tests for the specific
ea00c4
    case as well as adjusts some existent tests to be compatible with the
ea00c4
    new options. This patch also addresses this problem by assigning the
ea00c4
    appropriate subnet type for each case on the openstack handler.
ea00c4
ea00c4
    rhbz: #1889635
ea00c4
    rhbz: #1889635
ea00c4
ea00c4
    Signed-off-by: Eduardo Otubo otubo@redhat.com
ea00c4
ea00c4
Signed-off-by: Eduardo Otubo otubo@redhat.com
ea00c4
---
ea00c4
 cloudinit/net/network_state.py                 |   3 +-
ea00c4
 cloudinit/net/sysconfig.py                     |   4 +
ea00c4
 cloudinit/sources/helpers/openstack.py         |   8 +-
ea00c4
 tests/unittests/test_distros/test_netconfig.py |   2 +
ea00c4
 tests/unittests/test_net.py                    | 100 +++++++++++++++++++++++++
ea00c4
 5 files changed, 115 insertions(+), 2 deletions(-)
ea00c4
ea00c4
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
ea00c4
index b2f7d31..d9e7fd5 100644
ea00c4
--- a/cloudinit/net/network_state.py
ea00c4
+++ b/cloudinit/net/network_state.py
ea00c4
@@ -820,7 +820,8 @@ def _normalize_subnet(subnet):
ea00c4
 
ea00c4
     if subnet.get('type') in ('static', 'static6'):
ea00c4
         normal_subnet.update(
ea00c4
-            _normalize_net_keys(normal_subnet, address_keys=('address',)))
ea00c4
+            _normalize_net_keys(normal_subnet, address_keys=(
ea00c4
+                'address', 'ip_address',)))
ea00c4
     normal_subnet['routes'] = [_normalize_route(r)
ea00c4
                                for r in subnet.get('routes', [])]
ea00c4
 
ea00c4
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
ea00c4
index af093dd..c078898 100644
ea00c4
--- a/cloudinit/net/sysconfig.py
ea00c4
+++ b/cloudinit/net/sysconfig.py
ea00c4
@@ -451,6 +451,10 @@ class Renderer(renderer.Renderer):
ea00c4
                             iface_cfg[mtu_key] = subnet['mtu']
ea00c4
                     else:
ea00c4
                         iface_cfg[mtu_key] = subnet['mtu']
ea00c4
+
ea00c4
+                if subnet_is_ipv6(subnet) and flavor == 'rhel':
ea00c4
+                    iface_cfg['IPV6_FORCE_ACCEPT_RA'] = False
ea00c4
+                    iface_cfg['IPV6_AUTOCONF'] = False
ea00c4
             elif subnet_type == 'manual':
ea00c4
                 if flavor == 'suse':
ea00c4
                     LOG.debug('Unknown subnet type setting "%s"', subnet_type)
ea00c4
diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
ea00c4
index 65e020c..3e6365f 100644
ea00c4
--- a/cloudinit/sources/helpers/openstack.py
ea00c4
+++ b/cloudinit/sources/helpers/openstack.py
ea00c4
@@ -602,11 +602,17 @@ def convert_net_json(network_json=None, known_macs=None):
ea00c4
             elif network['type'] in ['ipv6_slaac', 'ipv6_dhcpv6-stateless',
ea00c4
                                      'ipv6_dhcpv6-stateful']:
ea00c4
                 subnet.update({'type': network['type']})
ea00c4
-            elif network['type'] in ['ipv4', 'ipv6']:
ea00c4
+            elif network['type'] in ['ipv4', 'static']:
ea00c4
                 subnet.update({
ea00c4
                     'type': 'static',
ea00c4
                     'address': network.get('ip_address'),
ea00c4
                 })
ea00c4
+            elif network['type'] in ['ipv6', 'static6']:
ea00c4
+                cfg.update({'accept-ra': False})
ea00c4
+                subnet.update({
ea00c4
+                    'type': 'static6',
ea00c4
+                    'address': network.get('ip_address'),
ea00c4
+                })
ea00c4
 
ea00c4
             # Enable accept_ra for stateful and legacy ipv6_dhcp types
ea00c4
             if network['type'] in ['ipv6_dhcpv6-stateful', 'ipv6_dhcp']:
ea00c4
diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
ea00c4
index 8d7b09c..f9fc3a1 100644
ea00c4
--- a/tests/unittests/test_distros/test_netconfig.py
ea00c4
+++ b/tests/unittests/test_distros/test_netconfig.py
ea00c4
@@ -514,7 +514,9 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
ea00c4
                 DEVICE=eth0
ea00c4
                 IPV6ADDR=2607:f0d0:1002:0011::2/64
ea00c4
                 IPV6INIT=yes
ea00c4
+                IPV6_AUTOCONF=no
ea00c4
                 IPV6_DEFAULTGW=2607:f0d0:1002:0011::1
ea00c4
+                IPV6_FORCE_ACCEPT_RA=no
ea00c4
                 NM_CONTROLLED=no
ea00c4
                 ONBOOT=yes
ea00c4
                 TYPE=Ethernet
ea00c4
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
ea00c4
index 9985a97..d7a7a65 100644
ea00c4
--- a/tests/unittests/test_net.py
ea00c4
+++ b/tests/unittests/test_net.py
ea00c4
@@ -750,7 +750,9 @@ IPADDR=172.19.1.34
ea00c4
 IPV6ADDR=2001:DB8::10/64
ea00c4
 IPV6ADDR_SECONDARIES="2001:DB9::10/64 2001:DB10::10/64"
ea00c4
 IPV6INIT=yes
ea00c4
+IPV6_AUTOCONF=no
ea00c4
 IPV6_DEFAULTGW=2001:DB8::1
ea00c4
+IPV6_FORCE_ACCEPT_RA=no
ea00c4
 NETMASK=255.255.252.0
ea00c4
 ONBOOT=yes
ea00c4
 TYPE=Ethernet
ea00c4
@@ -1022,6 +1024,8 @@ NETWORK_CONFIGS = {
ea00c4
                 IPADDR=192.168.14.2
ea00c4
                 IPV6ADDR=2001:1::1/64
ea00c4
                 IPV6INIT=yes
ea00c4
+                IPV6_AUTOCONF=no
ea00c4
+                IPV6_FORCE_ACCEPT_RA=no
ea00c4
                 NETMASK=255.255.255.0
ea00c4
                 ONBOOT=yes
ea00c4
                 TYPE=Ethernet
ea00c4
@@ -1247,6 +1251,33 @@ NETWORK_CONFIGS = {
ea00c4
             """),
ea00c4
         },
ea00c4
     },
ea00c4
+    'static6': {
ea00c4
+        'yaml': textwrap.dedent("""\
ea00c4
+        version: 1
ea00c4
+        config:
ea00c4
+          - type: 'physical'
ea00c4
+            name: 'iface0'
ea00c4
+            accept-ra: 'no'
ea00c4
+            subnets:
ea00c4
+            - type: 'static6'
ea00c4
+              address: 2001:1::1/64
ea00c4
+    """).rstrip(' '),
ea00c4
+        'expected_sysconfig_rhel': {
ea00c4
+            'ifcfg-iface0': textwrap.dedent("""\
ea00c4
+            BOOTPROTO=none
ea00c4
+            DEVICE=iface0
ea00c4
+            IPV6ADDR=2001:1::1/64
ea00c4
+            IPV6INIT=yes
ea00c4
+            IPV6_AUTOCONF=no
ea00c4
+            IPV6_FORCE_ACCEPT_RA=no
ea00c4
+            DEVICE=iface0
ea00c4
+            NM_CONTROLLED=no
ea00c4
+            ONBOOT=yes
ea00c4
+            TYPE=Ethernet
ea00c4
+            USERCTL=no
ea00c4
+            """),
ea00c4
+        },
ea00c4
+    },
ea00c4
     'dhcpv6_stateless': {
ea00c4
         'expected_eni': textwrap.dedent("""\
ea00c4
         auto lo
ea00c4
@@ -1636,6 +1667,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
ea00c4
                 IPADDR=192.168.14.2
ea00c4
                 IPV6ADDR=2001:1::1/64
ea00c4
                 IPV6INIT=yes
ea00c4
+                IPV6_AUTOCONF=no
ea00c4
+                IPV6_FORCE_ACCEPT_RA=no
ea00c4
                 IPV6_DEFAULTGW=2001:4800:78ff:1b::1
ea00c4
                 MACADDR=bb:bb:bb:bb:bb:aa
ea00c4
                 NETMASK=255.255.255.0
ea00c4
@@ -2158,6 +2191,8 @@ iface bond0 inet6 static
ea00c4
         IPADDR1=192.168.1.2
ea00c4
         IPV6ADDR=2001:1::1/92
ea00c4
         IPV6INIT=yes
ea00c4
+        IPV6_AUTOCONF=no
ea00c4
+        IPV6_FORCE_ACCEPT_RA=no
ea00c4
         MTU=9000
ea00c4
         NETMASK=255.255.255.0
ea00c4
         NETMASK1=255.255.255.0
ea00c4
@@ -2259,6 +2294,8 @@ iface bond0 inet6 static
ea00c4
                 IPADDR1=192.168.1.2
ea00c4
                 IPV6ADDR=2001:1::bbbb/96
ea00c4
                 IPV6INIT=yes
ea00c4
+                IPV6_AUTOCONF=no
ea00c4
+                IPV6_FORCE_ACCEPT_RA=no
ea00c4
                 IPV6_DEFAULTGW=2001:1::1
ea00c4
                 MTU=2222
ea00c4
                 NETMASK=255.255.255.0
ea00c4
@@ -2341,6 +2378,9 @@ iface bond0 inet6 static
ea00c4
                 HWADDR=52:54:00:12:34:00
ea00c4
                 IPV6ADDR=2001:1::100/96
ea00c4
                 IPV6INIT=yes
ea00c4
+                IPV6_AUTOCONF=no
ea00c4
+                IPV6_FORCE_ACCEPT_RA=no
ea00c4
+                NM_CONTROLLED=no
ea00c4
                 ONBOOT=yes
ea00c4
                 TYPE=Ethernet
ea00c4
                 USERCTL=no
ea00c4
@@ -2352,6 +2392,9 @@ iface bond0 inet6 static
ea00c4
                 HWADDR=52:54:00:12:34:01
ea00c4
                 IPV6ADDR=2001:1::101/96
ea00c4
                 IPV6INIT=yes
ea00c4
+                IPV6_AUTOCONF=no
ea00c4
+                IPV6_FORCE_ACCEPT_RA=no
ea00c4
+                NM_CONTROLLED=no
ea00c4
                 ONBOOT=yes
ea00c4
                 TYPE=Ethernet
ea00c4
                 USERCTL=no
ea00c4
@@ -3151,6 +3194,61 @@ USERCTL=no
ea00c4
         self._compare_files_to_expected(entry[self.expected_name], found)
ea00c4
         self._assert_headers(found)
ea00c4
 
ea00c4
+    def test_stattic6_from_json(self):
ea00c4
+        net_json = {
ea00c4
+            "services": [{"type": "dns", "address": "172.19.0.12"}],
ea00c4
+            "networks": [{
ea00c4
+                "network_id": "dacd568d-5be6-4786-91fe-750c374b78b4",
ea00c4
+                "type": "ipv4", "netmask": "255.255.252.0",
ea00c4
+                "link": "tap1a81968a-79",
ea00c4
+                "routes": [{
ea00c4
+                    "netmask": "0.0.0.0",
ea00c4
+                    "network": "0.0.0.0",
ea00c4
+                    "gateway": "172.19.3.254",
ea00c4
+                }, {
ea00c4
+                    "netmask": "0.0.0.0",  # A second default gateway
ea00c4
+                    "network": "0.0.0.0",
ea00c4
+                    "gateway": "172.20.3.254",
ea00c4
+                }],
ea00c4
+                "ip_address": "172.19.1.34", "id": "network0"
ea00c4
+            }, {
ea00c4
+                "network_id": "mgmt",
ea00c4
+                "netmask": "ffff:ffff:ffff:ffff::",
ea00c4
+                "link": "interface1",
ea00c4
+                "mode": "link-local",
ea00c4
+                "routes": [],
ea00c4
+                "ip_address": "fe80::c096:67ff:fe5c:6e84",
ea00c4
+                "type": "static6",
ea00c4
+                "id": "network1",
ea00c4
+                "services": [],
ea00c4
+                "accept-ra": "false"
ea00c4
+            }],
ea00c4
+            "links": [
ea00c4
+                {
ea00c4
+                    "ethernet_mac_address": "fa:16:3e:ed:9a:59",
ea00c4
+                    "mtu": None, "type": "bridge", "id":
ea00c4
+                    "tap1a81968a-79",
ea00c4
+                    "vif_id": "1a81968a-797a-400f-8a80-567f997eb93f"
ea00c4
+                },
ea00c4
+            ],
ea00c4
+        }
ea00c4
+        macs = {'fa:16:3e:ed:9a:59': 'eth0'}
ea00c4
+        render_dir = self.tmp_dir()
ea00c4
+        network_cfg = openstack.convert_net_json(net_json, known_macs=macs)
ea00c4
+        ns = network_state.parse_net_config_data(network_cfg,
ea00c4
+                                                 skip_broken=False)
ea00c4
+        renderer = self._get_renderer()
ea00c4
+        with self.assertRaises(ValueError):
ea00c4
+            renderer.render_network_state(ns, target=render_dir)
ea00c4
+        self.assertEqual([], os.listdir(render_dir))
ea00c4
+
ea00c4
+    def test_static6_from_yaml(self):
ea00c4
+        entry = NETWORK_CONFIGS['static6']
ea00c4
+        found = self._render_and_read(network_config=yaml.load(
ea00c4
+            entry['yaml']))
ea00c4
+        self._compare_files_to_expected(entry[self.expected_name], found)
ea00c4
+        self._assert_headers(found)
ea00c4
+
ea00c4
     def test_dhcpv6_reject_ra_config_v2(self):
ea00c4
         entry = NETWORK_CONFIGS['dhcpv6_reject_ra']
ea00c4
         found = self._render_and_read(network_config=yaml.load(
ea00c4
@@ -3268,6 +3366,8 @@ USERCTL=no
ea00c4
                    IPADDR=192.168.42.100
ea00c4
                    IPV6ADDR=2001:db8::100/32
ea00c4
                    IPV6INIT=yes
ea00c4
+                   IPV6_AUTOCONF=no
ea00c4
+                   IPV6_FORCE_ACCEPT_RA=no
ea00c4
                    IPV6_DEFAULTGW=2001:db8::1
ea00c4
                    NETMASK=255.255.255.0
ea00c4
                    NM_CONTROLLED=no
ea00c4
-- 
ea00c4
1.8.3.1
ea00c4